@namzu/sdk 12.0.0 → 12.0.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 12.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 5fe072b: A concurrent fan-out no longer kills most of its own launches.
8
+
9
+ `LocalTaskGateway.createTask` passes a progress tee into `sendMessage`, and that
10
+ callback read `task.taskId` — the `const` that the very same `await` assigns. So
11
+ a child that emitted any run event before `sendMessage` resolved reached that
12
+ line inside the temporal dead zone and threw `Cannot access 'task' before
13
+ initialization`, taking the whole launch down with it.
14
+
15
+ Two things kept it hidden. A single sequential launch usually resolves before
16
+ the child says anything, so the ordering rarely bit. And the throw only happens
17
+ when a progress listener is actually attached — with an empty subscriber set the
18
+ loop body never runs and the dead zone is never entered, which is why no unit
19
+ test caught it. `create_task` attaches one for its idle bound on every blocking
20
+ launch, so production always had one.
21
+
22
+ Under a concurrent fan-out both conditions hold. Observed on the published
23
+ package: four `create_task` calls from one assistant turn — the shape that
24
+ tool's own description tells the model to use — and three of the four died with
25
+ `create_task failed: Cannot access 'task' before initialization`.
26
+
27
+ The tee now reads an id filled in after the spawn resolves, and reports nothing
28
+ before then. That window is not a loss: with no id the caller does not yet hold
29
+ the handle, so no idle bound is running against the task and there is no
30
+ progress to attribute to anyone.
31
+
32
+ Introduced in #130 with the idle-bound work. Found by running a live concurrent
33
+ fan-out against the published package rather than by any test.
34
+
3
35
  ## 12.0.0
4
36
 
5
37
  ### Major Changes
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=a-progress-tee-cannot-name-a-task-that-does-not-exist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a-progress-tee-cannot-name-a-task-that-does-not-exist.test.d.ts","sourceRoot":"","sources":["../../../src/gateway/__tests__/a-progress-tee-cannot-name-a-task-that-does-not-exist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { LocalTaskGateway } from '../local.js';
3
+ /**
4
+ * A child that spoke before its own spawn resolved killed the launch.
5
+ *
6
+ * The progress tee handed to `sendMessage` read `task.taskId` — the `const`
7
+ * that the very same `await` assigns. So any run event emitted by the child
8
+ * before `sendMessage` returned reached that line inside the temporal dead
9
+ * zone and threw `Cannot access 'task' before initialization`, taking the whole
10
+ * `create_task` down with it.
11
+ *
12
+ * It survived review and a full unit suite because a single sequential launch
13
+ * usually resolves before the child says anything. A CONCURRENT fan-out does
14
+ * not — and that is the shape `create_task`'s own description tells the model
15
+ * to use. Observed live on the published package: four launches from one turn,
16
+ * three dead.
17
+ */
18
+ const RUN = 'run_tee';
19
+ /** Emits a run event DURING the spawn, before it resolves. */
20
+ class TalksDuringSpawn {
21
+ emitsBeforeResolve;
22
+ /** Kept so a test can make the child speak AFTER the spawn resolved too. */
23
+ lastListener;
24
+ constructor(emitsBeforeResolve) {
25
+ this.emitsBeforeResolve = emitsBeforeResolve;
26
+ }
27
+ /** The child says something once the caller holds its handle. */
28
+ speakNow() {
29
+ this.lastListener?.({ type: 'iteration_started', runId: RUN, iteration: 99 });
30
+ }
31
+ async sendMessage(options, _context, listener) {
32
+ this.lastListener = listener;
33
+ // The child is alive and streaming before the caller holds its handle.
34
+ for (let i = 0; i < this.emitsBeforeResolve; i += 1) {
35
+ listener?.({ type: 'iteration_started', runId: RUN, iteration: i });
36
+ }
37
+ await Promise.resolve();
38
+ return {
39
+ taskId: 'task_spoke_early',
40
+ agentId: options.agentId,
41
+ agent: {},
42
+ childAbortController: new AbortController(),
43
+ context: {},
44
+ state: 'completed',
45
+ pendingMessages: [],
46
+ createdAt: 1,
47
+ };
48
+ }
49
+ cancel() { }
50
+ cancelAll() { }
51
+ async continueTask() { }
52
+ queueMessage() { }
53
+ drainMessages() {
54
+ return [];
55
+ }
56
+ async waitForCompletion() { }
57
+ getInstance() {
58
+ return undefined;
59
+ }
60
+ listByParent() {
61
+ return [];
62
+ }
63
+ listActive() {
64
+ return [];
65
+ }
66
+ getState() {
67
+ return undefined;
68
+ }
69
+ on() { }
70
+ off() { }
71
+ cleanup() { }
72
+ dispose() { }
73
+ }
74
+ function context() {
75
+ return {
76
+ parentRunId: RUN,
77
+ parentAgentId: 'supervisor',
78
+ parentAbortController: new AbortController(),
79
+ depth: 0,
80
+ budgetTracker: { total: 100_000, remaining: 100_000 },
81
+ tenantId: 'tnt_t',
82
+ threadId: 'thd_t',
83
+ sessionId: 'ses_t',
84
+ projectId: 'prj_t',
85
+ parentActor: { kind: 'agent', agentId: 'supervisor', tenantId: 'tnt_t' },
86
+ };
87
+ }
88
+ describe('a launch survives a child that speaks before the spawn resolves', () => {
89
+ it('does not throw when an event arrives mid-spawn', async () => {
90
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(3), context());
91
+ // The listener is what makes this reproduce, and its absence is what
92
+ // made the bug invisible for so long: with no progress subscriber the
93
+ // loop body never runs, so `task.taskId` is never evaluated and the
94
+ // dead zone is never entered. `create_task` attaches one for the idle
95
+ // bound on every blocking launch, which is why it bit in production and
96
+ // not in any test.
97
+ gateway.onTaskProgress?.(() => { });
98
+ await expect(gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' })).resolves.toMatchObject({ taskId: 'task_spoke_early' });
99
+ });
100
+ it('still forwards those events to the host listener', async () => {
101
+ // The tee is what broke, not the forwarding. A host watching the child
102
+ // must not lose its early events to this fix.
103
+ const seen = [];
104
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(3), context(), (e) => {
105
+ seen.push(e.type);
106
+ });
107
+ await gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' });
108
+ expect(seen).toEqual(['iteration_started', 'iteration_started', 'iteration_started']);
109
+ });
110
+ it('reports progress once the task has an id to report it against', async () => {
111
+ // Before the id exists nothing is waiting on the task — the caller does
112
+ // not hold the handle yet — so silence there is correct. What must work
113
+ // is everything after.
114
+ const manager = new TalksDuringSpawn(1);
115
+ const gateway = new LocalTaskGateway(manager, context());
116
+ const progressed = [];
117
+ gateway.onTaskProgress?.((id) => progressed.push(id));
118
+ await gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' });
119
+ // The one emitted mid-spawn is not attributed — there was no id yet.
120
+ expect(progressed).toEqual([]);
121
+ // Now the child speaks with the handle already in the caller's hands,
122
+ // which is the case an idle bound is actually measuring.
123
+ manager.speakNow();
124
+ expect(progressed).toEqual(['task_spoke_early']);
125
+ });
126
+ it('survives a concurrent fan-out, which is how this was found', async () => {
127
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(2), context());
128
+ // Same reason as above: the live failure came through the idle bound's
129
+ // subscriber, so a fan-out test without one would not reproduce the
130
+ // thing it is named after.
131
+ gateway.onTaskProgress?.(() => { });
132
+ const launched = await Promise.all([1, 2, 3, 4].map(() => gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' })));
133
+ expect(launched).toHaveLength(4);
134
+ expect(launched.every((h) => h.taskId === 'task_spoke_early')).toBe(true);
135
+ });
136
+ });
137
+ //# sourceMappingURL=a-progress-tee-cannot-name-a-task-that-does-not-exist.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a-progress-tee-cannot-name-a-task-that-does-not-exist.test.js","sourceRoot":"","sources":["../../../src/gateway/__tests__/a-progress-tee-cannot-name-a-task-that-does-not-exist.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAI9D,qBAAa,gBAAiB,YAAW,WAAW;IACnD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAE/C,OAAO,CAAC,WAAW,CAAC,CAA2E;IAE/F,OAAO,CAAC,mBAAmB,CAA+C;IAE1E;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAmC;IAC/D,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;gBAGvE,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,gBAAgB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,sBAAsB,GAAG,gBAAgB,CAAC,EACvF,OAAO,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;KAAE;IAWpD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IAqFjE;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kBAAkB;IA0BpB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAShD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAK/C;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKrC,SAAS,IAAI,UAAU,EAAE;IAgBzB;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9D,eAAe,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;CAMnE"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAI9D,qBAAa,gBAAiB,YAAW,WAAW;IACnD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAE/C,OAAO,CAAC,WAAW,CAAC,CAA2E;IAE/F,OAAO,CAAC,mBAAmB,CAA+C;IAE1E;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAmC;IAC/D,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;gBAGvE,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,gBAAgB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,sBAAsB,GAAG,gBAAgB,CAAC,EACvF,OAAO,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;KAAE;IAWpD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IA6GjE;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kBAAkB;IA0BpB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAShD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAK/C;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKrC,SAAS,IAAI,UAAU,EAAE;IAgBzB;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9D,eAAe,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;CAMnE"}
@@ -36,6 +36,11 @@ export class LocalTaskGateway {
36
36
  }
37
37
  }
38
38
  async createTask(options) {
39
+ // Filled once the spawn resolves. A box rather than a bare binding
40
+ // because the assignment happens AFTER the `await` that the reader is
41
+ // passed into — see the progress tee below for why it cannot simply
42
+ // read the `task` const it is declared beside.
43
+ const launched = {};
39
44
  const task = await this.agentManager.sendMessage({
40
45
  agentId: options.agentId,
41
46
  input: {
@@ -84,11 +89,30 @@ export class LocalTaskGateway {
84
89
  // idle bound measures. The event itself is not forwarded — a
85
90
  // progress signal that carried the child's output would be a
86
91
  // second, undocumented way to read a worker's work.
92
+ //
93
+ // The id comes from `launched.id`, NOT from the `task` const
94
+ // below. This callback is handed to the very `await` that assigns
95
+ // `task`, so a child that emits anything before `sendMessage`
96
+ // resolves reached it inside the temporal dead zone and threw
97
+ // `Cannot access 'task' before initialization` — killing the launch
98
+ // outright.
99
+ //
100
+ // It survived because a single sequential launch usually resolves
101
+ // before the child says anything. A concurrent fan-out does not:
102
+ // with four `create_task` calls from one turn — the shape this
103
+ // tool's own description tells the model to use — the event loop
104
+ // interleaves and three of the four died. Found by running one.
87
105
  (event) => {
88
106
  this.listener?.(event);
107
+ // No id yet means nothing is waiting on this task: the caller
108
+ // does not hold the handle, so an idle bound cannot be running
109
+ // against it. There is no progress to report to anyone.
110
+ if (launched.id === undefined)
111
+ return;
89
112
  for (const notify of this.progressListeners)
90
- notify(task.taskId);
113
+ notify(launched.id);
91
114
  });
115
+ launched.id = task.taskId;
92
116
  this.trackedTaskIds.add(task.taskId);
93
117
  this.agentManager
94
118
  .waitForCompletion(task.taskId)
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namzu/sdk",
3
- "version": "12.0.0",
3
+ "version": "12.0.1",
4
4
  "description": "Open-source AI agent SDK with a built-in runtime. Nothing between you and your agents.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -0,0 +1,175 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import type { Agent } from '../../types/agent/core.js'
4
+ import type { AgentManagerContract } from '../../types/agent/manager.js'
5
+ import type {
6
+ AgentTask,
7
+ AgentTaskContext,
8
+ AgentTaskState,
9
+ SendMessageOptions,
10
+ } from '../../types/agent/task.js'
11
+ import type { AgentId, RunId, SessionId, TaskId, TenantId } from '../../types/ids/index.js'
12
+ import type { RunEventListener } from '../../types/run/events.js'
13
+ import type { ProjectId, ThreadId } from '../../types/session/ids.js'
14
+ import { LocalTaskGateway } from '../local.js'
15
+
16
+ /**
17
+ * A child that spoke before its own spawn resolved killed the launch.
18
+ *
19
+ * The progress tee handed to `sendMessage` read `task.taskId` — the `const`
20
+ * that the very same `await` assigns. So any run event emitted by the child
21
+ * before `sendMessage` returned reached that line inside the temporal dead
22
+ * zone and threw `Cannot access 'task' before initialization`, taking the whole
23
+ * `create_task` down with it.
24
+ *
25
+ * It survived review and a full unit suite because a single sequential launch
26
+ * usually resolves before the child says anything. A CONCURRENT fan-out does
27
+ * not — and that is the shape `create_task`'s own description tells the model
28
+ * to use. Observed live on the published package: four launches from one turn,
29
+ * three dead.
30
+ */
31
+
32
+ const RUN = 'run_tee' as RunId
33
+
34
+ /** Emits a run event DURING the spawn, before it resolves. */
35
+ class TalksDuringSpawn implements AgentManagerContract {
36
+ /** Kept so a test can make the child speak AFTER the spawn resolved too. */
37
+ private lastListener?: RunEventListener
38
+
39
+ constructor(private readonly emitsBeforeResolve: number) {}
40
+
41
+ /** The child says something once the caller holds its handle. */
42
+ speakNow(): void {
43
+ this.lastListener?.({ type: 'iteration_started', runId: RUN, iteration: 99 } as never)
44
+ }
45
+
46
+ async sendMessage(
47
+ options: SendMessageOptions,
48
+ _context: AgentTaskContext,
49
+ listener?: RunEventListener,
50
+ ): Promise<AgentTask> {
51
+ this.lastListener = listener
52
+ // The child is alive and streaming before the caller holds its handle.
53
+ for (let i = 0; i < this.emitsBeforeResolve; i += 1) {
54
+ listener?.({ type: 'iteration_started', runId: RUN, iteration: i } as never)
55
+ }
56
+ await Promise.resolve()
57
+ return {
58
+ taskId: 'task_spoke_early' as TaskId,
59
+ agentId: options.agentId,
60
+ agent: {} as Agent<never, never>,
61
+ childAbortController: new AbortController(),
62
+ context: {} as AgentTaskContext,
63
+ state: 'completed' as AgentTaskState,
64
+ pendingMessages: [],
65
+ createdAt: 1,
66
+ } as AgentTask
67
+ }
68
+
69
+ cancel(): void {}
70
+ cancelAll(): void {}
71
+ async continueTask(): Promise<void> {}
72
+ queueMessage(): void {}
73
+ drainMessages() {
74
+ return []
75
+ }
76
+ async waitForCompletion(): Promise<void> {}
77
+ getInstance(): AgentTask | undefined {
78
+ return undefined
79
+ }
80
+ listByParent(): AgentTask[] {
81
+ return []
82
+ }
83
+ listActive(): AgentTask[] {
84
+ return []
85
+ }
86
+ getState(): AgentTaskState | undefined {
87
+ return undefined
88
+ }
89
+ on(): void {}
90
+ off(): void {}
91
+ cleanup(): void {}
92
+ dispose(): void {}
93
+ }
94
+
95
+ function context(): AgentTaskContext {
96
+ return {
97
+ parentRunId: RUN,
98
+ parentAgentId: 'supervisor',
99
+ parentAbortController: new AbortController(),
100
+ depth: 0,
101
+ budgetTracker: { total: 100_000, remaining: 100_000 },
102
+ tenantId: 'tnt_t' as TenantId,
103
+ threadId: 'thd_t' as ThreadId,
104
+ sessionId: 'ses_t' as SessionId,
105
+ projectId: 'prj_t' as ProjectId,
106
+ parentActor: { kind: 'agent', agentId: 'supervisor' as AgentId, tenantId: 'tnt_t' as TenantId },
107
+ } as AgentTaskContext
108
+ }
109
+
110
+ describe('a launch survives a child that speaks before the spawn resolves', () => {
111
+ it('does not throw when an event arrives mid-spawn', async () => {
112
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(3), context())
113
+ // The listener is what makes this reproduce, and its absence is what
114
+ // made the bug invisible for so long: with no progress subscriber the
115
+ // loop body never runs, so `task.taskId` is never evaluated and the
116
+ // dead zone is never entered. `create_task` attaches one for the idle
117
+ // bound on every blocking launch, which is why it bit in production and
118
+ // not in any test.
119
+ gateway.onTaskProgress?.(() => {})
120
+
121
+ await expect(
122
+ gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' }),
123
+ ).resolves.toMatchObject({ taskId: 'task_spoke_early' })
124
+ })
125
+
126
+ it('still forwards those events to the host listener', async () => {
127
+ // The tee is what broke, not the forwarding. A host watching the child
128
+ // must not lose its early events to this fix.
129
+ const seen: string[] = []
130
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(3), context(), (e) => {
131
+ seen.push(e.type)
132
+ })
133
+
134
+ await gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' })
135
+
136
+ expect(seen).toEqual(['iteration_started', 'iteration_started', 'iteration_started'])
137
+ })
138
+
139
+ it('reports progress once the task has an id to report it against', async () => {
140
+ // Before the id exists nothing is waiting on the task — the caller does
141
+ // not hold the handle yet — so silence there is correct. What must work
142
+ // is everything after.
143
+ const manager = new TalksDuringSpawn(1)
144
+ const gateway = new LocalTaskGateway(manager, context())
145
+ const progressed: TaskId[] = []
146
+ gateway.onTaskProgress?.((id) => progressed.push(id))
147
+
148
+ await gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' })
149
+ // The one emitted mid-spawn is not attributed — there was no id yet.
150
+ expect(progressed).toEqual([])
151
+
152
+ // Now the child speaks with the handle already in the caller's hands,
153
+ // which is the case an idle bound is actually measuring.
154
+ manager.speakNow()
155
+
156
+ expect(progressed).toEqual(['task_spoke_early' as TaskId])
157
+ })
158
+
159
+ it('survives a concurrent fan-out, which is how this was found', async () => {
160
+ const gateway = new LocalTaskGateway(new TalksDuringSpawn(2), context())
161
+ // Same reason as above: the live failure came through the idle bound's
162
+ // subscriber, so a fan-out test without one would not reproduce the
163
+ // thing it is named after.
164
+ gateway.onTaskProgress?.(() => {})
165
+
166
+ const launched = await Promise.all(
167
+ [1, 2, 3, 4].map(() =>
168
+ gateway.createTask({ agentId: 'worker', prompt: 'go', workingDirectory: '/tmp' }),
169
+ ),
170
+ )
171
+
172
+ expect(launched).toHaveLength(4)
173
+ expect(launched.every((h) => h.taskId === 'task_spoke_early')).toBe(true)
174
+ })
175
+ })
@@ -60,6 +60,12 @@ export class LocalTaskGateway implements TaskGateway {
60
60
  }
61
61
 
62
62
  async createTask(options: CreateTaskOptions): Promise<TaskHandle> {
63
+ // Filled once the spawn resolves. A box rather than a bare binding
64
+ // because the assignment happens AFTER the `await` that the reader is
65
+ // passed into — see the progress tee below for why it cannot simply
66
+ // read the `task` const it is declared beside.
67
+ const launched: { id?: TaskId } = {}
68
+
63
69
  const task = await this.agentManager.sendMessage(
64
70
  {
65
71
  agentId: options.agentId,
@@ -109,12 +115,30 @@ export class LocalTaskGateway implements TaskGateway {
109
115
  // idle bound measures. The event itself is not forwarded — a
110
116
  // progress signal that carried the child's output would be a
111
117
  // second, undocumented way to read a worker's work.
118
+ //
119
+ // The id comes from `launched.id`, NOT from the `task` const
120
+ // below. This callback is handed to the very `await` that assigns
121
+ // `task`, so a child that emits anything before `sendMessage`
122
+ // resolves reached it inside the temporal dead zone and threw
123
+ // `Cannot access 'task' before initialization` — killing the launch
124
+ // outright.
125
+ //
126
+ // It survived because a single sequential launch usually resolves
127
+ // before the child says anything. A concurrent fan-out does not:
128
+ // with four `create_task` calls from one turn — the shape this
129
+ // tool's own description tells the model to use — the event loop
130
+ // interleaves and three of the four died. Found by running one.
112
131
  (event) => {
113
132
  this.listener?.(event)
114
- for (const notify of this.progressListeners) notify(task.taskId)
133
+ // No id yet means nothing is waiting on this task: the caller
134
+ // does not hold the handle, so an idle bound cannot be running
135
+ // against it. There is no progress to report to anyone.
136
+ if (launched.id === undefined) return
137
+ for (const notify of this.progressListeners) notify(launched.id)
115
138
  },
116
139
  )
117
140
 
141
+ launched.id = task.taskId
118
142
  this.trackedTaskIds.add(task.taskId)
119
143
 
120
144
  this.agentManager