[{"data":1,"prerenderedAt":2704},["ShallowReactive",2],{"docs:/docs/agents/patterns":3},{"id":4,"title":5,"body":6,"description":2697,"extension":2698,"meta":2699,"navigation":210,"path":2700,"seo":2701,"stem":2702,"__hash__":2703},"docs/docs/agents/patterns.md","Agent Coordination Patterns",{"type":7,"value":8,"toc":2663},"minimark",[9,14,27,137,151,154,380,385,388,393,406,410,701,711,715,903,927,931,962,965,972,975,986,990,1173,1191,1194,1218,1222,1225,1228,1239,1242,1633,1638,1641,1672,1676,1679,1682,1696,1699,1943,1961,1964,1995,1999,2010,2013,2024,2027,2266,2284,2287,2308,2311,2314,2317,2328,2331,2560,2581,2584,2614,2618,2633,2651,2659],[10,11,13],"h1",{"id":12},"coordination-patterns","Coordination Patterns",[15,16,17,18,22,23,26],"p",{},"The ",[19,20,21],"code",{},"@nolag/agents"," SDK provides six coordination patterns. Each is a standalone class you construct on an ",[19,24,25],{},"AgentRoom",", and each maps to one or two of the room's seven topics under the hood.",[28,29,30,46],"table",{},[31,32,33],"thead",{},[34,35,36,40,43],"tr",{},[37,38,39],"th",{},"Topic",[37,41,42],{},"Carries",[37,44,45],{},"Notes",[47,48,49,63,76,89,101,113,125],"tbody",{},[34,50,51,57,60],{},[52,53,54],"td",{},[19,55,56],{},"tasks",[52,58,59],{},"Task envelopes (Handoff)",[52,61,62],{},"Load-balanced when the client enables it",[34,64,65,70,73],{},[52,66,67],{},[19,68,69],{},"results",[52,71,72],{},"Task results and tool responses",[52,74,75],{},"Directed to the requesting agent by filter",[34,77,78,83,86],{},[52,79,80],{},[19,81,82],{},"state",[52,84,85],{},"Blackboard writes",[52,87,88],{},"Published retained",[34,90,91,96,99],{},[52,92,93],{},[19,94,95],{},"events",[52,97,98],{},"Observe events",[52,100],{},[34,102,103,108,111],{},[52,104,105],{},[19,106,107],{},"inbox",[52,109,110],{},"Direct agent-to-agent messages",[52,112],{},[34,114,115,120,123],{},[52,116,117],{},[19,118,119],{},"tools",[52,121,122],{},"Tool requests",[52,124,62],{},[34,126,127,132,135],{},[52,128,129],{},[19,130,131],{},"approval",[52,133,134],{},"Approval requests and decisions",[52,136,88],{},[15,138,17,139,142,143,146,147,150],{},[19,140,141],{},"nolag-agents-sdk"," blueprint seeds the ",[19,144,145],{},"default-workflow"," room with all seven topics. Rooms never exist implicitly, so create any other room in the portal or with ",[19,148,149],{},"api.rooms.create()"," before an agent joins it.",[15,152,153],{},"Every example below assumes this setup:",[155,156,157],"code-tabs",{},[158,159,165],"pre",{"className":160,"code":161,"filename":162,"language":163,"meta":164,"style":164},"language-typescript shiki shiki-themes github-light github-dark","import { NoLag } from \"@nolag/js-sdk\";\nimport { NoLagAgents } from \"@nolag/agents\";\n\nconst client = NoLag(ACTOR_TOKEN);\nconst agents = new NoLagAgents({\n  client,\n  appName: APP_SLUG, // the slug returned when you created the app\n  agentId: \"worker-1\",\n  presence: { name: \"worker-1\", role: \"agent\", capabilities: [\"research\", \"finance\"] },\n});\nawait client.connect();\nawait agents.ready();\n\nconst room = agents.room(\"default-workflow\");\n","Setup","typescript","",[19,166,167,190,205,212,238,257,263,279,291,319,325,340,353,358],{"__ignoreMap":164},[168,169,172,176,180,183,187],"span",{"class":170,"line":171},"line",1,[168,173,175],{"class":174},"szBVR","import",[168,177,179],{"class":178},"sVt8B"," { NoLag } ",[168,181,182],{"class":174},"from",[168,184,186],{"class":185},"sZZnC"," \"@nolag/js-sdk\"",[168,188,189],{"class":178},";\n",[168,191,193,195,198,200,203],{"class":170,"line":192},2,[168,194,175],{"class":174},[168,196,197],{"class":178}," { NoLagAgents } ",[168,199,182],{"class":174},[168,201,202],{"class":185}," \"@nolag/agents\"",[168,204,189],{"class":178},[168,206,208],{"class":170,"line":207},3,[168,209,211],{"emptyLinePlaceholder":210},true,"\n",[168,213,215,218,222,225,229,232,235],{"class":170,"line":214},4,[168,216,217],{"class":174},"const",[168,219,221],{"class":220},"sj4cs"," client",[168,223,224],{"class":174}," =",[168,226,228],{"class":227},"sScJk"," NoLag",[168,230,231],{"class":178},"(",[168,233,234],{"class":220},"ACTOR_TOKEN",[168,236,237],{"class":178},");\n",[168,239,241,243,246,248,251,254],{"class":170,"line":240},5,[168,242,217],{"class":174},[168,244,245],{"class":220}," agents",[168,247,224],{"class":174},[168,249,250],{"class":174}," new",[168,252,253],{"class":227}," NoLagAgents",[168,255,256],{"class":178},"({\n",[168,258,260],{"class":170,"line":259},6,[168,261,262],{"class":178},"  client,\n",[168,264,266,269,272,275],{"class":170,"line":265},7,[168,267,268],{"class":178},"  appName: ",[168,270,271],{"class":220},"APP_SLUG",[168,273,274],{"class":178},", ",[168,276,278],{"class":277},"sJ8bj","// the slug returned when you created the app\n",[168,280,282,285,288],{"class":170,"line":281},8,[168,283,284],{"class":178},"  agentId: ",[168,286,287],{"class":185},"\"worker-1\"",[168,289,290],{"class":178},",\n",[168,292,294,297,299,302,305,308,311,313,316],{"class":170,"line":293},9,[168,295,296],{"class":178},"  presence: { name: ",[168,298,287],{"class":185},[168,300,301],{"class":178},", role: ",[168,303,304],{"class":185},"\"agent\"",[168,306,307],{"class":178},", capabilities: [",[168,309,310],{"class":185},"\"research\"",[168,312,274],{"class":178},[168,314,315],{"class":185},"\"finance\"",[168,317,318],{"class":178},"] },\n",[168,320,322],{"class":170,"line":321},10,[168,323,324],{"class":178},"});\n",[168,326,328,331,334,337],{"class":170,"line":327},11,[168,329,330],{"class":174},"await",[168,332,333],{"class":178}," client.",[168,335,336],{"class":227},"connect",[168,338,339],{"class":178},"();\n",[168,341,343,345,348,351],{"class":170,"line":342},12,[168,344,330],{"class":174},[168,346,347],{"class":178}," agents.",[168,349,350],{"class":227},"ready",[168,352,339],{"class":178},[168,354,356],{"class":170,"line":355},13,[168,357,211],{"emptyLinePlaceholder":210},[168,359,361,363,366,368,370,373,375,378],{"class":170,"line":360},14,[168,362,217],{"class":174},[168,364,365],{"class":220}," room",[168,367,224],{"class":174},[168,369,347],{"class":178},[168,371,372],{"class":227},"room",[168,374,231],{"class":178},[168,376,377],{"class":185},"\"default-workflow\"",[168,379,237],{"class":178},[381,382,384],"h2",{"id":383},"handoff","Handoff",[15,386,387],{},"The Handoff pattern implements task dispatch and result collection. An orchestrator dispatches a task for one capability. Workers advertise their capabilities through presence and handle the tasks whose capability they registered a handler for. Each result is published back to the dispatcher that asked for it.",[389,390,392],"h3",{"id":391},"when-to-use","When to use",[394,395,396,400,403],"ul",{},[397,398,399],"li",{},"Distributing work across a pool of specialized agents",[397,401,402],{},"Sharing a stream of tasks across identical workers (with load balancing on the client)",[397,404,405],{},"Fan-out/fan-in workflows where multiple agents process subtasks",[389,407,409],{"id":408},"dispatching-tasks","Dispatching tasks",[155,411,412],{},[158,413,416],{"className":160,"code":414,"filename":415,"language":163,"meta":164,"style":164},"import { Handoff } from \"@nolag/agents\";\n\nconst handoff = new Handoff(room);\n\n// Dispatch to any connected worker advertising the \"research\" capability.\n// Throws if no such worker is connected (pass allowNoWorkers: true to publish anyway).\nconst result = await handoff.dispatch(\n  \"research\",\n  { url: \"https://example.com/article\" },\n  {\n    priority: \"high\",     // \"low\" | \"medium\" | \"high\" | \"critical\", default \"medium\"\n    timeout: 30000,       // rejects if no result arrives within 30 s\n    waitForResult: true,  // resolve with the worker's ResultEnvelope\n    tags: [\"tenant:acme\"],\n  },\n);\n\nif (result) {\n  if (result.status === \"success\") {\n    console.log(\"Summary:\", result.payload.summary, \"from\", result.completedBy);\n  } else {\n    console.log(\"Worker reported:\", result.status, result.error?.message);\n  }\n}\n\n// Without waitForResult the call returns once the task is published\nawait handoff.dispatch(\"research\", { url: \"https://example.com/other\" });\n","TypeScript",[19,417,418,431,435,452,456,461,466,487,494,505,510,524,538,552,563,569,574,579,588,606,629,641,656,662,668,673,679],{"__ignoreMap":164},[168,419,420,422,425,427,429],{"class":170,"line":171},[168,421,175],{"class":174},[168,423,424],{"class":178}," { Handoff } ",[168,426,182],{"class":174},[168,428,202],{"class":185},[168,430,189],{"class":178},[168,432,433],{"class":170,"line":192},[168,434,211],{"emptyLinePlaceholder":210},[168,436,437,439,442,444,446,449],{"class":170,"line":207},[168,438,217],{"class":174},[168,440,441],{"class":220}," handoff",[168,443,224],{"class":174},[168,445,250],{"class":174},[168,447,448],{"class":227}," Handoff",[168,450,451],{"class":178},"(room);\n",[168,453,454],{"class":170,"line":214},[168,455,211],{"emptyLinePlaceholder":210},[168,457,458],{"class":170,"line":240},[168,459,460],{"class":277},"// Dispatch to any connected worker advertising the \"research\" capability.\n",[168,462,463],{"class":170,"line":259},[168,464,465],{"class":277},"// Throws if no such worker is connected (pass allowNoWorkers: true to publish anyway).\n",[168,467,468,470,473,475,478,481,484],{"class":170,"line":265},[168,469,217],{"class":174},[168,471,472],{"class":220}," result",[168,474,224],{"class":174},[168,476,477],{"class":174}," await",[168,479,480],{"class":178}," handoff.",[168,482,483],{"class":227},"dispatch",[168,485,486],{"class":178},"(\n",[168,488,489,492],{"class":170,"line":281},[168,490,491],{"class":185},"  \"research\"",[168,493,290],{"class":178},[168,495,496,499,502],{"class":170,"line":293},[168,497,498],{"class":178},"  { url: ",[168,500,501],{"class":185},"\"https://example.com/article\"",[168,503,504],{"class":178}," },\n",[168,506,507],{"class":170,"line":321},[168,508,509],{"class":178},"  {\n",[168,511,512,515,518,521],{"class":170,"line":327},[168,513,514],{"class":178},"    priority: ",[168,516,517],{"class":185},"\"high\"",[168,519,520],{"class":178},",     ",[168,522,523],{"class":277},"// \"low\" | \"medium\" | \"high\" | \"critical\", default \"medium\"\n",[168,525,526,529,532,535],{"class":170,"line":342},[168,527,528],{"class":178},"    timeout: ",[168,530,531],{"class":220},"30000",[168,533,534],{"class":178},",       ",[168,536,537],{"class":277},"// rejects if no result arrives within 30 s\n",[168,539,540,543,546,549],{"class":170,"line":355},[168,541,542],{"class":178},"    waitForResult: ",[168,544,545],{"class":220},"true",[168,547,548],{"class":178},",  ",[168,550,551],{"class":277},"// resolve with the worker's ResultEnvelope\n",[168,553,554,557,560],{"class":170,"line":360},[168,555,556],{"class":178},"    tags: [",[168,558,559],{"class":185},"\"tenant:acme\"",[168,561,562],{"class":178},"],\n",[168,564,566],{"class":170,"line":565},15,[168,567,568],{"class":178},"  },\n",[168,570,572],{"class":170,"line":571},16,[168,573,237],{"class":178},[168,575,577],{"class":170,"line":576},17,[168,578,211],{"emptyLinePlaceholder":210},[168,580,582,585],{"class":170,"line":581},18,[168,583,584],{"class":174},"if",[168,586,587],{"class":178}," (result) {\n",[168,589,591,594,597,600,603],{"class":170,"line":590},19,[168,592,593],{"class":174},"  if",[168,595,596],{"class":178}," (result.status ",[168,598,599],{"class":174},"===",[168,601,602],{"class":185}," \"success\"",[168,604,605],{"class":178},") {\n",[168,607,609,612,615,617,620,623,626],{"class":170,"line":608},20,[168,610,611],{"class":178},"    console.",[168,613,614],{"class":227},"log",[168,616,231],{"class":178},[168,618,619],{"class":185},"\"Summary:\"",[168,621,622],{"class":178},", result.payload.summary, ",[168,624,625],{"class":185},"\"from\"",[168,627,628],{"class":178},", result.completedBy);\n",[168,630,632,635,638],{"class":170,"line":631},21,[168,633,634],{"class":178},"  } ",[168,636,637],{"class":174},"else",[168,639,640],{"class":178}," {\n",[168,642,644,646,648,650,653],{"class":170,"line":643},22,[168,645,611],{"class":178},[168,647,614],{"class":227},[168,649,231],{"class":178},[168,651,652],{"class":185},"\"Worker reported:\"",[168,654,655],{"class":178},", result.status, result.error?.message);\n",[168,657,659],{"class":170,"line":658},23,[168,660,661],{"class":178},"  }\n",[168,663,665],{"class":170,"line":664},24,[168,666,667],{"class":178},"}\n",[168,669,671],{"class":170,"line":670},25,[168,672,211],{"emptyLinePlaceholder":210},[168,674,676],{"class":170,"line":675},26,[168,677,678],{"class":277},"// Without waitForResult the call returns once the task is published\n",[168,680,682,684,686,688,690,692,695,698],{"class":170,"line":681},27,[168,683,330],{"class":174},[168,685,480],{"class":178},[168,687,483],{"class":227},[168,689,231],{"class":178},[168,691,310],{"class":185},[168,693,694],{"class":178},", { url: ",[168,696,697],{"class":185},"\"https://example.com/other\"",[168,699,700],{"class":178}," });\n",[15,702,703,706,707,710],{},[19,704,705],{},"priority"," and ",[19,708,709],{},"tags"," travel on the envelope for the worker to read; the SDK does not reorder or queue tasks by priority.",[389,712,714],{"id":713},"handling-tasks","Handling tasks",[155,716,717],{},[158,718,720],{"className":160,"code":719,"filename":415,"language":163,"meta":164,"style":164},"import { Handoff } from \"@nolag/agents\";\n\nconst handoff = new Handoff(room);\n\n// Only tasks whose capability is in the list reach the handler; pass \"*\" for all\nhandoff.onTask([\"research\", \"finance\"], async (task, respond) => {\n  console.log(\"Received:\", task.taskId, task.capability, task.priority, task.payload);\n\n  try {\n    const summary = await doResearch(task.payload);\n    respond(\"success\", { summary });\n  } catch (err) {\n    respond(\"error\", {}, { code: \"research_failed\", message: String(err) });\n  }\n});\n",[19,721,722,734,738,752,756,761,804,819,823,830,848,861,871,895,899],{"__ignoreMap":164},[168,723,724,726,728,730,732],{"class":170,"line":171},[168,725,175],{"class":174},[168,727,424],{"class":178},[168,729,182],{"class":174},[168,731,202],{"class":185},[168,733,189],{"class":178},[168,735,736],{"class":170,"line":192},[168,737,211],{"emptyLinePlaceholder":210},[168,739,740,742,744,746,748,750],{"class":170,"line":207},[168,741,217],{"class":174},[168,743,441],{"class":220},[168,745,224],{"class":174},[168,747,250],{"class":174},[168,749,448],{"class":227},[168,751,451],{"class":178},[168,753,754],{"class":170,"line":214},[168,755,211],{"emptyLinePlaceholder":210},[168,757,758],{"class":170,"line":240},[168,759,760],{"class":277},"// Only tasks whose capability is in the list reach the handler; pass \"*\" for all\n",[168,762,763,766,769,772,774,776,778,781,784,787,791,793,796,799,802],{"class":170,"line":259},[168,764,765],{"class":178},"handoff.",[168,767,768],{"class":227},"onTask",[168,770,771],{"class":178},"([",[168,773,310],{"class":185},[168,775,274],{"class":178},[168,777,315],{"class":185},[168,779,780],{"class":178},"], ",[168,782,783],{"class":174},"async",[168,785,786],{"class":178}," (",[168,788,790],{"class":789},"s4XuR","task",[168,792,274],{"class":178},[168,794,795],{"class":789},"respond",[168,797,798],{"class":178},") ",[168,800,801],{"class":174},"=>",[168,803,640],{"class":178},[168,805,806,809,811,813,816],{"class":170,"line":265},[168,807,808],{"class":178},"  console.",[168,810,614],{"class":227},[168,812,231],{"class":178},[168,814,815],{"class":185},"\"Received:\"",[168,817,818],{"class":178},", task.taskId, task.capability, task.priority, task.payload);\n",[168,820,821],{"class":170,"line":281},[168,822,211],{"emptyLinePlaceholder":210},[168,824,825,828],{"class":170,"line":293},[168,826,827],{"class":174},"  try",[168,829,640],{"class":178},[168,831,832,835,838,840,842,845],{"class":170,"line":321},[168,833,834],{"class":174},"    const",[168,836,837],{"class":220}," summary",[168,839,224],{"class":174},[168,841,477],{"class":174},[168,843,844],{"class":227}," doResearch",[168,846,847],{"class":178},"(task.payload);\n",[168,849,850,853,855,858],{"class":170,"line":327},[168,851,852],{"class":227},"    respond",[168,854,231],{"class":178},[168,856,857],{"class":185},"\"success\"",[168,859,860],{"class":178},", { summary });\n",[168,862,863,865,868],{"class":170,"line":342},[168,864,634],{"class":178},[168,866,867],{"class":174},"catch",[168,869,870],{"class":178}," (err) {\n",[168,872,873,875,877,880,883,886,889,892],{"class":170,"line":355},[168,874,852],{"class":227},[168,876,231],{"class":178},[168,878,879],{"class":185},"\"error\"",[168,881,882],{"class":178},", {}, { code: ",[168,884,885],{"class":185},"\"research_failed\"",[168,887,888],{"class":178},", message: ",[168,890,891],{"class":227},"String",[168,893,894],{"class":178},"(err) });\n",[168,896,897],{"class":170,"line":360},[168,898,661],{"class":178},[168,900,901],{"class":170,"line":565},[168,902,324],{"class":178},[15,904,905,906,909,910,913,914,917,918,920,921,926],{},"The capabilities a worker advertises come from the ",[19,907,908],{},"presence.capabilities"," option on ",[19,911,912],{},"NoLagAgents","; that is what ",[19,915,916],{},"dispatch()"," checks before publishing. The list passed to ",[19,919,768],{}," is a client-side filter: every worker in the room receives every task and drops the ones it does not handle. For broker-side routing see ",[922,923,925],"a",{"href":924},"/docs/agents/tags#server-side-routing","server-side routing",".",[389,928,930],{"id":929},"topic-mapping","Topic mapping",[28,932,933,942],{},[31,934,935],{},[34,936,937,939],{},[37,938,39],{},[37,940,941],{},"Purpose",[47,943,944,953],{},[34,945,946,950],{},[52,947,948],{},[19,949,56],{},[52,951,952],{},"Task envelopes from the dispatcher; one-of-N across a load-balance group when the client enables load balancing",[34,954,955,959],{},[52,956,957],{},[19,958,69],{},[52,960,961],{},"Result envelopes, published with the dispatcher's agent id as filter so only that dispatcher receives them",[381,963,964],{"id":107},"Inbox",[15,966,967,968,971],{},"The Inbox pattern provides direct agent-to-agent messaging. Unlike Handoff, which any capable worker can pick up, Inbox addresses a message to one agent by its ",[19,969,970],{},"agentId",". Use it for point-to-point communication between known agents.",[389,973,392],{"id":974},"when-to-use-1",[394,976,977,980,983],{},[397,978,979],{},"Sending instructions to a specific agent",[397,981,982],{},"Agent-to-agent conversation threads",[397,984,985],{},"Passing results between pipeline stages with known recipients",[389,987,989],{"id":988},"code-example","Code example",[155,991,992],{},[158,993,995],{"className":160,"code":994,"filename":415,"language":163,"meta":164,"style":164},"import { Inbox } from \"@nolag/agents\";\n\nconst inbox = new Inbox(room, agents.agentId);\n\n// Send a direct message to the agent whose agentId is \"reviewer-agent\"\ninbox.send(\"reviewer-agent\", {\n  type: \"review-request\",\n  document: draftContent,\n  deadline: \"2026-10-15\",\n});\n\n// Receive messages addressed to this agent\ninbox.onMessage((message) => {\n  console.log(\"From:\", message.from);\n  console.log(\"Payload:\", message.payload);\n  console.log(\"Sent at:\", new Date(message.createdAt).toISOString());\n});\n",[19,996,997,1010,1014,1031,1035,1040,1056,1066,1071,1081,1085,1089,1094,1113,1127,1141,1169],{"__ignoreMap":164},[168,998,999,1001,1004,1006,1008],{"class":170,"line":171},[168,1000,175],{"class":174},[168,1002,1003],{"class":178}," { Inbox } ",[168,1005,182],{"class":174},[168,1007,202],{"class":185},[168,1009,189],{"class":178},[168,1011,1012],{"class":170,"line":192},[168,1013,211],{"emptyLinePlaceholder":210},[168,1015,1016,1018,1021,1023,1025,1028],{"class":170,"line":207},[168,1017,217],{"class":174},[168,1019,1020],{"class":220}," inbox",[168,1022,224],{"class":174},[168,1024,250],{"class":174},[168,1026,1027],{"class":227}," Inbox",[168,1029,1030],{"class":178},"(room, agents.agentId);\n",[168,1032,1033],{"class":170,"line":214},[168,1034,211],{"emptyLinePlaceholder":210},[168,1036,1037],{"class":170,"line":240},[168,1038,1039],{"class":277},"// Send a direct message to the agent whose agentId is \"reviewer-agent\"\n",[168,1041,1042,1045,1048,1050,1053],{"class":170,"line":259},[168,1043,1044],{"class":178},"inbox.",[168,1046,1047],{"class":227},"send",[168,1049,231],{"class":178},[168,1051,1052],{"class":185},"\"reviewer-agent\"",[168,1054,1055],{"class":178},", {\n",[168,1057,1058,1061,1064],{"class":170,"line":265},[168,1059,1060],{"class":178},"  type: ",[168,1062,1063],{"class":185},"\"review-request\"",[168,1065,290],{"class":178},[168,1067,1068],{"class":170,"line":281},[168,1069,1070],{"class":178},"  document: draftContent,\n",[168,1072,1073,1076,1079],{"class":170,"line":293},[168,1074,1075],{"class":178},"  deadline: ",[168,1077,1078],{"class":185},"\"2026-10-15\"",[168,1080,290],{"class":178},[168,1082,1083],{"class":170,"line":321},[168,1084,324],{"class":178},[168,1086,1087],{"class":170,"line":327},[168,1088,211],{"emptyLinePlaceholder":210},[168,1090,1091],{"class":170,"line":342},[168,1092,1093],{"class":277},"// Receive messages addressed to this agent\n",[168,1095,1096,1098,1101,1104,1107,1109,1111],{"class":170,"line":355},[168,1097,1044],{"class":178},[168,1099,1100],{"class":227},"onMessage",[168,1102,1103],{"class":178},"((",[168,1105,1106],{"class":789},"message",[168,1108,798],{"class":178},[168,1110,801],{"class":174},[168,1112,640],{"class":178},[168,1114,1115,1117,1119,1121,1124],{"class":170,"line":360},[168,1116,808],{"class":178},[168,1118,614],{"class":227},[168,1120,231],{"class":178},[168,1122,1123],{"class":185},"\"From:\"",[168,1125,1126],{"class":178},", message.from);\n",[168,1128,1129,1131,1133,1135,1138],{"class":170,"line":565},[168,1130,808],{"class":178},[168,1132,614],{"class":227},[168,1134,231],{"class":178},[168,1136,1137],{"class":185},"\"Payload:\"",[168,1139,1140],{"class":178},", message.payload);\n",[168,1142,1143,1145,1147,1149,1152,1154,1157,1160,1163,1166],{"class":170,"line":571},[168,1144,808],{"class":178},[168,1146,614],{"class":227},[168,1148,231],{"class":178},[168,1150,1151],{"class":185},"\"Sent at:\"",[168,1153,274],{"class":178},[168,1155,1156],{"class":174},"new",[168,1158,1159],{"class":227}," Date",[168,1161,1162],{"class":178},"(message.createdAt).",[168,1164,1165],{"class":227},"toISOString",[168,1167,1168],{"class":178},"());\n",[168,1170,1171],{"class":170,"line":576},[168,1172,324],{"class":178},[15,1174,1175,1176,1178,1179,1181,1182,1185,1186,1190],{},"Every agent in the room receives the ",[19,1177,107],{}," topic; each ",[19,1180,964],{}," instance hands your handler only the messages whose ",[19,1183,1184],{},"to"," matches its agent id. A recipient that is not connected when the message is published does not receive it later; see ",[922,1187,1189],{"href":1188},"/docs/concepts/replay","Replay"," for what the broker does and does not replay.",[389,1192,930],{"id":1193},"topic-mapping-1",[28,1195,1196,1204],{},[31,1197,1198],{},[34,1199,1200,1202],{},[37,1201,39],{},[37,1203,941],{},[47,1205,1206],{},[34,1207,1208,1212],{},[52,1209,1210],{},[19,1211,107],{},[52,1213,1214,1215,1217],{},"Direct messages; ",[19,1216,1184],{}," names the recipient agent id",[381,1219,1221],{"id":1220},"blackboard","Blackboard",[15,1223,1224],{},"The Blackboard pattern provides shared key-value state. All agents in a room see the same keys. Writes are broadcast in real time, and each write carries a version number so readers can tell a stale value from a fresh one. Writes are last-writer-wins: nothing is rejected, and the version is computed by the writer from the last value it saw.",[389,1226,392],{"id":1227},"when-to-use-2",[394,1229,1230,1233,1236],{},[397,1231,1232],{},"Sharing workflow state across multiple agents",[397,1234,1235],{},"Maintaining a shared plan or configuration",[397,1237,1238],{},"Accumulating results from multiple agents into a single view",[389,1240,989],{"id":1241},"code-example-1",[155,1243,1244],{},[158,1245,1247],{"className":160,"code":1246,"filename":415,"language":163,"meta":164,"style":164},"import { Blackboard } from \"@nolag/agents\";\n\ninterface Plan {\n  steps: string[];\n  currentStep: number;\n}\n\nconst blackboard = new Blackboard(room, agents.agentId);\n\n// Write shared state\nblackboard.set(\"workflow-plan\", {\n  steps: [\"research\", \"draft\", \"review\", \"publish\"],\n  currentStep: 0,\n});\n\n// Read shared state (undefined until a write for that key has been seen)\nconst plan = blackboard.get(\"workflow-plan\") as Plan | undefined;\nif (plan) {\n  console.log(\"Current step:\", plan.steps[plan.currentStep]);\n\n  // Read-modify-write; the SDK bumps the version for you\n  blackboard.set(\"workflow-plan\", { ...plan, currentStep: plan.currentStep + 1 });\n}\n\n// Watch one key for changes\nblackboard.onChange(\"workflow-plan\", (envelope) => {\n  console.log(`${envelope.key} v${envelope.version} by ${envelope.updatedBy}:`, envelope.value);\n});\n\n// Everything this agent has seen\nfor (const [key, envelope] of blackboard.getAll()) {\n  console.log(key, envelope.version);\n}\n",[19,1248,1249,1262,1266,1276,1290,1302,1306,1310,1326,1330,1335,1350,1374,1384,1388,1392,1397,1431,1438,1452,1456,1461,1489,1493,1497,1502,1525,1569,1574,1579,1585,1618,1628],{"__ignoreMap":164},[168,1250,1251,1253,1256,1258,1260],{"class":170,"line":171},[168,1252,175],{"class":174},[168,1254,1255],{"class":178}," { Blackboard } ",[168,1257,182],{"class":174},[168,1259,202],{"class":185},[168,1261,189],{"class":178},[168,1263,1264],{"class":170,"line":192},[168,1265,211],{"emptyLinePlaceholder":210},[168,1267,1268,1271,1274],{"class":170,"line":207},[168,1269,1270],{"class":174},"interface",[168,1272,1273],{"class":227}," Plan",[168,1275,640],{"class":178},[168,1277,1278,1281,1284,1287],{"class":170,"line":214},[168,1279,1280],{"class":789},"  steps",[168,1282,1283],{"class":174},":",[168,1285,1286],{"class":220}," string",[168,1288,1289],{"class":178},"[];\n",[168,1291,1292,1295,1297,1300],{"class":170,"line":240},[168,1293,1294],{"class":789},"  currentStep",[168,1296,1283],{"class":174},[168,1298,1299],{"class":220}," number",[168,1301,189],{"class":178},[168,1303,1304],{"class":170,"line":259},[168,1305,667],{"class":178},[168,1307,1308],{"class":170,"line":265},[168,1309,211],{"emptyLinePlaceholder":210},[168,1311,1312,1314,1317,1319,1321,1324],{"class":170,"line":281},[168,1313,217],{"class":174},[168,1315,1316],{"class":220}," blackboard",[168,1318,224],{"class":174},[168,1320,250],{"class":174},[168,1322,1323],{"class":227}," Blackboard",[168,1325,1030],{"class":178},[168,1327,1328],{"class":170,"line":293},[168,1329,211],{"emptyLinePlaceholder":210},[168,1331,1332],{"class":170,"line":321},[168,1333,1334],{"class":277},"// Write shared state\n",[168,1336,1337,1340,1343,1345,1348],{"class":170,"line":327},[168,1338,1339],{"class":178},"blackboard.",[168,1341,1342],{"class":227},"set",[168,1344,231],{"class":178},[168,1346,1347],{"class":185},"\"workflow-plan\"",[168,1349,1055],{"class":178},[168,1351,1352,1355,1357,1359,1362,1364,1367,1369,1372],{"class":170,"line":342},[168,1353,1354],{"class":178},"  steps: [",[168,1356,310],{"class":185},[168,1358,274],{"class":178},[168,1360,1361],{"class":185},"\"draft\"",[168,1363,274],{"class":178},[168,1365,1366],{"class":185},"\"review\"",[168,1368,274],{"class":178},[168,1370,1371],{"class":185},"\"publish\"",[168,1373,562],{"class":178},[168,1375,1376,1379,1382],{"class":170,"line":355},[168,1377,1378],{"class":178},"  currentStep: ",[168,1380,1381],{"class":220},"0",[168,1383,290],{"class":178},[168,1385,1386],{"class":170,"line":360},[168,1387,324],{"class":178},[168,1389,1390],{"class":170,"line":565},[168,1391,211],{"emptyLinePlaceholder":210},[168,1393,1394],{"class":170,"line":571},[168,1395,1396],{"class":277},"// Read shared state (undefined until a write for that key has been seen)\n",[168,1398,1399,1401,1404,1406,1409,1412,1414,1416,1418,1421,1423,1426,1429],{"class":170,"line":576},[168,1400,217],{"class":174},[168,1402,1403],{"class":220}," plan",[168,1405,224],{"class":174},[168,1407,1408],{"class":178}," blackboard.",[168,1410,1411],{"class":227},"get",[168,1413,231],{"class":178},[168,1415,1347],{"class":185},[168,1417,798],{"class":178},[168,1419,1420],{"class":174},"as",[168,1422,1273],{"class":227},[168,1424,1425],{"class":174}," |",[168,1427,1428],{"class":220}," undefined",[168,1430,189],{"class":178},[168,1432,1433,1435],{"class":170,"line":581},[168,1434,584],{"class":174},[168,1436,1437],{"class":178}," (plan) {\n",[168,1439,1440,1442,1444,1446,1449],{"class":170,"line":590},[168,1441,808],{"class":178},[168,1443,614],{"class":227},[168,1445,231],{"class":178},[168,1447,1448],{"class":185},"\"Current step:\"",[168,1450,1451],{"class":178},", plan.steps[plan.currentStep]);\n",[168,1453,1454],{"class":170,"line":608},[168,1455,211],{"emptyLinePlaceholder":210},[168,1457,1458],{"class":170,"line":631},[168,1459,1460],{"class":277},"  // Read-modify-write; the SDK bumps the version for you\n",[168,1462,1463,1466,1468,1470,1472,1475,1478,1481,1484,1487],{"class":170,"line":643},[168,1464,1465],{"class":178},"  blackboard.",[168,1467,1342],{"class":227},[168,1469,231],{"class":178},[168,1471,1347],{"class":185},[168,1473,1474],{"class":178},", { ",[168,1476,1477],{"class":174},"...",[168,1479,1480],{"class":178},"plan, currentStep: plan.currentStep ",[168,1482,1483],{"class":174},"+",[168,1485,1486],{"class":220}," 1",[168,1488,700],{"class":178},[168,1490,1491],{"class":170,"line":658},[168,1492,667],{"class":178},[168,1494,1495],{"class":170,"line":664},[168,1496,211],{"emptyLinePlaceholder":210},[168,1498,1499],{"class":170,"line":670},[168,1500,1501],{"class":277},"// Watch one key for changes\n",[168,1503,1504,1506,1509,1511,1513,1516,1519,1521,1523],{"class":170,"line":675},[168,1505,1339],{"class":178},[168,1507,1508],{"class":227},"onChange",[168,1510,231],{"class":178},[168,1512,1347],{"class":185},[168,1514,1515],{"class":178},", (",[168,1517,1518],{"class":789},"envelope",[168,1520,798],{"class":178},[168,1522,801],{"class":174},[168,1524,640],{"class":178},[168,1526,1527,1529,1531,1533,1536,1538,1540,1543,1546,1548,1550,1553,1556,1558,1560,1563,1566],{"class":170,"line":681},[168,1528,808],{"class":178},[168,1530,614],{"class":227},[168,1532,231],{"class":178},[168,1534,1535],{"class":185},"`${",[168,1537,1518],{"class":178},[168,1539,926],{"class":185},[168,1541,1542],{"class":178},"key",[168,1544,1545],{"class":185},"} v${",[168,1547,1518],{"class":178},[168,1549,926],{"class":185},[168,1551,1552],{"class":178},"version",[168,1554,1555],{"class":185},"} by ${",[168,1557,1518],{"class":178},[168,1559,926],{"class":185},[168,1561,1562],{"class":178},"updatedBy",[168,1564,1565],{"class":185},"}:`",[168,1567,1568],{"class":178},", envelope.value);\n",[168,1570,1572],{"class":170,"line":1571},28,[168,1573,324],{"class":178},[168,1575,1577],{"class":170,"line":1576},29,[168,1578,211],{"emptyLinePlaceholder":210},[168,1580,1582],{"class":170,"line":1581},30,[168,1583,1584],{"class":277},"// Everything this agent has seen\n",[168,1586,1588,1591,1593,1595,1598,1600,1602,1604,1607,1610,1612,1615],{"class":170,"line":1587},31,[168,1589,1590],{"class":174},"for",[168,1592,786],{"class":178},[168,1594,217],{"class":174},[168,1596,1597],{"class":178}," [",[168,1599,1542],{"class":220},[168,1601,274],{"class":178},[168,1603,1518],{"class":220},[168,1605,1606],{"class":178},"] ",[168,1608,1609],{"class":174},"of",[168,1611,1408],{"class":178},[168,1613,1614],{"class":227},"getAll",[168,1616,1617],{"class":178},"()) {\n",[168,1619,1621,1623,1625],{"class":170,"line":1620},32,[168,1622,808],{"class":178},[168,1624,614],{"class":227},[168,1626,1627],{"class":178},"(key, envelope.version);\n",[168,1629,1631],{"class":170,"line":1630},33,[168,1632,667],{"class":178},[15,1634,1635,1637],{},[19,1636,82],{}," is published retained, so the broker hands a newly subscribed agent the most recent write on the topic. That is one message, not the whole board: an agent that joins after several keys were written sees the last key only, until the others are written again.",[389,1639,930],{"id":1640},"topic-mapping-2",[28,1642,1643,1651],{},[31,1644,1645],{},[34,1646,1647,1649],{},[37,1648,39],{},[37,1650,941],{},[47,1652,1653],{},[34,1654,1655,1659],{},[52,1656,1657],{},[19,1658,82],{},[52,1660,1661,1662,274,1664,274,1667,274,1669,1671],{},"State envelopes (",[19,1663,1542],{},[19,1665,1666],{},"value",[19,1668,1552],{},[19,1670,1562],{},"), published retained",[381,1673,1675],{"id":1674},"observe","Observe",[15,1677,1678],{},"The Observe pattern provides a real-time event stream for monitoring. Agents emit structured events with a category and a severity; observers and dashboards subscribe to the stream. Only what agents emit appears on it: the other patterns do not emit events on their own.",[389,1680,392],{"id":1681},"when-to-use-3",[394,1683,1684,1687,1690,1693],{},[397,1685,1686],{},"Building monitoring dashboards for agent workflows",[397,1688,1689],{},"Audit logging of agent decisions, at the points you choose to emit",[397,1691,1692],{},"Debugging agent behavior in real time",[397,1694,1695],{},"Triggering side effects on specific events",[389,1697,989],{"id":1698},"code-example-2",[155,1700,1701],{},[158,1702,1704],{"className":160,"code":1703,"filename":415,"language":163,"meta":164,"style":164},"import { Observe } from \"@nolag/agents\";\n\nconst observe = new Observe(room, agents.agentId);\n\n// Watch all events\nobserve.on((event) => {\n  console.log(`[${event.severity}] ${event.category} from ${event.emittedBy}`, event.payload);\n});\n\n// Watch one category, or one severity\nobserve.on((event) => console.log(\"Checkpoint:\", event.payload), { category: \"checkpoint\" });\nobserve.on((event) => alertOps(event), { severity: \"error\" });\n\n// Emit events (severity defaults to \"info\")\nobserve.emit(\"checkpoint\", { step: \"research-complete\", duration: 4200 });\nobserve.emit(\"llm:error\", { model: \"gpt-4o\", message: \"rate limited\" }, \"error\");\n",[19,1705,1706,1719,1723,1739,1743,1748,1767,1811,1815,1819,1824,1856,1880,1884,1889,1914],{"__ignoreMap":164},[168,1707,1708,1710,1713,1715,1717],{"class":170,"line":171},[168,1709,175],{"class":174},[168,1711,1712],{"class":178}," { Observe } ",[168,1714,182],{"class":174},[168,1716,202],{"class":185},[168,1718,189],{"class":178},[168,1720,1721],{"class":170,"line":192},[168,1722,211],{"emptyLinePlaceholder":210},[168,1724,1725,1727,1730,1732,1734,1737],{"class":170,"line":207},[168,1726,217],{"class":174},[168,1728,1729],{"class":220}," observe",[168,1731,224],{"class":174},[168,1733,250],{"class":174},[168,1735,1736],{"class":227}," Observe",[168,1738,1030],{"class":178},[168,1740,1741],{"class":170,"line":214},[168,1742,211],{"emptyLinePlaceholder":210},[168,1744,1745],{"class":170,"line":240},[168,1746,1747],{"class":277},"// Watch all events\n",[168,1749,1750,1753,1756,1758,1761,1763,1765],{"class":170,"line":259},[168,1751,1752],{"class":178},"observe.",[168,1754,1755],{"class":227},"on",[168,1757,1103],{"class":178},[168,1759,1760],{"class":789},"event",[168,1762,798],{"class":178},[168,1764,801],{"class":174},[168,1766,640],{"class":178},[168,1768,1769,1771,1773,1775,1778,1780,1782,1785,1788,1790,1792,1795,1798,1800,1802,1805,1808],{"class":170,"line":265},[168,1770,808],{"class":178},[168,1772,614],{"class":227},[168,1774,231],{"class":178},[168,1776,1777],{"class":185},"`[${",[168,1779,1760],{"class":178},[168,1781,926],{"class":185},[168,1783,1784],{"class":178},"severity",[168,1786,1787],{"class":185},"}] ${",[168,1789,1760],{"class":178},[168,1791,926],{"class":185},[168,1793,1794],{"class":178},"category",[168,1796,1797],{"class":185},"} from ${",[168,1799,1760],{"class":178},[168,1801,926],{"class":185},[168,1803,1804],{"class":178},"emittedBy",[168,1806,1807],{"class":185},"}`",[168,1809,1810],{"class":178},", event.payload);\n",[168,1812,1813],{"class":170,"line":281},[168,1814,324],{"class":178},[168,1816,1817],{"class":170,"line":293},[168,1818,211],{"emptyLinePlaceholder":210},[168,1820,1821],{"class":170,"line":321},[168,1822,1823],{"class":277},"// Watch one category, or one severity\n",[168,1825,1826,1828,1830,1832,1834,1836,1838,1841,1843,1845,1848,1851,1854],{"class":170,"line":327},[168,1827,1752],{"class":178},[168,1829,1755],{"class":227},[168,1831,1103],{"class":178},[168,1833,1760],{"class":789},[168,1835,798],{"class":178},[168,1837,801],{"class":174},[168,1839,1840],{"class":178}," console.",[168,1842,614],{"class":227},[168,1844,231],{"class":178},[168,1846,1847],{"class":185},"\"Checkpoint:\"",[168,1849,1850],{"class":178},", event.payload), { category: ",[168,1852,1853],{"class":185},"\"checkpoint\"",[168,1855,700],{"class":178},[168,1857,1858,1860,1862,1864,1866,1868,1870,1873,1876,1878],{"class":170,"line":342},[168,1859,1752],{"class":178},[168,1861,1755],{"class":227},[168,1863,1103],{"class":178},[168,1865,1760],{"class":789},[168,1867,798],{"class":178},[168,1869,801],{"class":174},[168,1871,1872],{"class":227}," alertOps",[168,1874,1875],{"class":178},"(event), { severity: ",[168,1877,879],{"class":185},[168,1879,700],{"class":178},[168,1881,1882],{"class":170,"line":355},[168,1883,211],{"emptyLinePlaceholder":210},[168,1885,1886],{"class":170,"line":360},[168,1887,1888],{"class":277},"// Emit events (severity defaults to \"info\")\n",[168,1890,1891,1893,1896,1898,1900,1903,1906,1909,1912],{"class":170,"line":565},[168,1892,1752],{"class":178},[168,1894,1895],{"class":227},"emit",[168,1897,231],{"class":178},[168,1899,1853],{"class":185},[168,1901,1902],{"class":178},", { step: ",[168,1904,1905],{"class":185},"\"research-complete\"",[168,1907,1908],{"class":178},", duration: ",[168,1910,1911],{"class":220},"4200",[168,1913,700],{"class":178},[168,1915,1916,1918,1920,1922,1925,1928,1931,1933,1936,1939,1941],{"class":170,"line":571},[168,1917,1752],{"class":178},[168,1919,1895],{"class":227},[168,1921,231],{"class":178},[168,1923,1924],{"class":185},"\"llm:error\"",[168,1926,1927],{"class":178},", { model: ",[168,1929,1930],{"class":185},"\"gpt-4o\"",[168,1932,888],{"class":178},[168,1934,1935],{"class":185},"\"rate limited\"",[168,1937,1938],{"class":178}," }, ",[168,1940,879],{"class":185},[168,1942,237],{"class":178},[15,1944,17,1945,1948,1949,1952,1953,1956,1957,1960],{},[19,1946,1947],{},"on()"," filter runs after the event arrives. For a busy room, ",[19,1950,1951],{},"observe.setFilters([\"checkpoint\"])"," moves the selection to the broker; emitters then pass ",[19,1954,1955],{},"{ filter: \"checkpoint\" }"," as the fourth argument to ",[19,1958,1959],{},"emit()"," so the broker can route it. Observers without filters still receive everything.",[389,1962,930],{"id":1963},"topic-mapping-3",[28,1965,1966,1974],{},[31,1967,1968],{},[34,1969,1970,1972],{},[37,1971,39],{},[37,1973,941],{},[47,1975,1976],{},[34,1977,1978,1982],{},[52,1979,1980],{},[19,1981,95],{},[52,1983,1984,1985,274,1987,274,1989,274,1992,1994],{},"Event envelopes (",[19,1986,1794],{},[19,1988,1784],{},[19,1990,1991],{},"payload",[19,1993,1804],{},")",[381,1996,1998],{"id":1997},"approve","Approve",[15,2000,2001,2002,2005,2006,2009],{},"The Approve pattern implements human-in-the-loop gating. An agent can pause its workflow and request approval from a human (or supervisor agent) before proceeding. The request carries an ",[19,2003,2004],{},"action"," name and free-form ",[19,2007,2008],{},"context"," so the approver can make an informed decision.",[389,2011,392],{"id":2012},"when-to-use-4",[394,2014,2015,2018,2021],{},[397,2016,2017],{},"Gating destructive or high-stakes actions",[397,2019,2020],{},"Compliance workflows requiring human sign-off",[397,2022,2023],{},"Multi-level approval chains",[389,2025,989],{"id":2026},"code-example-3",[155,2028,2029],{},[158,2030,2032],{"className":160,"code":2031,"filename":415,"language":163,"meta":164,"style":164},"import { Approve } from \"@nolag/agents\";\n\nconst approve = new Approve(room, agents.agentId);\n\n// Agent requests approval; the promise resolves with the decision\n// and rejects if nobody answers within the timeout\nconst approval = await approve.request(\n  \"send-email\",\n  { to: \"investors@company.com\", subject: \"Quarterly report\", bodyPreview: reportSummary },\n  { urgency: \"high\", timeout: 600000 },\n);\n\nif (approval.decision === \"approved\") {\n  await sendEmail(reportHtml);\n} else {\n  console.log(approval.decision, \"by\", approval.respondedBy, approval.reason);\n}\n\n// Human-facing app (or supervisor agent) handles approval requests\napprove.onRequest((request, respond) => {\n  console.log(request.action, request.urgency, request.requestedBy, request.context);\n  respond(\"approved\");\n  // or: respond(\"rejected\", \"Budget not approved\")\n  // or: respond(\"deferred\", \"Needs finance review first\")\n});\n",[19,2033,2034,2047,2051,2067,2071,2076,2081,2100,2107,2124,2139,2143,2147,2161,2172,2181,2196,2200,2204,2209,2231,2240,2252,2257,2262],{"__ignoreMap":164},[168,2035,2036,2038,2041,2043,2045],{"class":170,"line":171},[168,2037,175],{"class":174},[168,2039,2040],{"class":178}," { Approve } ",[168,2042,182],{"class":174},[168,2044,202],{"class":185},[168,2046,189],{"class":178},[168,2048,2049],{"class":170,"line":192},[168,2050,211],{"emptyLinePlaceholder":210},[168,2052,2053,2055,2058,2060,2062,2065],{"class":170,"line":207},[168,2054,217],{"class":174},[168,2056,2057],{"class":220}," approve",[168,2059,224],{"class":174},[168,2061,250],{"class":174},[168,2063,2064],{"class":227}," Approve",[168,2066,1030],{"class":178},[168,2068,2069],{"class":170,"line":214},[168,2070,211],{"emptyLinePlaceholder":210},[168,2072,2073],{"class":170,"line":240},[168,2074,2075],{"class":277},"// Agent requests approval; the promise resolves with the decision\n",[168,2077,2078],{"class":170,"line":259},[168,2079,2080],{"class":277},"// and rejects if nobody answers within the timeout\n",[168,2082,2083,2085,2088,2090,2092,2095,2098],{"class":170,"line":265},[168,2084,217],{"class":174},[168,2086,2087],{"class":220}," approval",[168,2089,224],{"class":174},[168,2091,477],{"class":174},[168,2093,2094],{"class":178}," approve.",[168,2096,2097],{"class":227},"request",[168,2099,486],{"class":178},[168,2101,2102,2105],{"class":170,"line":281},[168,2103,2104],{"class":185},"  \"send-email\"",[168,2106,290],{"class":178},[168,2108,2109,2112,2115,2118,2121],{"class":170,"line":293},[168,2110,2111],{"class":178},"  { to: ",[168,2113,2114],{"class":185},"\"investors@company.com\"",[168,2116,2117],{"class":178},", subject: ",[168,2119,2120],{"class":185},"\"Quarterly report\"",[168,2122,2123],{"class":178},", bodyPreview: reportSummary },\n",[168,2125,2126,2129,2131,2134,2137],{"class":170,"line":321},[168,2127,2128],{"class":178},"  { urgency: ",[168,2130,517],{"class":185},[168,2132,2133],{"class":178},", timeout: ",[168,2135,2136],{"class":220},"600000",[168,2138,504],{"class":178},[168,2140,2141],{"class":170,"line":327},[168,2142,237],{"class":178},[168,2144,2145],{"class":170,"line":342},[168,2146,211],{"emptyLinePlaceholder":210},[168,2148,2149,2151,2154,2156,2159],{"class":170,"line":355},[168,2150,584],{"class":174},[168,2152,2153],{"class":178}," (approval.decision ",[168,2155,599],{"class":174},[168,2157,2158],{"class":185}," \"approved\"",[168,2160,605],{"class":178},[168,2162,2163,2166,2169],{"class":170,"line":360},[168,2164,2165],{"class":174},"  await",[168,2167,2168],{"class":227}," sendEmail",[168,2170,2171],{"class":178},"(reportHtml);\n",[168,2173,2174,2177,2179],{"class":170,"line":565},[168,2175,2176],{"class":178},"} ",[168,2178,637],{"class":174},[168,2180,640],{"class":178},[168,2182,2183,2185,2187,2190,2193],{"class":170,"line":571},[168,2184,808],{"class":178},[168,2186,614],{"class":227},[168,2188,2189],{"class":178},"(approval.decision, ",[168,2191,2192],{"class":185},"\"by\"",[168,2194,2195],{"class":178},", approval.respondedBy, approval.reason);\n",[168,2197,2198],{"class":170,"line":576},[168,2199,667],{"class":178},[168,2201,2202],{"class":170,"line":581},[168,2203,211],{"emptyLinePlaceholder":210},[168,2205,2206],{"class":170,"line":590},[168,2207,2208],{"class":277},"// Human-facing app (or supervisor agent) handles approval requests\n",[168,2210,2211,2214,2217,2219,2221,2223,2225,2227,2229],{"class":170,"line":608},[168,2212,2213],{"class":178},"approve.",[168,2215,2216],{"class":227},"onRequest",[168,2218,1103],{"class":178},[168,2220,2097],{"class":789},[168,2222,274],{"class":178},[168,2224,795],{"class":789},[168,2226,798],{"class":178},[168,2228,801],{"class":174},[168,2230,640],{"class":178},[168,2232,2233,2235,2237],{"class":170,"line":631},[168,2234,808],{"class":178},[168,2236,614],{"class":227},[168,2238,2239],{"class":178},"(request.action, request.urgency, request.requestedBy, request.context);\n",[168,2241,2242,2245,2247,2250],{"class":170,"line":643},[168,2243,2244],{"class":227},"  respond",[168,2246,231],{"class":178},[168,2248,2249],{"class":185},"\"approved\"",[168,2251,237],{"class":178},[168,2253,2254],{"class":170,"line":658},[168,2255,2256],{"class":277},"  // or: respond(\"rejected\", \"Budget not approved\")\n",[168,2258,2259],{"class":170,"line":664},[168,2260,2261],{"class":277},"  // or: respond(\"deferred\", \"Needs finance review first\")\n",[168,2263,2264],{"class":170,"line":670},[168,2265,324],{"class":178},[15,2267,2268,2269,274,2272,2275,2276,2279,2280,2283],{},"Decisions are ",[19,2270,2271],{},"approved",[19,2273,2274],{},"rejected",", or ",[19,2277,2278],{},"deferred",", with an optional ",[19,2281,2282],{},"reason"," string. Nothing else travels back with the decision; if the approver needs to hand an edited payload to the agent, put it on the Blackboard.",[389,2285,930],{"id":2286},"topic-mapping-4",[28,2288,2289,2297],{},[31,2290,2291],{},[34,2292,2293,2295],{},[37,2294,39],{},[37,2296,941],{},[47,2298,2299],{},[34,2300,2301,2305],{},[52,2302,2303],{},[19,2304,131],{},[52,2306,2307],{},"Approval requests and decisions, published retained (the most recent message on the topic is delivered to a new subscriber)",[381,2309,2310],{"id":119},"Tools",[15,2312,2313],{},"The Tools pattern enables remote tool invocation. One agent registers tool handlers; other agents call them by name and receive a correlated response. This is similar to MCP tool use but runs over the NoLag messaging layer for real-time, multi-agent scenarios. There is no registry to browse: callers need to know the tool name.",[389,2315,392],{"id":2316},"when-to-use-5",[394,2318,2319,2322,2325],{},[397,2320,2321],{},"Sharing capabilities across agents (search, database access, APIs)",[397,2323,2324],{},"Building tool-augmented agents that compose external services",[397,2326,2327],{},"Running several replicas of a tool server behind one name (with load balancing on the client)",[389,2329,989],{"id":2330},"code-example-4",[155,2332,2333],{},[158,2334,2336],{"className":160,"code":2335,"filename":415,"language":163,"meta":164,"style":164},"import { Tools } from \"@nolag/agents\";\n\nconst tools = new Tools(room, agents.agentId);\n\n// Tool server: register a handler; its return value is the response\ntools.register(\"web-search\", async (args) => {\n  const results = await searchWeb(args.query as string);\n  return { results };\n});\n\n// Caller: invoke by name and wait for the correlated response\nconst response = await tools.invoke(\n  \"web-search\",\n  { query: \"latest quarterly earnings ACME Corp\" },\n  { timeout: 15000 },\n);\n\nif (response.status === \"success\") {\n  console.log(\"Search results:\", response.result);\n} else {\n  console.log(\"Tool failed:\", response.error?.code, response.error?.message);\n}\n",[19,2337,2338,2351,2355,2371,2375,2380,2408,2432,2440,2444,2448,2453,2472,2479,2489,2499,2503,2507,2520,2534,2542,2556],{"__ignoreMap":164},[168,2339,2340,2342,2345,2347,2349],{"class":170,"line":171},[168,2341,175],{"class":174},[168,2343,2344],{"class":178}," { Tools } ",[168,2346,182],{"class":174},[168,2348,202],{"class":185},[168,2350,189],{"class":178},[168,2352,2353],{"class":170,"line":192},[168,2354,211],{"emptyLinePlaceholder":210},[168,2356,2357,2359,2362,2364,2366,2369],{"class":170,"line":207},[168,2358,217],{"class":174},[168,2360,2361],{"class":220}," tools",[168,2363,224],{"class":174},[168,2365,250],{"class":174},[168,2367,2368],{"class":227}," Tools",[168,2370,1030],{"class":178},[168,2372,2373],{"class":170,"line":214},[168,2374,211],{"emptyLinePlaceholder":210},[168,2376,2377],{"class":170,"line":240},[168,2378,2379],{"class":277},"// Tool server: register a handler; its return value is the response\n",[168,2381,2382,2385,2388,2390,2393,2395,2397,2399,2402,2404,2406],{"class":170,"line":259},[168,2383,2384],{"class":178},"tools.",[168,2386,2387],{"class":227},"register",[168,2389,231],{"class":178},[168,2391,2392],{"class":185},"\"web-search\"",[168,2394,274],{"class":178},[168,2396,783],{"class":174},[168,2398,786],{"class":178},[168,2400,2401],{"class":789},"args",[168,2403,798],{"class":178},[168,2405,801],{"class":174},[168,2407,640],{"class":178},[168,2409,2410,2413,2416,2418,2420,2423,2426,2428,2430],{"class":170,"line":265},[168,2411,2412],{"class":174},"  const",[168,2414,2415],{"class":220}," results",[168,2417,224],{"class":174},[168,2419,477],{"class":174},[168,2421,2422],{"class":227}," searchWeb",[168,2424,2425],{"class":178},"(args.query ",[168,2427,1420],{"class":174},[168,2429,1286],{"class":220},[168,2431,237],{"class":178},[168,2433,2434,2437],{"class":170,"line":281},[168,2435,2436],{"class":174},"  return",[168,2438,2439],{"class":178}," { results };\n",[168,2441,2442],{"class":170,"line":293},[168,2443,324],{"class":178},[168,2445,2446],{"class":170,"line":321},[168,2447,211],{"emptyLinePlaceholder":210},[168,2449,2450],{"class":170,"line":327},[168,2451,2452],{"class":277},"// Caller: invoke by name and wait for the correlated response\n",[168,2454,2455,2457,2460,2462,2464,2467,2470],{"class":170,"line":342},[168,2456,217],{"class":174},[168,2458,2459],{"class":220}," response",[168,2461,224],{"class":174},[168,2463,477],{"class":174},[168,2465,2466],{"class":178}," tools.",[168,2468,2469],{"class":227},"invoke",[168,2471,486],{"class":178},[168,2473,2474,2477],{"class":170,"line":355},[168,2475,2476],{"class":185},"  \"web-search\"",[168,2478,290],{"class":178},[168,2480,2481,2484,2487],{"class":170,"line":360},[168,2482,2483],{"class":178},"  { query: ",[168,2485,2486],{"class":185},"\"latest quarterly earnings ACME Corp\"",[168,2488,504],{"class":178},[168,2490,2491,2494,2497],{"class":170,"line":565},[168,2492,2493],{"class":178},"  { timeout: ",[168,2495,2496],{"class":220},"15000",[168,2498,504],{"class":178},[168,2500,2501],{"class":170,"line":571},[168,2502,237],{"class":178},[168,2504,2505],{"class":170,"line":576},[168,2506,211],{"emptyLinePlaceholder":210},[168,2508,2509,2511,2514,2516,2518],{"class":170,"line":581},[168,2510,584],{"class":174},[168,2512,2513],{"class":178}," (response.status ",[168,2515,599],{"class":174},[168,2517,602],{"class":185},[168,2519,605],{"class":178},[168,2521,2522,2524,2526,2528,2531],{"class":170,"line":590},[168,2523,808],{"class":178},[168,2525,614],{"class":227},[168,2527,231],{"class":178},[168,2529,2530],{"class":185},"\"Search results:\"",[168,2532,2533],{"class":178},", response.result);\n",[168,2535,2536,2538,2540],{"class":170,"line":608},[168,2537,2176],{"class":178},[168,2539,637],{"class":174},[168,2541,640],{"class":178},[168,2543,2544,2546,2548,2550,2553],{"class":170,"line":631},[168,2545,808],{"class":178},[168,2547,614],{"class":227},[168,2549,231],{"class":178},[168,2551,2552],{"class":185},"\"Tool failed:\"",[168,2554,2555],{"class":178},", response.error?.code, response.error?.message);\n",[168,2557,2558],{"class":170,"line":643},[168,2559,667],{"class":178},[15,2561,2562,2563,706,2566,2569,2570,2572,2573,2576,2577,2580],{},"A handler that throws produces a response with ",[19,2564,2565],{},"status: \"error\"",[19,2567,2568],{},"error.code: \"TOOL_ERROR\"",". A tool server that owns the tool's namespace (the prefix before the first ",[19,2571,926],{},", or no prefix) but has no handler for the name answers with ",[19,2574,2575],{},"NO_HANDLER"," instead of letting the caller wait out the timeout. ",[19,2578,2579],{},"invoke()"," rejects only when the timeout passes with no response at all.",[389,2582,930],{"id":2583},"topic-mapping-5",[28,2585,2586,2594],{},[31,2587,2588],{},[34,2589,2590,2592],{},[37,2591,39],{},[37,2593,941],{},[47,2595,2596,2605],{},[34,2597,2598,2602],{},[52,2599,2600],{},[19,2601,119],{},[52,2603,2604],{},"Tool requests; one-of-N across a load-balance group when the client enables load balancing",[34,2606,2607,2611],{},[52,2608,2609],{},[19,2610,69],{},[52,2612,2613],{},"Tool responses, published with the caller's agent id as filter",[381,2615,2617],{"id":2616},"tenant-isolation","Tenant Isolation",[15,2619,2620,2621,2625,2626,2629,2630,926],{},"All six coordination patterns work with ",[922,2622,2624],{"href":2623},"/docs/scopes/getting-started","Access Scopes"," for multi-tenant deployments. When you assign an agent actor to a scope, the broker rewrites every topic that actor publishes or subscribes to from ",[19,2627,2628],{},"app/room/topic"," to ",[19,2631,2632],{},"app/scope/room/topic",[15,2634,2635,2636,2638,2639,2642,2643,2646,2647,2650],{},"For example, the ",[19,2637,56],{}," topic for an actor in the ",[19,2640,2641],{},"acme"," scope becomes ",[19,2644,2645],{},"my-agents-a3f9/acme/default-workflow/tasks"," instead of ",[19,2648,2649],{},"my-agents-a3f9/default-workflow/tasks",". An orchestrator in one tenant cannot dispatch tasks to workers in another tenant. The isolation applies to all patterns (Inbox, Blackboard, Observe, Approve, and Tools) with no changes to your agent code: the room and topic names you use in the SDK stay the same.",[15,2652,2653,2654,2658],{},"To set up tenant isolation for your agents, create a scope for each tenant and assign each tenant's agent actors to that scope. See the ",[922,2655,2657],{"href":2656},"/docs/scopes/multi-tenancy","Multi-Tenant Patterns"," guide for step-by-step implementation details.",[2660,2661,2662],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":164,"searchDepth":192,"depth":192,"links":2664},[2665,2671,2676,2681,2686,2691,2696],{"id":383,"depth":192,"text":384,"children":2666},[2667,2668,2669,2670],{"id":391,"depth":207,"text":392},{"id":408,"depth":207,"text":409},{"id":713,"depth":207,"text":714},{"id":929,"depth":207,"text":930},{"id":107,"depth":192,"text":964,"children":2672},[2673,2674,2675],{"id":974,"depth":207,"text":392},{"id":988,"depth":207,"text":989},{"id":1193,"depth":207,"text":930},{"id":1220,"depth":192,"text":1221,"children":2677},[2678,2679,2680],{"id":1227,"depth":207,"text":392},{"id":1241,"depth":207,"text":989},{"id":1640,"depth":207,"text":930},{"id":1674,"depth":192,"text":1675,"children":2682},[2683,2684,2685],{"id":1681,"depth":207,"text":392},{"id":1698,"depth":207,"text":989},{"id":1963,"depth":207,"text":930},{"id":1997,"depth":192,"text":1998,"children":2687},[2688,2689,2690],{"id":2012,"depth":207,"text":392},{"id":2026,"depth":207,"text":989},{"id":2286,"depth":207,"text":930},{"id":119,"depth":192,"text":2310,"children":2692},[2693,2694,2695],{"id":2316,"depth":207,"text":392},{"id":2330,"depth":207,"text":989},{"id":2583,"depth":207,"text":930},{"id":2616,"depth":192,"text":2617},"Deep dive on the six coordination patterns in @nolag/agents: Handoff, Inbox, Blackboard, Observe, Approve, and Tools.","md",{},"/docs/agents/patterns",{"title":5,"description":2697},"docs/agents/patterns","nhUgMq1B4HBaXbrnJsmhgC9fR-64qHVOXlO65Cnj5ag",1789869418562]