@directive-run/knowledge 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -15
- package/ai/ai-adapters.md +2 -0
- package/ai/ai-agents-streaming.md +182 -149
- package/ai/ai-budget-resilience.md +299 -132
- package/ai/ai-communication.md +215 -197
- package/ai/ai-debug-observability.md +253 -173
- package/ai/ai-guardrails-memory.md +185 -153
- package/ai/ai-mcp-rag.md +199 -199
- package/ai/ai-multi-agent.md +247 -153
- package/ai/ai-orchestrator.md +344 -114
- package/ai/ai-security.md +282 -180
- package/ai/ai-tasks.md +3 -1
- package/ai/ai-testing-evals.md +357 -256
- package/core/anti-patterns.md +9 -2
- package/core/constraints.md +6 -2
- package/core/core-patterns.md +2 -0
- package/core/error-boundaries.md +2 -0
- package/core/history.md +2 -0
- package/core/multi-module.md +8 -3
- package/core/naming.md +15 -6
- package/core/plugins.md +2 -0
- package/core/react-adapter.md +250 -174
- package/core/resolvers.md +2 -0
- package/core/schema-types.md +2 -0
- package/core/system-api.md +2 -0
- package/core/testing.md +251 -143
- package/examples/checkers.ts +15 -16
- package/examples/contact-form.ts +2 -2
- package/examples/counter-react.ts +1 -1
- package/examples/counter-svelte.ts +1 -1
- package/examples/counter-vue.ts +1 -1
- package/examples/counter.ts +1 -1
- package/examples/data-triggers.ts +4 -4
- package/examples/feature-flags.ts +2 -2
- package/examples/form-wizard.ts +2 -2
- package/examples/newsletter.ts +2 -2
- package/examples/server.ts +2 -2
- package/examples/shopping-cart.ts +1 -1
- package/examples/topic-guard.ts +1 -1
- package/package.json +3 -3
- package/sitemap.md +26 -3
- package/examples/debounce-constraints.ts +0 -95
- package/examples/multi-module.ts +0 -58
package/ai/ai-communication.md
CHANGED
|
@@ -1,281 +1,299 @@
|
|
|
1
|
-
# AI
|
|
1
|
+
# AI inter-agent communication
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Covers `@directive-run/ai` — `createMessageBus`, `createAgentNetwork`, request/response patterns, scratchpad, cross-agent fact reads.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`createMessageBus` for typed pub/sub, `createAgentNetwork` for capability-based discovery and request/response patterns, plus the scratchpad and cross-agent state access available via a multi-agent orchestrator. Import from `@directive-run/ai`.
|
|
6
|
+
|
|
7
|
+
## Decision tree
|
|
6
8
|
|
|
7
9
|
```
|
|
8
|
-
What
|
|
9
|
-
├──
|
|
10
|
-
├──
|
|
11
|
-
├──
|
|
12
|
-
├──
|
|
13
|
-
|
|
14
|
-
How do agents share state?
|
|
15
|
-
├── Read another agent's facts → orchestrator.system.facts.agentName.key
|
|
16
|
-
├── Cross-agent derivations → orchestrator.derive.agentName.derivation
|
|
17
|
-
├── Ephemeral key-value store → context.scratchpad
|
|
18
|
-
│
|
|
19
|
-
How do agents hand off work?
|
|
20
|
-
├── One-time transfer → handoff pattern (DELEGATION + await DELEGATION_RESULT)
|
|
21
|
-
├── Ongoing collaboration → agent network with capabilities
|
|
22
|
-
└── Conditional routing → reroute in supervisor pattern
|
|
10
|
+
What do you need?
|
|
11
|
+
├── Typed pub/sub between agents → createMessageBus(config?)
|
|
12
|
+
├── Capability lookup + request/response → createAgentNetwork({ bus, agents? })
|
|
13
|
+
├── Per-pattern ephemeral state → context.scratchpad inside tasks/agents
|
|
14
|
+
├── Reading another agent's facts → orchestrator.system.facts[agentId].x
|
|
15
|
+
└── Cross-agent derived state → orchestrator.system.derive[agentId].x
|
|
23
16
|
```
|
|
24
17
|
|
|
25
|
-
##
|
|
18
|
+
## `createMessageBus(config?)`
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
Fire-and-forget pub/sub keyed by `agentId`. `publish()` is synchronous — it returns the message id before delivery completes. Use `onDelivery` / `onDeliveryError` in config to observe delivery status.
|
|
28
21
|
|
|
29
22
|
```typescript
|
|
30
|
-
import { createMessageBus } from "@directive-run/ai";
|
|
23
|
+
import { createMessageBus, type TypedAgentMessage } from "@directive-run/ai";
|
|
24
|
+
|
|
25
|
+
const bus = createMessageBus({
|
|
26
|
+
maxHistory: 1000,
|
|
27
|
+
defaultTtlMs: 60 * 60 * 1000,
|
|
28
|
+
maxPendingPerAgent: 100,
|
|
29
|
+
onDelivery: (msg, recipients) => log("delivered", msg.id, recipients),
|
|
30
|
+
onDeliveryError: (msg, error) => log("delivery_failed", msg.id, error.message),
|
|
31
|
+
});
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// Subscribe — returns a Subscription object with an .unsubscribe() method
|
|
34
|
+
const sub = bus.subscribe("writer", (message: TypedAgentMessage) => {
|
|
35
|
+
console.log(`writer received: ${message.type}`);
|
|
36
|
+
});
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
// Later
|
|
39
|
+
sub.unsubscribe();
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
type: "INFORM",
|
|
41
|
+
// Publish — returns the message id (sync)
|
|
42
|
+
const id = bus.publish({
|
|
43
|
+
type: "DELEGATION",
|
|
41
44
|
from: "researcher",
|
|
42
45
|
to: "writer",
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
task: "Summarize the findings",
|
|
47
|
+
context: { sources: 12 },
|
|
45
48
|
});
|
|
46
49
|
|
|
47
|
-
//
|
|
48
|
-
bus.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
content: "System entering maintenance mode",
|
|
52
|
-
});
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Request-Response Pattern
|
|
50
|
+
// Inspect
|
|
51
|
+
bus.getHistory({ from: "researcher" }, 50);
|
|
52
|
+
bus.getMessage(id);
|
|
53
|
+
bus.getPending("writer"); // messages queued for an offline subscriber
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const response = await bus.request({
|
|
60
|
-
type: "REQUEST",
|
|
61
|
-
from: "writer",
|
|
62
|
-
to: "researcher",
|
|
63
|
-
action: "verify_claim",
|
|
64
|
-
payload: { claim: "Transformers were invented in 2017" },
|
|
65
|
-
timeout: 5000, // Throws after 5s if no response
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
console.log(response.content); // "Verified: correct"
|
|
69
|
-
console.log(response.metadata); // { confidence: 0.95, source: "..." }
|
|
55
|
+
bus.clear();
|
|
56
|
+
bus.destroy();
|
|
70
57
|
```
|
|
71
58
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
All 11 message types in the system:
|
|
75
|
-
|
|
76
|
-
| Type | Direction | Purpose |
|
|
77
|
-
|---|---|---|
|
|
78
|
-
| `REQUEST` | Agent-to-agent | Ask another agent to do something |
|
|
79
|
-
| `RESPONSE` | Agent-to-agent | Reply to a REQUEST |
|
|
80
|
-
| `DELEGATION` | Agent-to-agent | Hand off a task to another agent |
|
|
81
|
-
| `DELEGATION_RESULT` | Agent-to-agent | Return result of delegated work |
|
|
82
|
-
| `QUERY` | Agent-to-agent | Ask for information without side effects |
|
|
83
|
-
| `INFORM` | Agent-to-agent/all | Share information, no response expected |
|
|
84
|
-
| `SUBSCRIBE` | Agent-to-agent | Request ongoing updates on a topic |
|
|
85
|
-
| `UNSUBSCRIBE` | Agent-to-agent | Stop receiving updates |
|
|
86
|
-
| `UPDATE` | Agent-to-subscriber | Push update to a subscriber |
|
|
87
|
-
| `ACK` | Agent-to-agent | Acknowledge receipt |
|
|
88
|
-
| `NACK` | Agent-to-agent | Reject or refuse a message |
|
|
89
|
-
|
|
90
|
-
## Subscribing to Messages
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
// Subscribe to all messages for an agent
|
|
94
|
-
const unsubscribe = bus.subscribe("writer", (message) => {
|
|
95
|
-
switch (message.type) {
|
|
96
|
-
case "INFORM":
|
|
97
|
-
console.log(`Info from ${message.from}: ${message.content}`);
|
|
98
|
-
break;
|
|
99
|
-
case "REQUEST":
|
|
100
|
-
// Handle and respond
|
|
101
|
-
bus.publish({
|
|
102
|
-
type: "RESPONSE",
|
|
103
|
-
from: "writer",
|
|
104
|
-
to: message.from,
|
|
105
|
-
correlationId: message.id,
|
|
106
|
-
content: "Done",
|
|
107
|
-
});
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Clean up
|
|
113
|
-
unsubscribe();
|
|
114
|
-
```
|
|
59
|
+
There is no `bus.request(...)` method — request/response lives on the AgentNetwork (next section).
|
|
115
60
|
|
|
116
|
-
##
|
|
61
|
+
## `createAgentNetwork({ bus, agents? })`
|
|
117
62
|
|
|
118
|
-
|
|
63
|
+
Wraps a MessageBus with registry, capability lookup, and request/response patterns. **Capability lookup returns `AgentInfo[]`, not `string[]`.** Request/response shapes — `request`, `delegate`, `query`, `broadcast`, `listen`, `send` — are all on the network, NOT on the bus.
|
|
119
64
|
|
|
120
65
|
```typescript
|
|
121
|
-
import { createAgentNetwork } from "@directive-run/ai";
|
|
66
|
+
import { createAgentNetwork, type AgentInfo } from "@directive-run/ai";
|
|
122
67
|
|
|
123
68
|
const network = createAgentNetwork({
|
|
124
69
|
bus,
|
|
125
70
|
agents: {
|
|
126
|
-
researcher: {
|
|
127
|
-
|
|
128
|
-
},
|
|
129
|
-
writer: {
|
|
130
|
-
capabilities: ["draft", "edit", "summarize"],
|
|
131
|
-
},
|
|
132
|
-
analyst: {
|
|
133
|
-
capabilities: ["analyze", "chart", "report"],
|
|
134
|
-
},
|
|
71
|
+
researcher: { capabilities: ["search", "verify", "cite"] },
|
|
72
|
+
writer: { capabilities: ["draft", "edit", "summarize"] },
|
|
73
|
+
analyst: { capabilities: ["analyze", "chart", "report"] },
|
|
135
74
|
},
|
|
75
|
+
defaultTimeout: 30_000,
|
|
76
|
+
onAgentOnline: (id) => log(`${id} online`),
|
|
77
|
+
onAgentOffline: (id) => log(`${id} offline`),
|
|
136
78
|
});
|
|
137
79
|
|
|
138
|
-
// Find
|
|
139
|
-
const
|
|
140
|
-
|
|
80
|
+
// Find — returns AgentInfo[], not string[]
|
|
81
|
+
const verifiers: AgentInfo[] = network.findByCapability("verify");
|
|
82
|
+
verifiers.forEach((info) => console.log(info.id, info.capabilities));
|
|
141
83
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Route a request to the best agent for a capability
|
|
146
|
-
const result = await network.route("verify", {
|
|
84
|
+
// Request/response with timeout
|
|
85
|
+
const reply = await network.request("coordinator", "researcher", "verify-claim", {
|
|
147
86
|
claim: "GPT-4 has 1.8T parameters",
|
|
148
|
-
});
|
|
87
|
+
}, 10_000);
|
|
88
|
+
console.log(reply.payload);
|
|
89
|
+
|
|
90
|
+
// Delegate a task and await its result
|
|
91
|
+
const result = await network.delegate(
|
|
92
|
+
"coordinator",
|
|
93
|
+
"writer",
|
|
94
|
+
"Draft a 200-word summary",
|
|
95
|
+
{ source: reply.payload },
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Question / answer
|
|
99
|
+
const answer = await network.query("coordinator", "analyst", "What's the median latency?", { window: "24h" });
|
|
100
|
+
|
|
101
|
+
// Broadcast to all agents
|
|
102
|
+
network.broadcast("coordinator", { type: "INFORM", content: "Cache cleared" });
|
|
103
|
+
|
|
104
|
+
// Plain fire-and-forget through the network (returns message id)
|
|
105
|
+
network.send("coordinator", "writer", { type: "INFORM", content: "Starting batch" });
|
|
106
|
+
|
|
107
|
+
// Subscribe an agent — same as bus.subscribe but registered through the network
|
|
108
|
+
const sub = network.listen("writer", (msg) => console.log(msg.type));
|
|
109
|
+
sub.unsubscribe();
|
|
110
|
+
|
|
111
|
+
network.destroy();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
There is no `network.route(capability, payload)` — pick the agent yourself via `findByCapability(...)`, then call `request` / `delegate` against the chosen `agent.id`.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const candidates = network.findByCapability("verify");
|
|
118
|
+
if (candidates.length === 0) throw new Error("no verifier available");
|
|
119
|
+
const target = candidates[0];
|
|
120
|
+
|
|
121
|
+
const result = await network.request("coordinator", target.id, "verify", { claim });
|
|
149
122
|
```
|
|
150
123
|
|
|
151
|
-
## Cross-
|
|
124
|
+
## Cross-agent state via facts + derivations
|
|
152
125
|
|
|
153
|
-
|
|
126
|
+
Each agent in a multi-agent orchestrator becomes a namespaced module, so its facts and derivations are readable on `orchestrator.system`.
|
|
154
127
|
|
|
155
128
|
```typescript
|
|
156
129
|
const orchestrator = createMultiAgentOrchestrator({
|
|
157
|
-
agents: {
|
|
158
|
-
researcher: {
|
|
159
|
-
name: "researcher",
|
|
160
|
-
instructions: "...",
|
|
161
|
-
model: "claude-sonnet-4-5",
|
|
162
|
-
},
|
|
163
|
-
writer: {
|
|
164
|
-
name: "writer",
|
|
165
|
-
instructions: "...",
|
|
166
|
-
model: "claude-sonnet-4-5",
|
|
167
|
-
},
|
|
168
|
-
},
|
|
130
|
+
agents: { researcher, writer },
|
|
169
131
|
runner,
|
|
170
132
|
});
|
|
171
133
|
|
|
172
134
|
orchestrator.start();
|
|
173
135
|
|
|
174
|
-
// Read another agent's
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
// Cross-agent derivations react to fact changes
|
|
179
|
-
const isReady = orchestrator.system.derive.researcher.isComplete;
|
|
136
|
+
// Read another agent's namespaced state (read-only outside its own resolvers)
|
|
137
|
+
const status = orchestrator.system.facts.researcher.status;
|
|
138
|
+
const output = orchestrator.system.facts.writer.output;
|
|
139
|
+
const isReady = orchestrator.system.derive.researcher.isComplete;
|
|
180
140
|
```
|
|
181
141
|
|
|
182
|
-
|
|
142
|
+
For a deeper treatment of multi-module fact access + cross-module dependencies, see `multi-module.md`.
|
|
183
143
|
|
|
184
|
-
|
|
144
|
+
## Scratchpad — per-pattern ephemeral state
|
|
145
|
+
|
|
146
|
+
The scratchpad is a read-only context object shared across tasks/agents inside a single pattern execution. **You cannot mutate `context.scratchpad` directly** — pass updates back through the task's return value, or use `network.send` / `bus.publish` for messages that outlive the pattern.
|
|
185
147
|
|
|
186
148
|
```typescript
|
|
187
|
-
// In a task – write to scratchpad
|
|
188
149
|
tasks: {
|
|
189
150
|
gather: {
|
|
190
|
-
run: async (input, context) => {
|
|
191
|
-
|
|
192
|
-
context.scratchpad.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return input;
|
|
151
|
+
run: async (input, signal, context) => {
|
|
152
|
+
// context.scratchpad is Readonly — do NOT do context.scratchpad.x = …
|
|
153
|
+
const seed = context.scratchpad.seed;
|
|
154
|
+
return JSON.stringify({ seed, data: await fetchData(seed) });
|
|
196
155
|
},
|
|
197
156
|
},
|
|
198
157
|
format: {
|
|
199
|
-
run: async (input, context) => {
|
|
200
|
-
// Read
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
return JSON.stringify({ data, processedAt: ts });
|
|
158
|
+
run: async (input, signal, context) => {
|
|
159
|
+
// Read scratchpad written by the pattern config
|
|
160
|
+
const region = context.scratchpad.region;
|
|
161
|
+
const parsed = JSON.parse(input);
|
|
162
|
+
return JSON.stringify({ ...parsed, region });
|
|
205
163
|
},
|
|
206
164
|
},
|
|
207
165
|
},
|
|
208
166
|
```
|
|
209
167
|
|
|
210
|
-
|
|
168
|
+
For full task surface, see `ai-tasks.md`.
|
|
211
169
|
|
|
212
|
-
|
|
170
|
+
## Message bus + orchestrator wiring
|
|
171
|
+
|
|
172
|
+
The orchestrator does NOT accept a `bus:` option directly — wire the bus alongside the orchestrator and route messages explicitly.
|
|
213
173
|
|
|
214
174
|
```typescript
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
175
|
+
import { createMultiAgentOrchestrator } from "@directive-run/ai/multi-agent";
|
|
176
|
+
import { createMessageBus, createAgentNetwork } from "@directive-run/ai";
|
|
177
|
+
|
|
178
|
+
const bus = createMessageBus({ maxHistory: 1000 });
|
|
179
|
+
const network = createAgentNetwork({
|
|
180
|
+
bus,
|
|
181
|
+
agents: {
|
|
182
|
+
researcher: { capabilities: ["search", "verify"] },
|
|
183
|
+
writer: { capabilities: ["draft", "edit"] },
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const orchestrator = createMultiAgentOrchestrator({
|
|
188
|
+
agents: { researcher, writer },
|
|
189
|
+
runner,
|
|
190
|
+
hooks: {
|
|
191
|
+
onAgentStart: (e) => {
|
|
192
|
+
bus.publish({
|
|
193
|
+
type: "AGENT_START",
|
|
194
|
+
from: "orchestrator",
|
|
195
|
+
to: e.agentName,
|
|
196
|
+
input: e.input,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
},
|
|
222
200
|
});
|
|
223
201
|
|
|
224
|
-
|
|
202
|
+
orchestrator.start();
|
|
203
|
+
|
|
204
|
+
// External systems can publish to the bus directly
|
|
225
205
|
bus.publish({
|
|
226
|
-
type: "
|
|
227
|
-
from: "
|
|
228
|
-
to: "
|
|
229
|
-
|
|
230
|
-
content: "Research findings: ...",
|
|
231
|
-
metadata: { sourcesFound: 12 },
|
|
206
|
+
type: "INFORM",
|
|
207
|
+
from: "external-pipeline",
|
|
208
|
+
to: "researcher",
|
|
209
|
+
content: "New corpus available",
|
|
232
210
|
});
|
|
233
211
|
```
|
|
234
212
|
|
|
235
|
-
|
|
213
|
+
## Anti-patterns
|
|
236
214
|
|
|
237
|
-
|
|
215
|
+
### `bus.request(...)`
|
|
238
216
|
|
|
239
217
|
```typescript
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
});
|
|
218
|
+
// WRONG — there is no request method on MessageBus
|
|
219
|
+
const reply = await bus.request({ type: "REQUEST", from: "a", to: "b", action: "verify", timeout: 10_000 });
|
|
220
|
+
|
|
221
|
+
// CORRECT — request/response is on AgentNetwork
|
|
222
|
+
const reply = await network.request("a", "b", "verify", { /* payload */ }, 10_000);
|
|
245
223
|
```
|
|
246
224
|
|
|
247
|
-
|
|
225
|
+
### `const unsub = bus.subscribe(...); unsub();`
|
|
248
226
|
|
|
249
227
|
```typescript
|
|
250
|
-
|
|
228
|
+
// WRONG — subscribe returns a Subscription object, not the unsubscribe function
|
|
229
|
+
const unsub = bus.subscribe("writer", handler);
|
|
230
|
+
unsub();
|
|
251
231
|
|
|
252
|
-
|
|
232
|
+
// CORRECT — call .unsubscribe() on the returned Subscription
|
|
233
|
+
const sub = bus.subscribe("writer", handler);
|
|
234
|
+
sub.unsubscribe();
|
|
235
|
+
```
|
|
253
236
|
|
|
254
|
-
|
|
255
|
-
|
|
237
|
+
### `network.findByCapability(...)` returning strings
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// WRONG — assumes the return is string[]
|
|
241
|
+
const writers = network.findByCapability("draft"); // ["writer"]?
|
|
242
|
+
network.send("coordinator", writers[0], message); // passes a string where AgentInfo was expected? No — but we lost capabilities/metadata
|
|
243
|
+
|
|
244
|
+
// CORRECT — it returns AgentInfo[]; use .id when you need the string
|
|
245
|
+
const candidates = network.findByCapability("draft");
|
|
246
|
+
network.send("coordinator", candidates[0].id, message);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### `network.route(capability, payload)`
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// WRONG — no such method
|
|
253
|
+
await network.route("verify", { claim });
|
|
254
|
+
|
|
255
|
+
// CORRECT — pick an agent via findByCapability, then request/delegate
|
|
256
|
+
const verifiers = network.findByCapability("verify");
|
|
257
|
+
if (verifiers.length === 0) throw new Error("no verifier");
|
|
258
|
+
const result = await network.request("coordinator", verifiers[0].id, "verify", { claim });
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `createMultiAgentOrchestrator({ bus })`
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// WRONG — bus is not an option on MultiAgentOrchestratorOptions
|
|
265
|
+
createMultiAgentOrchestrator({ agents, runner, bus })
|
|
266
|
+
|
|
267
|
+
// CORRECT — wire the bus separately and publish through hooks/handlers
|
|
268
|
+
createMultiAgentOrchestrator({
|
|
269
|
+
agents,
|
|
256
270
|
runner,
|
|
257
|
-
|
|
271
|
+
hooks: {
|
|
272
|
+
onAgentComplete: (e) => bus.publish({ type: "AGENT_COMPLETE", from: "orchestrator", to: e.agentName, output: e.output }),
|
|
273
|
+
},
|
|
258
274
|
});
|
|
275
|
+
```
|
|
259
276
|
|
|
260
|
-
|
|
277
|
+
### Mutating `context.scratchpad`
|
|
261
278
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
});
|
|
279
|
+
```typescript
|
|
280
|
+
// WRONG — context.scratchpad is Readonly
|
|
281
|
+
context.scratchpad.researchData = data;
|
|
282
|
+
context.scratchpad.timestamp = Date.now();
|
|
283
|
+
|
|
284
|
+
// CORRECT — return new state from the task; or use bus.publish to broadcast
|
|
285
|
+
return JSON.stringify({ ...JSON.parse(input), researchData: data, timestamp: Date.now() });
|
|
269
286
|
```
|
|
270
287
|
|
|
271
|
-
## Quick
|
|
288
|
+
## Quick reference
|
|
272
289
|
|
|
273
|
-
| API | Purpose |
|
|
290
|
+
| API | Purpose | Notes |
|
|
274
291
|
|---|---|---|
|
|
275
|
-
| `createMessageBus()` |
|
|
276
|
-
| `
|
|
277
|
-
| `bus
|
|
278
|
-
| `
|
|
279
|
-
| `
|
|
280
|
-
| `network.
|
|
281
|
-
| `
|
|
292
|
+
| `createMessageBus(config?)` | Pub/sub primitive | `publish` / `subscribe` / `getHistory` / `getMessage` / `getPending` / `clear` / `destroy` |
|
|
293
|
+
| `bus.subscribe(id, handler, filter?)` | Subscribe | returns a `Subscription` — call `sub.unsubscribe()` |
|
|
294
|
+
| `createAgentNetwork({ bus, agents? })` | Capability-aware coordination | `request` / `delegate` / `query` / `broadcast` / `send` / `listen` / `findByCapability` |
|
|
295
|
+
| `network.findByCapability(cap)` | Discovery | returns `AgentInfo[]` (NOT `string[]`) |
|
|
296
|
+
| `network.request(from, to, action, payload, timeout?)` | Request/response | `Promise<ResponseMessage>` |
|
|
297
|
+
| `network.delegate(from, to, task, context)` | Delegated task with result | `Promise<DelegationResultMessage>` |
|
|
298
|
+
| `orchestrator.system.facts[agentId].x` | Cross-agent fact read | each agent is a namespaced module |
|
|
299
|
+
| `context.scratchpad` | Per-pattern ephemeral state | Readonly inside tasks/agents |
|