@chances-ai/engine 27.0.0 → 28.0.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.
@@ -0,0 +1,262 @@
1
+ import { AppError, ErrorCode } from "@chances-ai/runtime";
2
+ import { DEFAULT_MAX_TURNS } from "./engine.js";
3
+ import { CREATE_TEAM_TOOL_NAME, DISMISS_WORKER_TOOL_NAME, LIST_WORKERS_TOOL_NAME, SEND_MESSAGE_TOOL_NAME, SPAWN_WORKER_TOOL_NAME, STOP_WORKER_TOOL_NAME, createChildAgentRuntime, } from "./task-tool.js";
4
+ /** Upper bound on a worker child's `maxTurns` — matches the one-shot `task`
5
+ * tool's `CHILD_MAX_TURNS_CEILING` so a worker can't wedge for hours. */
6
+ const WORKER_MAX_TURNS_CEILING = 50;
7
+ function strArg(args, key, required) {
8
+ const v = args[key];
9
+ if (v === undefined || v === null) {
10
+ if (required)
11
+ throw new AppError(ErrorCode.Tool, `Expected string arg "${key}"`);
12
+ return undefined;
13
+ }
14
+ if (typeof v !== "string") {
15
+ throw new AppError(ErrorCode.Tool, `Expected string arg "${key}"`);
16
+ }
17
+ return v;
18
+ }
19
+ /** A `to` target of the form `team:<id>` → the team id, else null. */
20
+ function teamTarget(to) {
21
+ return to.startsWith("team:") ? to.slice("team:".length) : null;
22
+ }
23
+ /**
24
+ * (7.8 §3.2) Build the coordinator's orchestration tool set:
25
+ * `spawn_worker` / `send_message` / `stop_worker` / `create_team` /
26
+ * `list_workers`. Registered on the COORDINATOR's main engine ONLY (coordinator
27
+ * mode) — a normal session never sees them, and the one-shot `task` stays the
28
+ * simple-delegation default. All orchestration ops are `isConcurrencySafe:false`
29
+ * (stateful side effects); only `list_workers` is read-only/parallel-safe.
30
+ */
31
+ export function createCoordinatorTools(deps) {
32
+ const reg = deps.workerRegistry;
33
+ const childMaxTurns = Math.min(Math.max(1, deps.maxTurns ?? DEFAULT_MAX_TURNS), WORKER_MAX_TURNS_CEILING);
34
+ const spawnWorker = {
35
+ name: SPAWN_WORKER_TOOL_NAME,
36
+ description: "Spawn a PERSISTENT worker (a child agent that stays alive so you can continue it with send_message, reusing its context). Unlike the one-shot `task` tool, the worker does not die after one reply — direct it to research/implement/verify, then `send_message` it follow-up work or `stop_worker` it if it goes the wrong way. Returns an agent_id you address it by. The worker has full tool access (it does the editing); YOU only direct.",
37
+ category: "integration",
38
+ parameters: {
39
+ type: "object",
40
+ properties: {
41
+ description: {
42
+ type: "string",
43
+ description: "Short label (1–3 words) for this worker. Shown in the permission prompt and the worker panel.",
44
+ },
45
+ prompt: {
46
+ type: "string",
47
+ description: "The worker's first instruction. Make it self-contained — include specific file paths, line numbers, and exactly what to do (you must understand the work before delegating it).",
48
+ },
49
+ subagent_type: {
50
+ type: "string",
51
+ description: "Optional persona from the agent catalog. Omit for a full-inheritance worker.",
52
+ },
53
+ team: {
54
+ type: "string",
55
+ description: "Optional team id (from create_team) to group this worker under, for broadcast / batch-stop.",
56
+ },
57
+ isolation: {
58
+ type: "string",
59
+ enum: ["worktree", "none"],
60
+ description: "'worktree' runs the worker in an isolated git worktree; 'none' (default) shares the working tree.",
61
+ },
62
+ context: {
63
+ type: "string",
64
+ enum: ["fork", "fresh"],
65
+ description: "'fresh' (default) = empty context. 'fork' = inherit a copy of THIS conversation's completed turns as background.",
66
+ },
67
+ },
68
+ required: ["prompt"],
69
+ },
70
+ isConcurrencySafe: () => false,
71
+ summarize: (args) => {
72
+ const prompt = strArg(args, "prompt", true);
73
+ const desc = strArg(args, "description", false);
74
+ const head = prompt.length > 80 ? prompt.slice(0, 80) + "…" : prompt;
75
+ return `spawn_worker${desc ? `[${desc}]` : ""}: ${head}`;
76
+ },
77
+ async execute(args, ctx) {
78
+ const prompt = strArg(args, "prompt", true);
79
+ const team = strArg(args, "team", false);
80
+ if (team !== undefined && !reg.hasTeam(team)) {
81
+ return { ok: false, output: `Unknown team '${team}'. Create it first with create_team.` };
82
+ }
83
+ const built = await createChildAgentRuntime(deps, {
84
+ prompt,
85
+ subagentType: strArg(args, "subagent_type", false),
86
+ isolation: strArg(args, "isolation", false),
87
+ context: strArg(args, "context", false),
88
+ }, childMaxTurns, ctx.signal);
89
+ if (!built.ok)
90
+ return { ok: false, output: built.output };
91
+ try {
92
+ const { agentId } = reg.spawn({
93
+ teamId: team,
94
+ firstPrompt: built.firstPrompt,
95
+ runtime: built.runtime,
96
+ });
97
+ return {
98
+ ok: true,
99
+ output: [
100
+ `(worker spawned) agent_id=${agentId} name=${built.runtime.agentName}${team ? ` team=${team}` : ""}`,
101
+ "Continue it with send_message(to: \"" + agentId + "\", message: …); stop it with stop_worker. You will get its result as a <task-notification> on a future turn.",
102
+ ].join("\n"),
103
+ };
104
+ }
105
+ catch (e) {
106
+ // Capacity refusal etc. → AppError(Tool) folded to ok:false.
107
+ if (e instanceof AppError) {
108
+ // worktree was created inside createChildAgentRuntime; close-on-failure
109
+ // happens via finalize() at the registry's next teardown — but since
110
+ // spawn never registered the worker, finalize the runtime now.
111
+ await built.runtime.finalize().catch(() => { });
112
+ await built.runtime.drainPty().catch(() => { });
113
+ return { ok: false, output: e.message };
114
+ }
115
+ throw e;
116
+ }
117
+ },
118
+ };
119
+ const sendMessage = {
120
+ name: SEND_MESSAGE_TOOL_NAME,
121
+ description: "Send a follow-up instruction to a live worker, CONTINUING it on its existing context (it remembers what it already read/did). `to` is an agent_id, a worker name, or `team:<id>` to broadcast to every worker in a team. If the worker is busy the message queues (FIFO); if it's idle/stopped it resumes. Synthesize the worker's prior findings into a concrete spec before sending — never delegate understanding back to it.",
122
+ category: "integration",
123
+ parameters: {
124
+ type: "object",
125
+ properties: {
126
+ to: { type: "string", description: "agent_id, worker name, or 'team:<id>' to broadcast." },
127
+ message: { type: "string", description: "The follow-up instruction (self-contained, with specifics)." },
128
+ },
129
+ required: ["to", "message"],
130
+ },
131
+ isConcurrencySafe: () => false,
132
+ summarize: (args) => {
133
+ const to = strArg(args, "to", true);
134
+ const msg = strArg(args, "message", true);
135
+ const head = msg.length > 60 ? msg.slice(0, 60) + "…" : msg;
136
+ return `send_message → ${to}: ${head}`;
137
+ },
138
+ async execute(args) {
139
+ const to = strArg(args, "to", true);
140
+ const message = strArg(args, "message", true);
141
+ const team = teamTarget(to);
142
+ if (team !== null) {
143
+ if (!reg.hasTeam(team))
144
+ return { ok: false, output: `Unknown team '${team}'.` };
145
+ const n = reg.broadcast(team, message);
146
+ return { ok: true, output: `Broadcast to ${n} worker(s) in team ${team}.` };
147
+ }
148
+ const delivered = reg.send(to, message);
149
+ if (!delivered) {
150
+ return {
151
+ ok: false,
152
+ output: `No live worker matches '${to}' (it may have been stopped-and-evicted or never existed). Use list_workers, or spawn_worker a fresh one.`,
153
+ };
154
+ }
155
+ return { ok: true, output: `Message queued for worker ${to}.` };
156
+ },
157
+ };
158
+ const stopWorker = {
159
+ name: STOP_WORKER_TOOL_NAME,
160
+ description: "Stop a worker that's going the wrong way: cancels its in-flight work but KEEPS it resumable (a later send_message continues it on the same context). `to` is an agent_id, a worker name, or `team:<id>` to stop every worker in a team.",
161
+ category: "integration",
162
+ parameters: {
163
+ type: "object",
164
+ properties: {
165
+ to: { type: "string", description: "agent_id, worker name, or 'team:<id>'." },
166
+ },
167
+ required: ["to"],
168
+ },
169
+ isConcurrencySafe: () => false,
170
+ summarize: (args) => `stop_worker → ${strArg(args, "to", true)}`,
171
+ async execute(args) {
172
+ const to = strArg(args, "to", true);
173
+ const team = teamTarget(to);
174
+ if (team !== null) {
175
+ if (!reg.hasTeam(team))
176
+ return { ok: false, output: `Unknown team '${team}'.` };
177
+ const n = reg.stopTeam(team);
178
+ return { ok: true, output: `Stopped ${n} worker(s) in team ${team}.` };
179
+ }
180
+ const stopped = reg.stop(to);
181
+ if (!stopped)
182
+ return { ok: false, output: `No live worker matches '${to}'.` };
183
+ return { ok: true, output: `Stopped worker ${to} (resumable with send_message).` };
184
+ },
185
+ };
186
+ const dismissWorker = {
187
+ name: DISMISS_WORKER_TOOL_NAME,
188
+ description: "Permanently dismiss a worker you are DONE with — it is closed (its worktree/PTY finalized) and its capacity slot is freed immediately. Unlike stop_worker (which keeps the worker resumable), a dismissed worker cannot be continued. Use this to reclaim a slot when you hit the worker capacity limit and no longer need a finished worker. `to` is an agent_id, a worker name, or `team:<id>` to dismiss every worker in a team.",
189
+ category: "integration",
190
+ parameters: {
191
+ type: "object",
192
+ properties: {
193
+ to: { type: "string", description: "agent_id, worker name, or 'team:<id>'." },
194
+ },
195
+ required: ["to"],
196
+ },
197
+ isConcurrencySafe: () => false,
198
+ summarize: (args) => `dismiss_worker → ${strArg(args, "to", true)}`,
199
+ async execute(args) {
200
+ const to = strArg(args, "to", true);
201
+ const team = teamTarget(to);
202
+ if (team !== null) {
203
+ if (!reg.hasTeam(team))
204
+ return { ok: false, output: `Unknown team '${team}'.` };
205
+ const n = await reg.dismissTeam(team);
206
+ return { ok: true, output: `Dismissed ${n} worker(s) in team ${team} (slots freed).` };
207
+ }
208
+ // Resolve a name → agentId so a name target works (close() is by agentId).
209
+ const match = reg.listWorkers().find((w) => w.agentId === to || w.name === to);
210
+ const dismissed = match ? await reg.close(match.agentId) : false;
211
+ if (!dismissed)
212
+ return { ok: false, output: `No live worker matches '${to}'.` };
213
+ return { ok: true, output: `Dismissed worker ${to} (slot freed; not resumable).` };
214
+ },
215
+ };
216
+ const createTeam = {
217
+ name: CREATE_TEAM_TOOL_NAME,
218
+ description: "Create a team to group related workers. Returns a team_id; pass it as spawn_worker's `team`, then send_message/stop_worker with `to: \"team:<id>\"` to broadcast / batch-stop.",
219
+ category: "integration",
220
+ parameters: {
221
+ type: "object",
222
+ properties: {
223
+ name: { type: "string", description: "Human-readable team label." },
224
+ },
225
+ required: ["name"],
226
+ },
227
+ isConcurrencySafe: () => false,
228
+ summarize: (args) => `create_team: ${strArg(args, "name", true)}`,
229
+ async execute(args) {
230
+ const name = strArg(args, "name", true);
231
+ const { teamId } = reg.createTeam(name);
232
+ return { ok: true, output: `(team created) team_id=${teamId} name=${name}` };
233
+ },
234
+ };
235
+ const listWorkers = {
236
+ name: LIST_WORKERS_TOOL_NAME,
237
+ description: "List your live workers and their status (running / idle / stopped / failed), optionally filtered by team. Use it to see what's in flight before directing more work.",
238
+ category: "file-read",
239
+ parameters: {
240
+ type: "object",
241
+ properties: {
242
+ team: { type: "string", description: "Optional team id to filter by." },
243
+ },
244
+ },
245
+ isConcurrencySafe: () => true,
246
+ summarize: (args) => {
247
+ const team = strArg(args, "team", false);
248
+ return team ? `list_workers (team ${team})` : "list_workers";
249
+ },
250
+ async execute(args) {
251
+ const team = strArg(args, "team", false);
252
+ const workers = reg.listWorkers(team);
253
+ if (workers.length === 0) {
254
+ return { ok: true, output: team ? `No workers in team ${team}.` : "No live workers." };
255
+ }
256
+ const lines = workers.map((w) => `- ${w.agentId} "${w.name}" [${w.status}]${w.teamId ? ` team=${w.teamId}` : ""} turns=${w.turnsRun} queued=${w.queued} — ${w.lastMessageHead}`);
257
+ return { ok: true, output: lines.join("\n") };
258
+ },
259
+ };
260
+ return [spawnWorker, sendMessage, stopWorker, dismissWorker, createTeam, listWorkers];
261
+ }
262
+ //# sourceMappingURL=coordinator-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-tools.js","sourceRoot":"","sources":["../../src/core/coordinator-tools.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { type ApprovalMode, type AsyncTaskRegistry, type BoundaryInjectionQueue, type CancellationToken, type EventBus, type TaskNotification, ModelSelection } from "@chances-ai/runtime";
1
+ import { type ApprovalMode, type AsyncTaskRegistry, type BoundaryInjectionQueue, type NotificationSource, type CancellationToken, type EventBus, type TaskNotification, ModelSelection } from "@chances-ai/runtime";
2
2
  import { type Message, type ModelRouter, type Route, type RetryConfig, type ToolCallRequest, type ToolDefinition } from "../ai/index.js";
3
3
  import type { SessionManager } from "../session/index.js";
4
4
  import type { MemoryStore } from "../memory/index.js";
@@ -126,6 +126,15 @@ export interface AgentEngineOptions {
126
126
  * backgrounds never accidentally orphan into a process exit.
127
127
  */
128
128
  backgroundTasks?: AsyncTaskRegistry;
129
+ /**
130
+ * (7.8 §3.4) Persistent coordinator worker registry. A SECOND
131
+ * {@link NotificationSource} the engine drains alongside `backgroundTasks` at
132
+ * every turn boundary — a worker's per-message result reaches the coordinator
133
+ * as a `<task-notification>` exactly like a background subagent's. Undefined on
134
+ * a normal (non-coordinator) session (drain is a no-op). Per-session in the
135
+ * isolated serve path, same as `backgroundTasks`.
136
+ */
137
+ workerRegistry?: NotificationSource;
129
138
  /**
130
139
  * (7.7 §4) Per-session steering queue. When provided, the engine drains it at
131
140
  * every turn boundary (turn-top + after each tool batch) and injects the
@@ -273,14 +282,19 @@ interface TurnState {
273
282
  * `injected*Ids` collections are mutated in place (append-only) by the loop and
274
283
  * its hooks; the engine reads them back after the loop to persist + acknowledge.
275
284
  */
285
+ /** (7.8 §3.4 — codex R2 M1) Per-source injected-notification tracking. Keyed by
286
+ * the `NotificationSource` object so each source's ack set is independent — a
287
+ * cross-source id collision can't shadow or mis-ack a note. Turn-local. */
288
+ export type InjectedNotifs = Map<NotificationSource, Set<string>>;
276
289
  export interface LoopRun {
277
290
  turnId: string;
278
291
  system: string;
279
292
  toolDefs: ToolDefinition[];
280
293
  /** Accumulator: assistant/tool/steering messages appended as the loop runs. */
281
294
  turnMessages: Message[];
282
- /** Notification ids already injected (turn-top + mid-turn) ack after persist. */
283
- injectedNotifIds: Set<string>;
295
+ /** (codex R2 M1) Notification ids already injected this turn, keyed PER source
296
+ * (turn-top + mid-turn) — ack the right source's own ids after persist. */
297
+ injectedNotifIds: InjectedNotifs;
284
298
  /** Steering ids already injected — ack after persist. */
285
299
  injectedSteerIds: string[];
286
300
  /** Soft turn ceiling for THIS run (already clamped to the hard cap). */
@@ -315,7 +329,7 @@ export interface AgentLoopHooks {
315
329
  /** Iteration-boundary injections: steering the user queued mid-turn + bg-task
316
330
  * notifications that completed mid-turn (peek-not-drain; ids recorded for the
317
331
  * post-persist ack). pi `getSteeringMessages`. */
318
- getBoundaryMessages(injectedNotifIds: Set<string>, injectedSteerIds: string[]): Message[];
332
+ getBoundaryMessages(injectedNotifIds: InjectedNotifs, injectedSteerIds: string[]): Message[];
319
333
  /** When the model returned NO tool calls: a `user`-role reminder to finish
320
334
  * open todos (loop continues) or `null` to let the turn resolve. The inverse
321
335
  * of pi's `shouldStopAfterTurn`. */
@@ -347,8 +361,24 @@ export declare class AgentEngine {
347
361
  * render each as a user message, and append its id to `injectedSteerIds` for
348
362
  * post-persist ack. Peek-not-drain: a cancelled turn re-delivers. */
349
363
  private drainSteering;
350
- /** (7.7 §4) Iteration-boundary drain: background-task notifications that
351
- * arrived mid-turn (combined render, same as turn-top) PLUS steering. Both
364
+ /** (7.8 §3.4) The notification sources the engine drains at turn boundaries:
365
+ * background subagent tasks AND persistent coordinator workers. Both
366
+ * implement {@link NotificationSource}; the engine renders their pending
367
+ * `<task-notification>`s together (FIFO within each) and acks EACH source's
368
+ * own ids after persist — never broadcasting a global id list (codex R1-§5). */
369
+ private notificationSources;
370
+ /** (7.7 §4 / 7.8 §3.4 — codex R2 M1) Collect notifications not yet injected this
371
+ * turn, across ALL sources, recording each into its OWN per-source injected set
372
+ * as it's taken. The injected tracking is keyed by `NotificationSource` (NOT a
373
+ * flat id set) so a hypothetical cross-source id collision can never make one
374
+ * source's note shadow another's, nor ack a note that was never injected. */
375
+ private collectFreshNotifications;
376
+ /** (7.7 §4 / 7.8 — codex R2 M1) Ack AFTER persist: each source acks EXACTLY the
377
+ * ids injected FROM it this turn (per-source set), never a merged global list.
378
+ * A cancelled turn (no ack) re-delivers; injected ids are turn-local. */
379
+ private ackNotifications;
380
+ /** (7.7 §4) Iteration-boundary drain: notifications that arrived mid-turn
381
+ * (combined render across all sources, same as turn-top) PLUS steering. Both
352
382
  * peek-not-drain; ids recorded for post-persist ack. */
353
383
  private drainBoundaryInjections;
354
384
  /** (7.7 §5.3) Build an incomplete-todos reminder when the model stopped with
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
@@ -112,19 +112,62 @@ export class AgentEngine {
112
112
  }
113
113
  return out;
114
114
  }
115
- /** (7.7 §4) Iteration-boundary drain: background-task notifications that
116
- * arrived mid-turn (combined render, same as turn-top) PLUS steering. Both
115
+ /** (7.8 §3.4) The notification sources the engine drains at turn boundaries:
116
+ * background subagent tasks AND persistent coordinator workers. Both
117
+ * implement {@link NotificationSource}; the engine renders their pending
118
+ * `<task-notification>`s together (FIFO within each) and acks EACH source's
119
+ * own ids after persist — never broadcasting a global id list (codex R1-§5). */
120
+ notificationSources() {
121
+ const out = [];
122
+ if (this.opts.backgroundTasks)
123
+ out.push(this.opts.backgroundTasks);
124
+ if (this.opts.workerRegistry)
125
+ out.push(this.opts.workerRegistry);
126
+ return out;
127
+ }
128
+ /** (7.7 §4 / 7.8 §3.4 — codex R2 M1) Collect notifications not yet injected this
129
+ * turn, across ALL sources, recording each into its OWN per-source injected set
130
+ * as it's taken. The injected tracking is keyed by `NotificationSource` (NOT a
131
+ * flat id set) so a hypothetical cross-source id collision can never make one
132
+ * source's note shadow another's, nor ack a note that was never injected. */
133
+ collectFreshNotifications(injected) {
134
+ const out = [];
135
+ for (const src of this.notificationSources()) {
136
+ let set = injected.get(src);
137
+ if (!set) {
138
+ set = new Set();
139
+ injected.set(src, set);
140
+ }
141
+ for (const n of src.peekPendingNotifications()) {
142
+ if (set.has(n.taskId))
143
+ continue;
144
+ out.push(n);
145
+ set.add(n.taskId); // record immediately so the boundary drain won't re-inject
146
+ }
147
+ }
148
+ return out;
149
+ }
150
+ /** (7.7 §4 / 7.8 — codex R2 M1) Ack AFTER persist: each source acks EXACTLY the
151
+ * ids injected FROM it this turn (per-source set), never a merged global list.
152
+ * A cancelled turn (no ack) re-delivers; injected ids are turn-local. */
153
+ ackNotifications(injected) {
154
+ for (const src of this.notificationSources()) {
155
+ const set = injected.get(src);
156
+ if (set && set.size > 0)
157
+ src.acknowledgeNotifications([...set]);
158
+ }
159
+ }
160
+ /** (7.7 §4) Iteration-boundary drain: notifications that arrived mid-turn
161
+ * (combined render across all sources, same as turn-top) PLUS steering. Both
117
162
  * peek-not-drain; ids recorded for post-persist ack. */
118
163
  drainBoundaryInjections(injectedNotifIds, injectedSteerIds) {
119
164
  const out = [];
120
- const fresh = (this.opts.backgroundTasks?.peekPendingNotifications() ?? []).filter((n) => !injectedNotifIds.has(n.taskId));
165
+ const fresh = this.collectFreshNotifications(injectedNotifIds);
121
166
  if (fresh.length > 0) {
122
167
  out.push({
123
168
  role: "user",
124
169
  content: [{ type: "text", text: fresh.map(renderTaskNotificationXml).join("\n") }],
125
170
  });
126
- for (const n of fresh)
127
- injectedNotifIds.add(n.taskId);
128
171
  }
129
172
  out.push(...this.drainSteering(injectedSteerIds));
130
173
  return out;
@@ -211,7 +254,7 @@ export class AgentEngine {
211
254
  return this.runTurnImpl(prompt, token, opts.expandMentions !== false, opts.trustedContext);
212
255
  }
213
256
  async runTurnImpl(prompt, token, expandMentions, trustedContext) {
214
- const { tools, session, plugins, backgroundTasks } = this.opts;
257
+ const { tools, session, plugins } = this.opts;
215
258
  const turnId = createId("turn");
216
259
  // (3.6) Carry the active session id on `turn:start` so the OTel
217
260
  // exporter can stamp `chances.gen_ai.session.id` correctly across
@@ -240,8 +283,10 @@ export class AgentEngine {
240
283
  // persisted to `session.messages()`. Acknowledgement (queue removal)
241
284
  // happens immediately after `session.appendTurn(turnMessages)`
242
285
  // succeeds.
243
- const notifications = backgroundTasks?.peekPendingNotifications() ?? [];
244
- const notificationIds = notifications.map((n) => n.taskId);
286
+ // (7.8 §3.4 — codex R2 M1) Collect across ALL notification sources (background
287
+ // tasks + persistent workers), recording each into its per-source injected set.
288
+ const injectedNotifIds = new Map();
289
+ const notifications = this.collectFreshNotifications(injectedNotifIds);
245
290
  const turnMessages = [];
246
291
  if (notifications.length > 0) {
247
292
  const xml = notifications.map(renderTaskNotificationXml).join("\n");
@@ -270,10 +315,9 @@ export class AgentEngine {
270
315
  turnMessages.push({ role: "user", content: [{ type: "text", text: trustedContext }] });
271
316
  }
272
317
  turnMessages.push({ role: "user", content: [{ type: "text", text: prompt }] });
273
- // (7.7 §4) Drain any steering queued before this turn started, plus track
274
- // which notifications/steering ids have been injected so the iteration-
275
- // boundary drain (and the post-persist ack) don't double-count.
276
- const injectedNotifIds = new Set(notificationIds);
318
+ // (7.7 §4) Drain any steering queued before this turn started. `injectedNotifIds`
319
+ // (above) tracks per-source notification ids; `injectedSteerIds` tracks steering
320
+ // — both so the iteration-boundary drain + post-persist ack don't double-count.
277
321
  const injectedSteerIds = [];
278
322
  turnMessages.push(...this.drainSteering(injectedSteerIds));
279
323
  const result = { text: "", inputTokens: 0, outputTokens: 0, costUsd: 0 };
@@ -300,9 +344,9 @@ export class AgentEngine {
300
344
  // a cancellation between peek and persist leaves both queues intact (the
301
345
  // next turn re-delivers). `injectedNotifIds` covers turn-top AND mid-turn
302
346
  // notifications; `injectedSteerIds` covers all injected steering.
303
- if (injectedNotifIds.size > 0) {
304
- backgroundTasks?.acknowledgeNotifications([...injectedNotifIds]);
305
- }
347
+ // (7.8 §3.4) Ack per source, each only its own injected ids (no global
348
+ // broadcast) — drains across backgroundTasks + workerRegistry uniformly.
349
+ this.ackNotifications(injectedNotifIds);
306
350
  if (injectedSteerIds.length > 0) {
307
351
  this.opts.steering?.acknowledge(injectedSteerIds);
308
352
  }