toolnexus 0.9.4 → 0.10.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 +31 -1
- package/dist/agents/agent.d.ts +60 -0
- package/dist/agents/agent.js +99 -0
- package/dist/agents/compaction.d.ts +77 -0
- package/dist/agents/compaction.js +96 -0
- package/dist/agents/home.d.ts +90 -0
- package/dist/agents/home.js +203 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.js +10 -0
- package/dist/agents/runtime.d.ts +357 -0
- package/dist/agents/runtime.js +733 -0
- package/dist/client.d.ts +28 -4
- package/dist/client.js +26 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/toolkit.d.ts +7 -0
- package/dist/toolkit.js +13 -3
- package/package.json +2 -2
- package/src/agents/agent.ts +129 -0
- package/src/agents/compaction.ts +148 -0
- package/src/agents/home.ts +250 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/runtime.ts +890 -0
- package/src/client.ts +50 -14
- package/src/index.ts +1 -0
- package/src/toolkit.ts +19 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-agents & agent runtime (SPEC.md §7D), exported as the `agents` namespace:
|
|
3
|
+
*
|
|
4
|
+
* import { agents } from "toolnexus"
|
|
5
|
+
* const explore = agents.agent("explore", { does: "read-only research" })
|
|
6
|
+
*/
|
|
7
|
+
export * from "./runtime.js";
|
|
8
|
+
export * from "./agent.js";
|
|
9
|
+
export * from "./home.js";
|
|
10
|
+
export * from "./compaction.js";
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent runtime substrate — SPEC.md §7D.
|
|
3
|
+
*
|
|
4
|
+
* A Handle is a live agent: state machine (`idle → running → idle|suspended|closed`,
|
|
5
|
+
* `suspended → running` only via the Answer to its pending Request), an inbox held as
|
|
6
|
+
* agent state, hierarchical budgets, and a deterministic parent-scoped id
|
|
7
|
+
* (`root/coordinator.1/explore.2`). The runtime exposes exactly six host verbs
|
|
8
|
+
* (spawn/post/wake/wait/interrupt/close) plus read-only list/inspect views, and owns
|
|
9
|
+
* the cross-cutting infrastructure: ONE ConversationStore for all handles
|
|
10
|
+
* (conversation id = handle id, so transcripts genuinely survive turns), an
|
|
11
|
+
* injectable clock (fixtures run virtual), and the handle table.
|
|
12
|
+
*
|
|
13
|
+
* Everything rides shipped machinery: the §8 client loop, §10 pending/waitFor, and
|
|
14
|
+
* the ConversationStore. SPEC pins transitions, never scheduling — conformance is
|
|
15
|
+
* identical per-handle transition traces against `examples/subagent-*` fixtures.
|
|
16
|
+
*/
|
|
17
|
+
import { type ClientStyle, type ConversationStore } from "../client.js";
|
|
18
|
+
import type { Answer, Request, Tool } from "../types.js";
|
|
19
|
+
/** Injectable time source. All runtime timers, timeouts, and deadlines use it. */
|
|
20
|
+
export interface Clock {
|
|
21
|
+
/** Current time in ms (epoch or virtual). */
|
|
22
|
+
now(): number;
|
|
23
|
+
/** Schedule `fn` after `ms`; returns a cancel function. */
|
|
24
|
+
setTimeout(fn: () => void, ms: number): () => void;
|
|
25
|
+
}
|
|
26
|
+
/** The real clock (default). Timers are unref'd so they never hold the process open. */
|
|
27
|
+
export declare const systemClock: Clock;
|
|
28
|
+
/** §7D hierarchical budget. Carved at spawn (`min(own, parent remaining)`) and
|
|
29
|
+
* live-enforced against the whole ancestor chain before each turn and spawn.
|
|
30
|
+
* Monetary budgets are excluded (vendor data; convert in a host `onBudget`). */
|
|
31
|
+
export interface Budget {
|
|
32
|
+
/** Turn cap for this handle (LLM round trips per run; lifetime cap across wakes). */
|
|
33
|
+
maxTurns?: number;
|
|
34
|
+
/** Token pool, shared downward with descendants. */
|
|
35
|
+
maxTokens?: number;
|
|
36
|
+
/** Tool-call pool, shared downward with descendants. */
|
|
37
|
+
maxToolCalls?: number;
|
|
38
|
+
/** Wall-clock deadline in ms from spawn (clock-based; min'd with the parent's). */
|
|
39
|
+
maxWallMs?: number;
|
|
40
|
+
/** Direct-children cap, checked at spawn. */
|
|
41
|
+
maxChildren?: number;
|
|
42
|
+
/** Concurrently RUNNING children cap (admission gate; queued wakes fire FIFO). */
|
|
43
|
+
maxConcurrent?: number;
|
|
44
|
+
/** Tree-depth cap, checked at spawn. */
|
|
45
|
+
maxDepth?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Why a handle was closed (passed to `onClose`). */
|
|
48
|
+
export type CloseReason = "closed" | "interrupted" | "budget" | "error";
|
|
49
|
+
/** An agent definition — the registry entry the runtime spawns handles from. */
|
|
50
|
+
export interface AgentDef {
|
|
51
|
+
name: string;
|
|
52
|
+
/** Routing description: what the delegating model sees for this agent. */
|
|
53
|
+
does: string;
|
|
54
|
+
/** Identity / system prompt (the "soul"). */
|
|
55
|
+
soul?: string;
|
|
56
|
+
/** Model id; "inherit" (default) resolves to the runtime's `llm.model`. */
|
|
57
|
+
model?: string;
|
|
58
|
+
/** This agent's scoped tool set (the toolkit view IS the security model). */
|
|
59
|
+
tools?: Tool[];
|
|
60
|
+
/** `task`-tool targets. Absent/empty ⇒ this agent gets NO `task` tool
|
|
61
|
+
* (delegation, like recursion, is opt-in — never default). */
|
|
62
|
+
team?: string[];
|
|
63
|
+
budget?: Budget;
|
|
64
|
+
/** §10 interpreter authority for suspensions escalating through this agent.
|
|
65
|
+
* Absent ⇒ pendings relay upward (durable at the root). */
|
|
66
|
+
waitFor?: (request: Request) => Promise<Answer>;
|
|
67
|
+
/** Runs once, before the first turn — the session-start injection point. */
|
|
68
|
+
onSpawn?: (h: Handle) => void | Promise<void>;
|
|
69
|
+
/** Runs before the final checkpoint on close. */
|
|
70
|
+
onClose?: (h: Handle, reason: CloseReason) => void | Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/** An unsolicited inbox item. Delivered ONLY as part of a fresh turn's coalesced
|
|
73
|
+
* drain, with provenance rendered per item (non-ancestor senders are untrusted). */
|
|
74
|
+
export interface InboxItem {
|
|
75
|
+
/** Sender handle path, or "external" / "clock". */
|
|
76
|
+
from: string;
|
|
77
|
+
/** Delivery channel: "peer" | "timer" | "external" | ... */
|
|
78
|
+
channel: string;
|
|
79
|
+
text: string;
|
|
80
|
+
}
|
|
81
|
+
export type HandleState = "idle" | "running" | "suspended" | "closed";
|
|
82
|
+
export type TaskStatus = "done" | "pending" | "incomplete" | "interrupted" | "closed" | "timeout" | "error";
|
|
83
|
+
/** The result of one completed (or halted) turn, as seen by waiters and `task`. */
|
|
84
|
+
export interface TaskResult {
|
|
85
|
+
text: string;
|
|
86
|
+
isError: boolean;
|
|
87
|
+
status: TaskStatus;
|
|
88
|
+
/** Present iff status === "pending". `data.path` carries the suspended handle's
|
|
89
|
+
* id path (§10 agent-escalation addendum) — never a field grafted on Request. */
|
|
90
|
+
pending?: Request;
|
|
91
|
+
turns: number;
|
|
92
|
+
totalTokens: number;
|
|
93
|
+
}
|
|
94
|
+
/** Uniform verb failure — returned, never thrown (only the root throws to the host). */
|
|
95
|
+
export interface VerbError {
|
|
96
|
+
error: string;
|
|
97
|
+
}
|
|
98
|
+
/** Type guard for the `spawn` return union. */
|
|
99
|
+
export declare function isVerbError(x: unknown): x is VerbError;
|
|
100
|
+
/** Read-only snapshot row from `list()` / `inspect()`. */
|
|
101
|
+
export interface HandleView {
|
|
102
|
+
id: string;
|
|
103
|
+
state: HandleState;
|
|
104
|
+
/** Total tokens attributed to this handle (own + rolled-up descendants). */
|
|
105
|
+
tokens: number;
|
|
106
|
+
/** Buffered inbox items. */
|
|
107
|
+
inbox: number;
|
|
108
|
+
}
|
|
109
|
+
interface Pools {
|
|
110
|
+
tokens: number;
|
|
111
|
+
toolCalls: number;
|
|
112
|
+
children: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The suspension checkpoint. When a Run halts pending, the handle's stored
|
|
116
|
+
* transcript is REWOUND to the pre-turn snapshot and the turn's full input is
|
|
117
|
+
* recorded here; resume replays the whole turn from this point (idempotency for
|
|
118
|
+
* delegated work comes from `task`-key reattachment, not transcript inspection).
|
|
119
|
+
*/
|
|
120
|
+
interface Checkpoint {
|
|
121
|
+
/** The exact turn input (prompt + rendered inbox drain) to replay. */
|
|
122
|
+
input: string;
|
|
123
|
+
}
|
|
124
|
+
interface QueuedWake {
|
|
125
|
+
h: Handle;
|
|
126
|
+
prompt?: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* A live agent. Public surface is the observable state (§7D pins transitions, not
|
|
130
|
+
* scheduling); fields below the "runtime internals" marker are owned by AgentRuntime.
|
|
131
|
+
*/
|
|
132
|
+
export declare class Handle {
|
|
133
|
+
readonly id: string;
|
|
134
|
+
readonly def: AgentDef;
|
|
135
|
+
readonly parent: Handle | null;
|
|
136
|
+
readonly depth: number;
|
|
137
|
+
readonly children: Handle[];
|
|
138
|
+
/** Inbox as agent state — bounded, transactional drain, checkpointable. */
|
|
139
|
+
readonly inbox: InboxItem[];
|
|
140
|
+
state: HandleState;
|
|
141
|
+
/** Remaining budget pools (tokens/toolCalls carved at spawn, drained by roll-up). */
|
|
142
|
+
readonly pool: Pools;
|
|
143
|
+
/** Effective per-handle limits after defaults. */
|
|
144
|
+
readonly eff: {
|
|
145
|
+
maxTurns: number;
|
|
146
|
+
maxConcurrent: number;
|
|
147
|
+
maxDepth: number;
|
|
148
|
+
};
|
|
149
|
+
/** Wall-clock deadline (clock ms), when a `maxWallMs` applies on this chain. */
|
|
150
|
+
readonly deadline?: number;
|
|
151
|
+
/** Total tokens attributed to this handle (own turns + descendant roll-up). */
|
|
152
|
+
usageTotal: number;
|
|
153
|
+
/** Total tool calls attributed to this handle (own + descendant roll-up). */
|
|
154
|
+
toolCallsTotal: number;
|
|
155
|
+
/** LLM round trips run by this handle itself (never reset — resume grows it). */
|
|
156
|
+
turnsTotal: number;
|
|
157
|
+
/** The unresolved Request while `suspended` (already stamped with `data.path`). */
|
|
158
|
+
pendingRequest?: Request;
|
|
159
|
+
/** Last settled turn result — what `wait()` answers with on a settled handle. */
|
|
160
|
+
lastResult?: TaskResult;
|
|
161
|
+
/** In-flight turn abort seam (the JS cancellation contract, §7D table). */
|
|
162
|
+
abort: AbortController | null;
|
|
163
|
+
/** The halted tool call recorded at suspension — resume retries it with the Answer. */
|
|
164
|
+
checkpoint?: Checkpoint;
|
|
165
|
+
/** Items consumed by the in-flight turn; restored to the inbox front on abort. */
|
|
166
|
+
drained: InboxItem[];
|
|
167
|
+
/** Waiters registered by `wait()`. */
|
|
168
|
+
waiters: Array<(r: TaskResult) => void>;
|
|
169
|
+
/** FIFO queue of children's wakes deferred by this handle's `maxConcurrent`. */
|
|
170
|
+
wakeQueue: QueuedWake[];
|
|
171
|
+
/** Set when spawned by the `task` tool — the reattachment key (agent+prompt). */
|
|
172
|
+
taskKey?: string;
|
|
173
|
+
/** Currently RUNNING children (the concurrency-gate counter). */
|
|
174
|
+
runningChildren: number;
|
|
175
|
+
/** Child id sequence (deterministic ids). */
|
|
176
|
+
seq: number;
|
|
177
|
+
/** onSpawn completion — awaited before the first turn. */
|
|
178
|
+
ready?: Promise<void>;
|
|
179
|
+
constructor(id: string, def: AgentDef, parent: Handle | null, now: number);
|
|
180
|
+
}
|
|
181
|
+
export interface RuntimeOptions {
|
|
182
|
+
/** Agent definitions by name. The `task` tool resolves targets here. */
|
|
183
|
+
registry: Record<string, AgentDef>;
|
|
184
|
+
/** LLM endpoint. Omit for hermetic runs against an injected `fetch` mock. */
|
|
185
|
+
llm?: {
|
|
186
|
+
baseUrl?: string;
|
|
187
|
+
style?: ClientStyle;
|
|
188
|
+
apiKey?: string;
|
|
189
|
+
model?: string;
|
|
190
|
+
};
|
|
191
|
+
/** HTTP transport for LLM requests (§8 Gap 2) — the hermetic-test seam. */
|
|
192
|
+
fetch?: typeof fetch;
|
|
193
|
+
/** Time source for every timer/timeout/deadline. Default: the system clock. */
|
|
194
|
+
clock?: Clock;
|
|
195
|
+
/** The ONE ConversationStore for all handles (conversation id = handle id).
|
|
196
|
+
* Default: in-memory for the runtime's lifetime. */
|
|
197
|
+
store?: ConversationStore;
|
|
198
|
+
/** Inbox capacity per handle (gate 1; full ⇒ loud synchronous reject). Default 8. */
|
|
199
|
+
inboxCap?: number;
|
|
200
|
+
/** Global turn gate (gate 3): concurrent LLM HTTP calls across the whole tree.
|
|
201
|
+
* Wraps ONLY the HTTP call, never a Run, and releases on acquirer death. Default 8. */
|
|
202
|
+
maxConcurrentTurns?: number;
|
|
203
|
+
/** Graceful-close bound for a running turn before escalating to abort. Default 200. */
|
|
204
|
+
shutdownMs?: number;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The §7D agent runtime: six host verbs over a tree of Handles, three loud
|
|
208
|
+
* backpressure gates, hierarchical budgets, §10 escalation/durable resume, and a
|
|
209
|
+
* transition trace (`trace`) — the conformance artifact fixtures assert against.
|
|
210
|
+
*/
|
|
211
|
+
export declare class AgentRuntime {
|
|
212
|
+
/** Transition trace — the §0 conformance artifact. */
|
|
213
|
+
readonly trace: string[];
|
|
214
|
+
readonly root: Handle;
|
|
215
|
+
/** The runtime-wide conversation store (one for ALL handles; id = handle id). */
|
|
216
|
+
readonly store: ConversationStore;
|
|
217
|
+
readonly clock: Clock;
|
|
218
|
+
/** LLM HTTP calls currently in flight (observability for the turn gate). */
|
|
219
|
+
concurrentTurns: number;
|
|
220
|
+
/** High-water mark of `concurrentTurns` (fixtures assert gate behavior with it). */
|
|
221
|
+
maxObservedConcurrentTurns: number;
|
|
222
|
+
private readonly opts;
|
|
223
|
+
private readonly registry;
|
|
224
|
+
private readonly inboxCap;
|
|
225
|
+
private readonly shutdownMs;
|
|
226
|
+
private turnSlots;
|
|
227
|
+
private readonly turnQueue;
|
|
228
|
+
constructor(opts: RuntimeOptions);
|
|
229
|
+
private t;
|
|
230
|
+
/**
|
|
231
|
+
* Create a child handle under `parent` with a deterministic parent-scoped id.
|
|
232
|
+
* Caps (`maxDepth`/`maxChildren`) and the live budget walk are checked here; the
|
|
233
|
+
* handle is returned to the spawner ALONE (handles are capabilities: post/wake
|
|
234
|
+
* what you hold, wait only on handles you spawned).
|
|
235
|
+
*/
|
|
236
|
+
spawn(parent: Handle, defName: string, budget?: Budget): Handle | VerbError;
|
|
237
|
+
/** Append to the inbox. NO state transition. Full or closed ⇒ synchronous reject. */
|
|
238
|
+
post(h: Handle, item: InboxItem): {
|
|
239
|
+
ok: true;
|
|
240
|
+
} | ({
|
|
241
|
+
ok: false;
|
|
242
|
+
} & VerbError);
|
|
243
|
+
/**
|
|
244
|
+
* `idle → running`. Admission (budget walk + concurrency slot + state flip +
|
|
245
|
+
* transactional drain) is atomic with the verb. Over the parent's `maxConcurrent`
|
|
246
|
+
* the wake queues FIFO and a completing sibling transfers its slot. Waking a
|
|
247
|
+
* suspended handle is a no-op (items buffer; only the Answer wakes it).
|
|
248
|
+
*/
|
|
249
|
+
wake(h: Handle, prompt?: string): {
|
|
250
|
+
ok: true;
|
|
251
|
+
} | ({
|
|
252
|
+
ok: false;
|
|
253
|
+
} & VerbError);
|
|
254
|
+
/**
|
|
255
|
+
* Resolve with the next completed result — or immediately with the last one when
|
|
256
|
+
* the handle is already settled (idle with a recorded result, suspended with its
|
|
257
|
+
* pending, or closed). Registration order is unobservable. An expired timeout
|
|
258
|
+
* yields an explicit timeout error while the child keeps running. Waiting is
|
|
259
|
+
* reserved to the spawner: pass `by` to enforce the capability rule.
|
|
260
|
+
*/
|
|
261
|
+
wait(h: Handle, opts?: {
|
|
262
|
+
timeoutMs?: number;
|
|
263
|
+
by?: Handle;
|
|
264
|
+
}): Promise<TaskResult>;
|
|
265
|
+
/**
|
|
266
|
+
* Abort the in-flight Run (JS seam: `AbortSignal`) → `idle`, with the drained
|
|
267
|
+
* inbox items restored (drain is transactional). On a suspended handle: cancel
|
|
268
|
+
* the pending Request → `idle` (the operator escape hatch). Waiters receive a
|
|
269
|
+
* uniform interrupted error result — never an exception.
|
|
270
|
+
*/
|
|
271
|
+
interrupt(h: Handle): void;
|
|
272
|
+
/**
|
|
273
|
+
* Graceful shutdown: stop accepting, close children leaf-first, let a running
|
|
274
|
+
* turn finish bounded by `shutdownMs` (then escalate to abort), run `onClose`,
|
|
275
|
+
* notify waiters, keep the final state queryable (close ≠ loss). Stop-all =
|
|
276
|
+
* `close(root)`. Runs from outside the tree (downward traversal).
|
|
277
|
+
*/
|
|
278
|
+
close(h: Handle, opts?: {
|
|
279
|
+
force?: boolean;
|
|
280
|
+
reason?: CloseReason;
|
|
281
|
+
}): Promise<void>;
|
|
282
|
+
/** Read-only snapshot of every handle (tree order, root excluded). */
|
|
283
|
+
list(h?: Handle, acc?: HandleView[]): HandleView[];
|
|
284
|
+
/** Read-only detail view of one handle. */
|
|
285
|
+
inspect(h: Handle): HandleView & {
|
|
286
|
+
turns: number;
|
|
287
|
+
poolTokens: number;
|
|
288
|
+
pending?: Request;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Route an Answer to the DEEPEST suspended handle, resume it from its checkpoint
|
|
292
|
+
* (retry-with-answer of the halted tool; turns/usage grow, never reset), then
|
|
293
|
+
* cascade upward: each suspended parent re-runs, and its re-invoked `task`
|
|
294
|
+
* REATTACHES to the existing child by task key — never spawns a duplicate.
|
|
295
|
+
* The root alone may throw to the host.
|
|
296
|
+
*/
|
|
297
|
+
resume(answer: Answer): Promise<void>;
|
|
298
|
+
private deepestSuspended;
|
|
299
|
+
/**
|
|
300
|
+
* Resume a suspended handle by REPLAYING its halted turn from the checkpoint:
|
|
301
|
+
* the stored transcript was rewound to the pre-turn snapshot when the Run halted,
|
|
302
|
+
* so the whole turn re-runs with the same input, this time with a one-shot §10
|
|
303
|
+
* interpreter that returns the Answer (when this level holds one). A replayed
|
|
304
|
+
* `task` call REATTACHES to the existing child by task key — reattachment, not
|
|
305
|
+
* transcript inspection, is the idempotency mechanism for delegated work.
|
|
306
|
+
*/
|
|
307
|
+
private resumeSuspended;
|
|
308
|
+
/** Walk the LIVE ancestor chain; name the first exhausted pool, if any. */
|
|
309
|
+
private poolLimit;
|
|
310
|
+
/** Lifetime turn cap on the handle itself (turns never reset across resumes). */
|
|
311
|
+
private turnCap;
|
|
312
|
+
/** A limit stop is LOUD: settle an incomplete result (never silent, never a throw). */
|
|
313
|
+
private settleRefused;
|
|
314
|
+
/** Usage roll-up IS the budget ledger: every ancestor's pool drains live. */
|
|
315
|
+
private rollUp;
|
|
316
|
+
/** Atomic admission: slot take + state flip (+ trace). Sync with the calling verb. */
|
|
317
|
+
private admit;
|
|
318
|
+
private isQueued;
|
|
319
|
+
private unqueue;
|
|
320
|
+
/** Transactional drain: one wake consumes the WHOLE inbox as one coalesced block.
|
|
321
|
+
* Timer ticks dedupe to a single counted entry; every item renders with provenance;
|
|
322
|
+
* non-ancestor/external senders render inside an explicitly untrusted wrapper. */
|
|
323
|
+
private drainInbox;
|
|
324
|
+
private isAncestorSender;
|
|
325
|
+
private acquireTurnSlot;
|
|
326
|
+
private makeRelease;
|
|
327
|
+
private gatedFetch;
|
|
328
|
+
private toolkitFor;
|
|
329
|
+
/** Stamp this level's handle path onto the Request at `data.path` (§10 addendum). */
|
|
330
|
+
private stampPath;
|
|
331
|
+
private pendingResult;
|
|
332
|
+
/** The escalation seam: this handle's §10 interpreter authority, with the
|
|
333
|
+
* escalation traced (nearest interpreter wins; the Request's `data.path` names
|
|
334
|
+
* the suspended subtree that is asking). */
|
|
335
|
+
private wrapWaitFor;
|
|
336
|
+
/**
|
|
337
|
+
* One turn. The caller has already admitted the handle (state = running); the
|
|
338
|
+
* synchronous prefix (input assembly + transactional drain) is therefore atomic
|
|
339
|
+
* with admission. Failures cross the handle boundary as results, never throws.
|
|
340
|
+
* When the Run halts pending, the stored transcript is REWOUND to the pre-turn
|
|
341
|
+
* snapshot and the input is checkpointed — resume replays the whole turn.
|
|
342
|
+
*/
|
|
343
|
+
private executeTurn;
|
|
344
|
+
/** Concurrency gate (gate 2): free this Run's slot and transfer it to queued
|
|
345
|
+
* sibling wakes, FIFO, re-checking budgets at dequeue time. */
|
|
346
|
+
private releaseChildSlot;
|
|
347
|
+
/**
|
|
348
|
+
* The one model-visible delegation tool. Advertises ONLY the caller's team
|
|
349
|
+
* (sorted by name, composed from each agent's `does`); out-of-team targets are
|
|
350
|
+
* refused with the team listed. A re-invoked call REATTACHES to the existing
|
|
351
|
+
* child by task key (agent+prompt) — settled ⇒ its recorded result; suspended ⇒
|
|
352
|
+
* its pending (or an inline resume when the retry carries the Answer); running ⇒
|
|
353
|
+
* await — and never spawns a duplicate.
|
|
354
|
+
*/
|
|
355
|
+
private taskTool;
|
|
356
|
+
}
|
|
357
|
+
export {};
|