@namzu/sdk 6.0.0 → 6.2.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +254 -0
  2. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  3. package/dist/agents/SupervisorAgent.js +18 -0
  4. package/dist/agents/SupervisorAgent.js.map +1 -1
  5. package/dist/agents/__tests__/supervisor-coordinator-registration.test.js +31 -0
  6. package/dist/agents/__tests__/supervisor-coordinator-registration.test.js.map +1 -1
  7. package/dist/gateway/__tests__/completion-inbox.test.d.ts +2 -0
  8. package/dist/gateway/__tests__/completion-inbox.test.d.ts.map +1 -0
  9. package/dist/gateway/__tests__/completion-inbox.test.js +275 -0
  10. package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -0
  11. package/dist/gateway/completion-inbox.d.ts +113 -0
  12. package/dist/gateway/completion-inbox.d.ts.map +1 -0
  13. package/dist/gateway/completion-inbox.js +225 -0
  14. package/dist/gateway/completion-inbox.js.map +1 -0
  15. package/dist/public-runtime.d.ts +1 -0
  16. package/dist/public-runtime.d.ts.map +1 -1
  17. package/dist/public-runtime.js +4 -0
  18. package/dist/public-runtime.js.map +1 -1
  19. package/dist/registry/tool/__tests__/dialect.test.js +8 -5
  20. package/dist/registry/tool/__tests__/dialect.test.js.map +1 -1
  21. package/dist/registry/tool/dialect.js +11 -4
  22. package/dist/registry/tool/dialect.js.map +1 -1
  23. package/dist/registry/tool/execute.d.ts.map +1 -1
  24. package/dist/registry/tool/execute.js +34 -0
  25. package/dist/registry/tool/execute.js.map +1 -1
  26. package/dist/runtime/query/__tests__/completion-notification.test.d.ts +2 -0
  27. package/dist/runtime/query/__tests__/completion-notification.test.d.ts.map +1 -0
  28. package/dist/runtime/query/__tests__/completion-notification.test.js +168 -0
  29. package/dist/runtime/query/__tests__/completion-notification.test.js.map +1 -0
  30. package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.d.ts +2 -0
  31. package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.d.ts.map +1 -0
  32. package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.js +122 -0
  33. package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.js.map +1 -0
  34. package/dist/runtime/query/__tests__/step-allow-list.test.d.ts +2 -0
  35. package/dist/runtime/query/__tests__/step-allow-list.test.d.ts.map +1 -0
  36. package/dist/runtime/query/__tests__/step-allow-list.test.js +171 -0
  37. package/dist/runtime/query/__tests__/step-allow-list.test.js.map +1 -0
  38. package/dist/runtime/query/executor.d.ts +14 -0
  39. package/dist/runtime/query/executor.d.ts.map +1 -1
  40. package/dist/runtime/query/executor.js +20 -1
  41. package/dist/runtime/query/executor.js.map +1 -1
  42. package/dist/runtime/query/index.d.ts +9 -0
  43. package/dist/runtime/query/index.d.ts.map +1 -1
  44. package/dist/runtime/query/index.js +1 -0
  45. package/dist/runtime/query/index.js.map +1 -1
  46. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  47. package/dist/runtime/query/iteration/index.js +74 -1
  48. package/dist/runtime/query/iteration/index.js.map +1 -1
  49. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  50. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  51. package/dist/runtime/query/iteration/phases/context.js +43 -13
  52. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  53. package/dist/tools/builtins/__tests__/bash.proc-test.d.ts +2 -0
  54. package/dist/tools/builtins/__tests__/bash.proc-test.d.ts.map +1 -0
  55. package/dist/tools/builtins/__tests__/bash.proc-test.js +115 -0
  56. package/dist/tools/builtins/__tests__/bash.proc-test.js.map +1 -0
  57. package/dist/tools/builtins/__tests__/bash.test.d.ts +2 -0
  58. package/dist/tools/builtins/__tests__/bash.test.d.ts.map +1 -0
  59. package/dist/tools/builtins/__tests__/bash.test.js +65 -0
  60. package/dist/tools/builtins/__tests__/bash.test.js.map +1 -0
  61. package/dist/tools/builtins/bash.d.ts.map +1 -1
  62. package/dist/tools/builtins/bash.js +89 -17
  63. package/dist/tools/builtins/bash.js.map +1 -1
  64. package/dist/tools/coordinator/__tests__/allow-delegation.test.d.ts +2 -0
  65. package/dist/tools/coordinator/__tests__/allow-delegation.test.d.ts.map +1 -0
  66. package/dist/tools/coordinator/__tests__/allow-delegation.test.js +94 -0
  67. package/dist/tools/coordinator/__tests__/allow-delegation.test.js.map +1 -0
  68. package/dist/tools/coordinator/__tests__/completion-delivery.test.d.ts +2 -0
  69. package/dist/tools/coordinator/__tests__/completion-delivery.test.d.ts.map +1 -0
  70. package/dist/tools/coordinator/__tests__/completion-delivery.test.js +255 -0
  71. package/dist/tools/coordinator/__tests__/completion-delivery.test.js.map +1 -0
  72. package/dist/tools/coordinator/__tests__/task-list.test.js +13 -7
  73. package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
  74. package/dist/tools/coordinator/index.d.ts +44 -0
  75. package/dist/tools/coordinator/index.d.ts.map +1 -1
  76. package/dist/tools/coordinator/index.js +239 -16
  77. package/dist/tools/coordinator/index.js.map +1 -1
  78. package/dist/types/agent/supervisor.d.ts +27 -0
  79. package/dist/types/agent/supervisor.d.ts.map +1 -1
  80. package/dist/types/tool/index.d.ts +13 -0
  81. package/dist/types/tool/index.d.ts.map +1 -1
  82. package/dist/types/tool/index.js.map +1 -1
  83. package/package.json +3 -2
  84. package/src/agents/SupervisorAgent.ts +19 -0
  85. package/src/agents/__tests__/supervisor-coordinator-registration.test.ts +37 -0
  86. package/src/gateway/__tests__/completion-inbox.test.ts +343 -0
  87. package/src/gateway/completion-inbox.ts +241 -0
  88. package/src/public-runtime.ts +4 -0
  89. package/src/registry/tool/__tests__/dialect.test.ts +8 -5
  90. package/src/registry/tool/dialect.ts +11 -4
  91. package/src/registry/tool/execute.ts +35 -0
  92. package/src/runtime/query/__tests__/completion-notification.test.ts +196 -0
  93. package/src/runtime/query/__tests__/run-survives-its-own-park.proc-test.ts +127 -0
  94. package/src/runtime/query/__tests__/step-allow-list.test.ts +205 -0
  95. package/src/runtime/query/executor.ts +21 -1
  96. package/src/runtime/query/index.ts +11 -0
  97. package/src/runtime/query/iteration/index.ts +77 -1
  98. package/src/runtime/query/iteration/phases/context.ts +51 -15
  99. package/src/tools/builtins/__tests__/bash.proc-test.ts +142 -0
  100. package/src/tools/builtins/__tests__/bash.test.ts +79 -0
  101. package/src/tools/builtins/bash.ts +100 -18
  102. package/src/tools/coordinator/__tests__/allow-delegation.test.ts +120 -0
  103. package/src/tools/coordinator/__tests__/completion-delivery.test.ts +347 -0
  104. package/src/tools/coordinator/__tests__/task-list.test.ts +13 -7
  105. package/src/tools/coordinator/index.ts +278 -21
  106. package/src/types/agent/supervisor.ts +28 -0
  107. package/src/types/tool/index.ts +13 -0
@@ -0,0 +1,275 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { CompletionInbox, formatCompletionNotification } from '../completion-inbox.js';
3
+ /**
4
+ * Who tells the supervisor a worker finished.
5
+ *
6
+ * Normally the `create_task` call does: it blocks, and the worker's output
7
+ * comes back as that call's `tool_result`. Two cases have no such call — a
8
+ * launch made in the background, and a blocking launch whose deadline passed
9
+ * while the worker kept going — and until this existed the result of those
10
+ * simply vanished. The gateway still held it; nothing ever read it.
11
+ *
12
+ * An earlier version of this channel was removed in `dc16d58` because it
13
+ * fired for completions the blocking tool had ALREADY delivered, so the
14
+ * supervisor saw every result twice: once as a `tool_result`, once as an
15
+ * orphan envelope. That removal is the reason the tests below care as much
16
+ * about what does NOT get delivered as about what does.
17
+ */
18
+ function handleFor(taskId, result) {
19
+ return {
20
+ taskId: taskId,
21
+ agentId: 'reviewer',
22
+ state: 'completed',
23
+ createdAt: 1_000,
24
+ completedAt: 3_500,
25
+ ...(result !== undefined
26
+ ? { result: { status: 'completed', result } }
27
+ : {}),
28
+ };
29
+ }
30
+ /** A gateway that only does the one thing the inbox uses. */
31
+ function fakeGateway() {
32
+ const listeners = new Set();
33
+ const gateway = {
34
+ onTaskCompleted(cb) {
35
+ listeners.add(cb);
36
+ return () => listeners.delete(cb);
37
+ },
38
+ };
39
+ return {
40
+ gateway,
41
+ settle: (h) => {
42
+ for (const cb of listeners)
43
+ cb(h);
44
+ },
45
+ get listeners() {
46
+ return listeners.size;
47
+ },
48
+ };
49
+ }
50
+ describe('a completion nobody waited for reaches the transcript', () => {
51
+ it('queues a settled task and hands it over once', () => {
52
+ const { gateway, settle } = fakeGateway();
53
+ const inbox = new CompletionInbox();
54
+ inbox.attach(gateway);
55
+ settle(handleFor('tsk_1', 'the report'));
56
+ expect(inbox.hasUnheard).toBe(true);
57
+ expect(inbox.drain().map((h) => h.taskId)).toEqual(['tsk_1']);
58
+ // Drained, not peeked: a notification that survives its own delivery
59
+ // is the duplicate bug wearing a different hat.
60
+ expect(inbox.drain()).toEqual([]);
61
+ expect(inbox.hasUnheard).toBe(false);
62
+ });
63
+ it('queues nothing at all when no task settles', () => {
64
+ const { gateway } = fakeGateway();
65
+ const inbox = new CompletionInbox();
66
+ inbox.attach(gateway);
67
+ expect(inbox.hasUnheard).toBe(false);
68
+ expect(inbox.drain()).toEqual([]);
69
+ });
70
+ });
71
+ describe('a completion the tool already delivered is never delivered twice', () => {
72
+ it('drops a claimed completion', () => {
73
+ // The `dc16d58` regression, pinned. `create_task` blocked, got the
74
+ // result, and returned it as its own tool_result — so the envelope
75
+ // must not also carry it.
76
+ const { gateway, settle } = fakeGateway();
77
+ const inbox = new CompletionInbox();
78
+ inbox.attach(gateway);
79
+ settle(handleFor('tsk_1', 'the report'));
80
+ inbox.claim('tsk_1');
81
+ expect(inbox.hasUnheard).toBe(false);
82
+ expect(inbox.drain()).toEqual([]);
83
+ });
84
+ it('drops it even when the claim beats the announcement', () => {
85
+ // Ordering is not guaranteed: the tool's own `waitForTask` can resolve
86
+ // before the gateway's completion listener runs. A claim that only
87
+ // worked after the announcement would leak a duplicate exactly in the
88
+ // races that are hardest to reproduce.
89
+ const { gateway, settle } = fakeGateway();
90
+ const inbox = new CompletionInbox();
91
+ inbox.attach(gateway);
92
+ inbox.claim('tsk_1');
93
+ settle(handleFor('tsk_1', 'the report'));
94
+ expect(inbox.drain()).toEqual([]);
95
+ });
96
+ it('still delivers the sibling that nobody claimed', () => {
97
+ // The mixed turn: one worker awaited to completion, one abandoned.
98
+ const { gateway, settle } = fakeGateway();
99
+ const inbox = new CompletionInbox();
100
+ inbox.attach(gateway);
101
+ settle(handleFor('tsk_awaited', 'delivered inline'));
102
+ settle(handleFor('tsk_abandoned', 'nobody heard this'));
103
+ inbox.claim('tsk_awaited');
104
+ expect(inbox.drain().map((h) => h.taskId)).toEqual(['tsk_abandoned']);
105
+ });
106
+ it('subscribes once no matter how often it is attached', () => {
107
+ // Two subscriptions would queue every completion twice and reproduce
108
+ // the duplicate delivery from the inside.
109
+ const { gateway, settle, ...rest } = fakeGateway();
110
+ const inbox = new CompletionInbox();
111
+ inbox.attach(gateway);
112
+ inbox.attach(gateway);
113
+ settle(handleFor('tsk_1', 'once'));
114
+ expect(inbox.drain()).toHaveLength(1);
115
+ void rest;
116
+ });
117
+ it('stops listening when closed', () => {
118
+ const { gateway, settle } = fakeGateway();
119
+ const inbox = new CompletionInbox();
120
+ inbox.attach(gateway);
121
+ inbox.close();
122
+ settle(handleFor('tsk_1', 'too late'));
123
+ expect(inbox.drain()).toEqual([]);
124
+ });
125
+ });
126
+ describe('a launch nobody is waiting for holds the run open', () => {
127
+ it('counts an expected task as pending work before it settles', () => {
128
+ // Without this the run settles while a background worker is still
129
+ // going and discards the result the launch existed to produce.
130
+ const { gateway } = fakeGateway();
131
+ const inbox = new CompletionInbox();
132
+ inbox.attach(gateway);
133
+ expect(inbox.hasPendingWork).toBe(false);
134
+ inbox.expect('tsk_1');
135
+ expect(inbox.hasPendingWork).toBe(true);
136
+ });
137
+ it('stops counting it once it settles', () => {
138
+ const { gateway, settle } = fakeGateway();
139
+ const inbox = new CompletionInbox();
140
+ inbox.attach(gateway);
141
+ inbox.expect('tsk_1');
142
+ settle(handleFor('tsk_1', 'done'));
143
+ // Still pending — as an UNHEARD completion now rather than an
144
+ // outstanding one, which is what the loop drains.
145
+ expect(inbox.hasPendingWork).toBe(true);
146
+ inbox.drain();
147
+ expect(inbox.hasPendingWork).toBe(false);
148
+ });
149
+ it('ignores a task already delivered inline', () => {
150
+ const { gateway } = fakeGateway();
151
+ const inbox = new CompletionInbox();
152
+ inbox.attach(gateway);
153
+ inbox.claim('tsk_1');
154
+ inbox.expect('tsk_1');
155
+ expect(inbox.hasPendingWork).toBe(false);
156
+ });
157
+ it('keeps a result that already arrived, even when the task is cancelled', () => {
158
+ // The window: a worker finishes, its completion is queued for the next
159
+ // drain, and the model — told nothing yet, and reading a tool that says
160
+ // it cancels a RUNNING task — cancels it. Clearing the queue here threw
161
+ // away work that was done and output that existed nowhere else.
162
+ //
163
+ // `forget` is about pending work. A finished result is not pending work.
164
+ const { gateway, settle } = fakeGateway();
165
+ const inbox = new CompletionInbox();
166
+ inbox.attach(gateway);
167
+ inbox.expect('tsk_1');
168
+ settle(handleFor('tsk_1', 'the worker finished before the cancel landed'));
169
+ inbox.forget('tsk_1');
170
+ const drained = inbox.drain();
171
+ expect(drained.map((h) => h.taskId), 'the finished result was discarded').toEqual(['tsk_1']);
172
+ expect(drained[0]?.result?.result).toBe('the worker finished before the cancel landed');
173
+ });
174
+ it('stops expecting a task that was cancelled', () => {
175
+ // `expect` is only cleared by a COMPLETION, so a cancelled worker used
176
+ // to keep the run open for the whole grace period, every time it tried
177
+ // to settle, waiting for a result that had been called off.
178
+ const { gateway } = fakeGateway();
179
+ const inbox = new CompletionInbox();
180
+ inbox.attach(gateway);
181
+ inbox.expect('tsk_1');
182
+ inbox.forget('tsk_1');
183
+ expect(inbox.hasPendingWork).toBe(false);
184
+ });
185
+ it('waits for an arrival and returns as soon as one lands', async () => {
186
+ const { gateway, settle } = fakeGateway();
187
+ const inbox = new CompletionInbox();
188
+ inbox.attach(gateway);
189
+ inbox.expect('tsk_1');
190
+ const waited = inbox.waitForArrival(5_000);
191
+ settle(handleFor('tsk_1', 'done'));
192
+ await waited;
193
+ expect(inbox.drain().map((h) => h.taskId)).toEqual(['tsk_1']);
194
+ });
195
+ it('does not wait at all when there is nothing outstanding', async () => {
196
+ const { gateway } = fakeGateway();
197
+ const inbox = new CompletionInbox();
198
+ inbox.attach(gateway);
199
+ // A deadline this long would hang the suite if it were honoured.
200
+ await inbox.waitForArrival(600_000);
201
+ });
202
+ it('gives up at the deadline rather than holding a run forever', async () => {
203
+ // The bound is the point: a worker that never finishes must not keep
204
+ // the run open indefinitely.
205
+ const { gateway } = fakeGateway();
206
+ const inbox = new CompletionInbox();
207
+ inbox.attach(gateway);
208
+ inbox.expect('tsk_never');
209
+ await inbox.waitForArrival(10);
210
+ expect(inbox.hasPendingWork).toBe(true);
211
+ });
212
+ it('releases a waiter when the inbox closes', async () => {
213
+ const { gateway } = fakeGateway();
214
+ const inbox = new CompletionInbox();
215
+ inbox.attach(gateway);
216
+ inbox.expect('tsk_1');
217
+ const waited = inbox.waitForArrival(600_000);
218
+ inbox.close();
219
+ await waited;
220
+ });
221
+ });
222
+ describe('the notification says which task and what it produced', () => {
223
+ it('carries the id, the agent, the state and the output', () => {
224
+ // All four matter. Without the id the supervisor cannot say which of
225
+ // five workers this was; without the output it has to make the extra
226
+ // call this mechanism exists to remove.
227
+ const text = formatCompletionNotification([handleFor('tsk_42', 'the findings')]);
228
+ expect(text).toContain('task_id: tsk_42');
229
+ expect(text).toContain('agent: reviewer');
230
+ expect(text).toContain('state: completed');
231
+ expect(text).toContain('duration_ms: 2500');
232
+ expect(text).toContain('the findings');
233
+ });
234
+ it('names the tool that fetches the rest when it truncates', () => {
235
+ const text = formatCompletionNotification([handleFor('tsk_42', 'x'.repeat(10_000))]);
236
+ expect(text).toContain('truncated');
237
+ // The id is repeated in the truncation notice, so the follow-up call
238
+ // does not require scrolling back up through 4 kB of output.
239
+ expect(text).toContain('wait_for_task with task_id "tsk_42"');
240
+ expect(text.length).toBeLessThan(4_500);
241
+ });
242
+ it('says so rather than going blank when a task produced nothing', () => {
243
+ const text = formatCompletionNotification([handleFor('tsk_42')]);
244
+ expect(text).toContain('task_id: tsk_42');
245
+ expect(text).toContain('produced no output');
246
+ });
247
+ it('groups a batch into one message', () => {
248
+ const text = formatCompletionNotification([
249
+ handleFor('tsk_1', 'first'),
250
+ handleFor('tsk_2', 'second'),
251
+ ]);
252
+ expect(text).toContain('2 tasks');
253
+ expect(text).toContain('tsk_1');
254
+ expect(text).toContain('tsk_2');
255
+ });
256
+ it('explains why it is arriving here and not as a tool result', () => {
257
+ // Otherwise the model has to guess whether it missed a call, and the
258
+ // guess it makes is to go looking — which is the polling loop again.
259
+ const text = formatCompletionNotification([handleFor('tsk_1', 'done')]);
260
+ expect(text).toContain('not waiting on it');
261
+ });
262
+ });
263
+ describe('the inbox does not require anything of a host gateway', () => {
264
+ it('uses only onTaskCompleted', () => {
265
+ // The whole point of attaching through the existing subscription: a
266
+ // host that implements TaskGateway keeps working untouched, and one
267
+ // that was already firing completions into an empty listener set now
268
+ // has a listener.
269
+ const onTaskCompleted = vi.fn(() => () => { });
270
+ const inbox = new CompletionInbox();
271
+ inbox.attach({ onTaskCompleted });
272
+ expect(onTaskCompleted).toHaveBeenCalledTimes(1);
273
+ });
274
+ });
275
+ //# sourceMappingURL=completion-inbox.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-inbox.test.js","sourceRoot":"","sources":["../../../src/gateway/__tests__/completion-inbox.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import type { TaskGateway, TaskHandle } from '../types/agent/gateway.js';
2
+ import type { TaskId } from '../types/ids/index.js';
3
+ /**
4
+ * Completions that finished with nobody left to hear them.
5
+ *
6
+ * A worker's result reaches the supervisor as the `tool_result` of the
7
+ * `create_task` that launched it. That works whenever the launching call is
8
+ * still the live path — but it is not the only way a task ends:
9
+ *
10
+ * - the launching tool hit its deadline and the executor returned
11
+ * *"timed out… it may still be running"* to the model. The worker then
12
+ * finished normally, holding a result nothing would ever read.
13
+ * - the task was launched in the background on purpose, so there is no
14
+ * call waiting on it by design.
15
+ *
16
+ * In both cases the completion exists, the gateway remembers it, and the
17
+ * model is never told. That is the gap this closes: the run subscribes once,
18
+ * every settled task lands here, and anything a tool did NOT hand over
19
+ * inline is drained into the transcript as a notification the next turn can
20
+ * read.
21
+ *
22
+ * The disambiguation is the whole design. An earlier version of the envelope
23
+ * path was removed (`dc16d58`) because it fired for completions the blocking
24
+ * tool had ALREADY delivered, so the supervisor saw every result twice —
25
+ * once correctly as a `tool_result`, once as an orphan envelope. Removing it
26
+ * fixed the duplicate and left the abandoned case with no channel at all.
27
+ * Claiming is what tells the two apart: a tool that delivers a completion
28
+ * says so, and only unclaimed completions become envelopes.
29
+ *
30
+ * It attaches through `onTaskCompleted`, which every `TaskGateway` already
31
+ * has, so a host gateway needs no change to take part — a host that was
32
+ * firing completions into a listener set with no listeners now has one.
33
+ */
34
+ export declare class CompletionInbox {
35
+ private readonly unheard;
36
+ private readonly claimed;
37
+ /** Launched with nothing waiting on it, and not settled yet. */
38
+ private readonly outstanding;
39
+ private readonly arrivals;
40
+ private detach?;
41
+ /**
42
+ * Start listening.
43
+ *
44
+ * Returns a detach function; calling `attach` twice is a no-op rather
45
+ * than a second subscription, because a doubly-attached inbox would
46
+ * queue every completion twice and reproduce the exact duplicate this
47
+ * class exists to prevent.
48
+ */
49
+ attach(gateway: TaskGateway): () => void;
50
+ /**
51
+ * Say that a task was launched with nothing waiting on it.
52
+ *
53
+ * Without this the inbox can only see completions that have already
54
+ * happened, and a run whose supervisor launched a background worker and
55
+ * then answered would settle while the worker was still going — throwing
56
+ * away the very result the launch existed to produce. Knowing a task is
57
+ * outstanding is what lets the loop hold the run open for it.
58
+ */
59
+ expect(taskId: TaskId): void;
60
+ /** Whether anything is either waiting to be told or still running. */
61
+ get hasPendingWork(): boolean;
62
+ /**
63
+ * Wait for the next completion, or for the deadline, whichever comes first.
64
+ *
65
+ * Bounded on purpose. A worker that never finishes must not hold a run
66
+ * open forever, and the caller decides how long "long enough" is — the
67
+ * run's own budget is the only thing that knows.
68
+ */
69
+ waitForArrival(timeoutMs: number): Promise<void>;
70
+ /**
71
+ * Say that this completion reached the model as a `tool_result`.
72
+ *
73
+ * Idempotent, and safe to call before the completion is announced: the
74
+ * claim is remembered so a late announcement does not re-queue it.
75
+ */
76
+ claim(taskId: TaskId): void;
77
+ /** Whether anything is waiting to be told. */
78
+ get hasUnheard(): boolean;
79
+ /**
80
+ * Take every unheard completion, leaving the inbox empty.
81
+ *
82
+ * Draining rather than peeking: a notification that stays queued after
83
+ * being delivered is the duplicate-delivery bug in a different costume.
84
+ */
85
+ drain(): TaskHandle[];
86
+ /**
87
+ * Stop expecting a task that is never going to arrive.
88
+ *
89
+ * Cancelling is the case this exists for. `expect` puts a task on the
90
+ * outstanding list and only a COMPLETION takes it off, so a cancelled
91
+ * worker left `hasPendingWork` true for the rest of the run — and every
92
+ * attempt to settle then paid the full grace period waiting for a result
93
+ * that had been called off.
94
+ */
95
+ forget(taskId: TaskId): void;
96
+ /** Stop listening. Safe to call more than once. */
97
+ close(): void;
98
+ }
99
+ /**
100
+ * The message a supervisor reads when a worker it stopped waiting for
101
+ * finishes.
102
+ *
103
+ * It carries the task id, because without one the model cannot say which of
104
+ * five workers this was, and it carries the output, because a notification
105
+ * that only says "done" forces exactly the follow-up call this mechanism
106
+ * exists to remove. Long output is truncated with the task id repeated in
107
+ * the truncation notice, so the full text stays one `wait_for_task` away and
108
+ * the model knows which id to ask for — that tool takes a `task_id` and
109
+ * returns immediately for a task that has already finished, where the
110
+ * listing takes only a state filter and could not have been followed.
111
+ */
112
+ export declare function formatCompletionNotification(handles: readonly TaskHandle[]): string;
113
+ //# sourceMappingURL=completion-inbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-inbox.d.ts","sourceRoot":"","sources":["../../src/gateway/completion-inbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,MAAM,CAAC,CAAY;IAE3B;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI;IAcxC;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK5B,sEAAsE;IACtE,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BhD;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM3B,8CAA8C;IAC9C,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;;;OAKG;IACH,KAAK,IAAI,UAAU,EAAE;IAQrB;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAsB5B,mDAAmD;IACnD,KAAK,IAAI,IAAI;CAUb;AAKD;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CA8BnF"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Completions that finished with nobody left to hear them.
3
+ *
4
+ * A worker's result reaches the supervisor as the `tool_result` of the
5
+ * `create_task` that launched it. That works whenever the launching call is
6
+ * still the live path — but it is not the only way a task ends:
7
+ *
8
+ * - the launching tool hit its deadline and the executor returned
9
+ * *"timed out… it may still be running"* to the model. The worker then
10
+ * finished normally, holding a result nothing would ever read.
11
+ * - the task was launched in the background on purpose, so there is no
12
+ * call waiting on it by design.
13
+ *
14
+ * In both cases the completion exists, the gateway remembers it, and the
15
+ * model is never told. That is the gap this closes: the run subscribes once,
16
+ * every settled task lands here, and anything a tool did NOT hand over
17
+ * inline is drained into the transcript as a notification the next turn can
18
+ * read.
19
+ *
20
+ * The disambiguation is the whole design. An earlier version of the envelope
21
+ * path was removed (`dc16d58`) because it fired for completions the blocking
22
+ * tool had ALREADY delivered, so the supervisor saw every result twice —
23
+ * once correctly as a `tool_result`, once as an orphan envelope. Removing it
24
+ * fixed the duplicate and left the abandoned case with no channel at all.
25
+ * Claiming is what tells the two apart: a tool that delivers a completion
26
+ * says so, and only unclaimed completions become envelopes.
27
+ *
28
+ * It attaches through `onTaskCompleted`, which every `TaskGateway` already
29
+ * has, so a host gateway needs no change to take part — a host that was
30
+ * firing completions into a listener set with no listeners now has one.
31
+ */
32
+ export class CompletionInbox {
33
+ unheard = new Map();
34
+ claimed = new Set();
35
+ /** Launched with nothing waiting on it, and not settled yet. */
36
+ outstanding = new Set();
37
+ arrivals = new Set();
38
+ detach;
39
+ /**
40
+ * Start listening.
41
+ *
42
+ * Returns a detach function; calling `attach` twice is a no-op rather
43
+ * than a second subscription, because a doubly-attached inbox would
44
+ * queue every completion twice and reproduce the exact duplicate this
45
+ * class exists to prevent.
46
+ */
47
+ attach(gateway) {
48
+ if (this.detach)
49
+ return this.detach;
50
+ this.detach = gateway.onTaskCompleted((handle) => {
51
+ // A completion claimed before it was announced — a tool that
52
+ // finished its wait faster than the listener ran — is already
53
+ // delivered. Nothing to queue.
54
+ this.outstanding.delete(handle.taskId);
55
+ if (this.claimed.has(handle.taskId))
56
+ return;
57
+ this.unheard.set(handle.taskId, handle);
58
+ for (const wake of this.arrivals)
59
+ wake();
60
+ });
61
+ return this.detach;
62
+ }
63
+ /**
64
+ * Say that a task was launched with nothing waiting on it.
65
+ *
66
+ * Without this the inbox can only see completions that have already
67
+ * happened, and a run whose supervisor launched a background worker and
68
+ * then answered would settle while the worker was still going — throwing
69
+ * away the very result the launch existed to produce. Knowing a task is
70
+ * outstanding is what lets the loop hold the run open for it.
71
+ */
72
+ expect(taskId) {
73
+ if (this.claimed.has(taskId))
74
+ return;
75
+ this.outstanding.add(taskId);
76
+ }
77
+ /** Whether anything is either waiting to be told or still running. */
78
+ get hasPendingWork() {
79
+ return this.unheard.size > 0 || this.outstanding.size > 0;
80
+ }
81
+ /**
82
+ * Wait for the next completion, or for the deadline, whichever comes first.
83
+ *
84
+ * Bounded on purpose. A worker that never finishes must not hold a run
85
+ * open forever, and the caller decides how long "long enough" is — the
86
+ * run's own budget is the only thing that knows.
87
+ */
88
+ waitForArrival(timeoutMs) {
89
+ if (this.unheard.size > 0)
90
+ return Promise.resolve();
91
+ if (this.outstanding.size === 0)
92
+ return Promise.resolve();
93
+ return new Promise((resolve) => {
94
+ const timer = setTimeout(finish, timeoutMs);
95
+ timer.unref?.();
96
+ function finish() {
97
+ clearTimeout(timer);
98
+ wake.done = true;
99
+ resolve();
100
+ }
101
+ const wake = Object.assign(() => {
102
+ if (!wake.done) {
103
+ this.arrivals.delete(wake);
104
+ finish();
105
+ }
106
+ }, { done: false });
107
+ this.arrivals.add(wake);
108
+ });
109
+ }
110
+ /**
111
+ * Say that this completion reached the model as a `tool_result`.
112
+ *
113
+ * Idempotent, and safe to call before the completion is announced: the
114
+ * claim is remembered so a late announcement does not re-queue it.
115
+ */
116
+ claim(taskId) {
117
+ this.claimed.add(taskId);
118
+ this.unheard.delete(taskId);
119
+ this.outstanding.delete(taskId);
120
+ }
121
+ /** Whether anything is waiting to be told. */
122
+ get hasUnheard() {
123
+ return this.unheard.size > 0;
124
+ }
125
+ /**
126
+ * Take every unheard completion, leaving the inbox empty.
127
+ *
128
+ * Draining rather than peeking: a notification that stays queued after
129
+ * being delivered is the duplicate-delivery bug in a different costume.
130
+ */
131
+ drain() {
132
+ if (this.unheard.size === 0)
133
+ return [];
134
+ const handles = [...this.unheard.values()];
135
+ this.unheard.clear();
136
+ for (const handle of handles)
137
+ this.claimed.add(handle.taskId);
138
+ return handles;
139
+ }
140
+ /**
141
+ * Stop expecting a task that is never going to arrive.
142
+ *
143
+ * Cancelling is the case this exists for. `expect` puts a task on the
144
+ * outstanding list and only a COMPLETION takes it off, so a cancelled
145
+ * worker left `hasPendingWork` true for the rest of the run — and every
146
+ * attempt to settle then paid the full grace period waiting for a result
147
+ * that had been called off.
148
+ */
149
+ forget(taskId) {
150
+ this.outstanding.delete(taskId);
151
+ // `unheard` is deliberately NOT touched.
152
+ //
153
+ // The two sets mean different things. `outstanding` is pending WORK,
154
+ // and cancelling is exactly the statement that it should stop being
155
+ // waited for. `unheard` is a RESULT that already exists — the worker
156
+ // finished, the completion arrived, and it is queued for the next
157
+ // drain. Clearing it here destroyed that.
158
+ //
159
+ // The window is small and entirely reachable: nothing has told the
160
+ // model the worker finished, and `cancel_task` says it cancels a
161
+ // running task, so cancelling one that has just completed is the
162
+ // obvious move rather than a mistake. The run then reports "cancelled"
163
+ // over work that was done and output that no longer exists anywhere.
164
+ //
165
+ // Note the asymmetry with `claim`, which does clear `unheard` — and is
166
+ // right to, because there a tool has just handed the model the same
167
+ // result. This one hands over nothing.
168
+ for (const wake of [...this.arrivals])
169
+ wake();
170
+ }
171
+ /** Stop listening. Safe to call more than once. */
172
+ close() {
173
+ this.detach?.();
174
+ this.detach = undefined;
175
+ this.unheard.clear();
176
+ this.outstanding.clear();
177
+ // Release anyone still waiting. A closed inbox would otherwise hold
178
+ // them to their own deadline for a completion that can no longer come.
179
+ for (const wake of [...this.arrivals])
180
+ wake();
181
+ this.arrivals.clear();
182
+ }
183
+ }
184
+ /** How much of a worker's output rides in the notification itself. */
185
+ const NOTIFICATION_OUTPUT_LIMIT = 4_000;
186
+ /**
187
+ * The message a supervisor reads when a worker it stopped waiting for
188
+ * finishes.
189
+ *
190
+ * It carries the task id, because without one the model cannot say which of
191
+ * five workers this was, and it carries the output, because a notification
192
+ * that only says "done" forces exactly the follow-up call this mechanism
193
+ * exists to remove. Long output is truncated with the task id repeated in
194
+ * the truncation notice, so the full text stays one `wait_for_task` away and
195
+ * the model knows which id to ask for — that tool takes a `task_id` and
196
+ * returns immediately for a task that has already finished, where the
197
+ * listing takes only a state filter and could not have been followed.
198
+ */
199
+ export function formatCompletionNotification(handles) {
200
+ const blocks = handles.map((handle) => {
201
+ const durationMs = handle.completedAt ? handle.completedAt - handle.createdAt : undefined;
202
+ const output = handle.result?.result ?? handle.result?.lastError ?? '';
203
+ const truncated = output.length > NOTIFICATION_OUTPUT_LIMIT
204
+ ? // `wait_for_task`, not `agent_task_list` — the listing takes only a
205
+ // state filter, so an instruction to call it "with task_id" named
206
+ // a parameter that does not exist and could not be followed. On an
207
+ // already-finished task the wait returns immediately.
208
+ `${output.slice(0, NOTIFICATION_OUTPUT_LIMIT)}\n… truncated. Call wait_for_task with task_id "${handle.taskId}" for the full output.`
209
+ : output;
210
+ const lines = [
211
+ `task_id: ${handle.taskId}`,
212
+ `agent: ${handle.agentId}`,
213
+ `state: ${handle.state}`,
214
+ ...(durationMs !== undefined ? [`duration_ms: ${durationMs}`] : []),
215
+ '',
216
+ truncated.length > 0 ? truncated : '(the task produced no output)',
217
+ ];
218
+ return `<task-notification>\n${lines.join('\n')}\n</task-notification>`;
219
+ });
220
+ const preamble = handles.length === 1
221
+ ? 'A task you launched has finished. This is its result — you were not waiting on it, so it arrives here rather than as a tool result.'
222
+ : `${handles.length} tasks you launched have finished. These are their results — you were not waiting on them, so they arrive here rather than as tool results.`;
223
+ return `${preamble}\n\n${blocks.join('\n\n')}`;
224
+ }
225
+ //# sourceMappingURL=completion-inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-inbox.js","sourceRoot":"","sources":["../../src/gateway/completion-inbox.ts"],"names":[],"mappings":"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"}
@@ -50,6 +50,7 @@ export { discoverAllPluginDirs, discoverPlugins, loadPluginManifest, PluginLifec
50
50
  export { AgentManager, EmergencySaveManager, PlanManager, RunPersistence, ThreadManager, } from './manager/index.js';
51
51
  export { InMemoryThreadStore } from './store/thread/memory.js';
52
52
  export { LocalTaskGateway } from './gateway/local.js';
53
+ export { CompletionInbox, formatCompletionNotification } from './gateway/completion-inbox.js';
53
54
  export { classifyProviderError, DEFAULT_PROVIDER_RETRY, DuplicateProviderError, isAbortError, isProviderError, LazyProviderLoadError, LazyProviderSyncCreateError, MOCK_CAPABILITIES, MockLLMProvider, parseRetryAfterMs, PERMISSIVE_PROVIDER_CAPABILITIES, ProviderError, ProviderRegistry, ProviderRequestError, registerMock, resolveProviderCapabilities, UnknownProviderError, withProviderRetry, } from './provider/index.js';
54
55
  export type { ProviderRetryConfig, WithProviderRetryOptions } from './provider/index.js';
55
56
  export { assertIsolation, describeIsolation, isolationOf, LocalSandboxProvider, missingIsolation, SandboxProviderFactory, } from './sandbox/index.js';