@myagentroam/agent 0.9.62 → 0.9.64

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 (119) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/dist/cli/codex-auth-file.js +268 -3
  4. package/dist/cli/main.js +427 -2
  5. package/dist/error.js +66 -2
  6. package/dist/host/contracts.js +20 -2
  7. package/dist/host/local-agent-host.js +146 -2
  8. package/dist/host/local-process-executor.js +39 -2
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +12 -2
  11. package/dist/model/anthropic-messages.js +474 -2
  12. package/dist/model/anthropic-thinking.js +33 -2
  13. package/dist/model/catalog-file.js +88 -2
  14. package/dist/model/configuration.js +137 -2
  15. package/dist/model/contracts.d.ts +2 -0
  16. package/dist/model/contracts.js +1 -2
  17. package/dist/model/http.js +529 -2
  18. package/dist/model/openai-responses.d.ts +2 -0
  19. package/dist/model/openai-responses.js +1602 -2
  20. package/dist/model/runtime-credential.js +35 -2
  21. package/dist/model/sse.js +199 -2
  22. package/dist/prompts/compact.js +25 -2
  23. package/dist/prompts/core.js +37 -2
  24. package/dist/prompts/execution-budget.js +10 -2
  25. package/dist/prompts/index.d.ts +3 -2
  26. package/dist/prompts/index.js +47 -2
  27. package/dist/prompts/modes.js +25 -2
  28. package/dist/prompts/output.js +1 -2
  29. package/dist/prompts/resources.d.ts +1 -1
  30. package/dist/prompts/resources.js +31 -2
  31. package/dist/prompts/subagent.d.ts +6 -0
  32. package/dist/prompts/subagent.js +29 -2
  33. package/dist/prompts/title.js +9 -2
  34. package/dist/prompts/workflow.js +32 -2
  35. package/dist/runtime/async-queue.js +62 -2
  36. package/dist/runtime/builtin-skills.js +19 -2
  37. package/dist/runtime/byted-ark-imagegen-skill.js +18 -2
  38. package/dist/runtime/codex-imagegen-skill.js +35 -2
  39. package/dist/runtime/compact.js +225 -2
  40. package/dist/runtime/context-gc-runtime.d.ts +35 -0
  41. package/dist/runtime/context-gc-runtime.js +163 -0
  42. package/dist/runtime/context-gc-tokens.d.ts +24 -0
  43. package/dist/runtime/context-gc-tokens.js +83 -0
  44. package/dist/runtime/context-gc.d.ts +66 -0
  45. package/dist/runtime/context-gc.js +909 -0
  46. package/dist/runtime/context-projection.d.ts +23 -0
  47. package/dist/runtime/context-projection.js +203 -0
  48. package/dist/runtime/environment-context.js +41 -2
  49. package/dist/runtime/execution-policy.js +13 -2
  50. package/dist/runtime/instructions.js +298 -2
  51. package/dist/runtime/normalize.js +40 -2
  52. package/dist/runtime/plan-output.js +95 -2
  53. package/dist/runtime/rollout-budget.d.ts +2 -0
  54. package/dist/runtime/rollout-budget.js +92 -2
  55. package/dist/runtime/token-budget.d.ts +2 -0
  56. package/dist/runtime/token-budget.js +44 -2
  57. package/dist/sdk/agent.js +1804 -2
  58. package/dist/sdk/types.d.ts +15 -5
  59. package/dist/sdk/types.js +1 -2
  60. package/dist/session/catalog.d.ts +5 -4
  61. package/dist/session/catalog.js +133 -2
  62. package/dist/session/context-gc-checkpoint.d.ts +148 -0
  63. package/dist/session/context-gc-checkpoint.js +665 -0
  64. package/dist/session/context-gc-replacements.d.ts +52 -0
  65. package/dist/session/context-gc-replacements.js +226 -0
  66. package/dist/session/context-gc-subagent.d.ts +43 -0
  67. package/dist/session/context-gc-subagent.js +267 -0
  68. package/dist/session/jsonl-store.d.ts +42 -4
  69. package/dist/session/jsonl-store.js +1657 -2
  70. package/dist/subagent/scheduler.d.ts +35 -2
  71. package/dist/subagent/scheduler.js +249 -2
  72. package/dist/subagent/session-controller.d.ts +94 -0
  73. package/dist/subagent/session-controller.js +675 -0
  74. package/dist/tools/agent-cancel.js +28 -2
  75. package/dist/tools/{task.d.ts → agent-message.d.ts} +2 -2
  76. package/dist/tools/agent-message.js +51 -0
  77. package/dist/tools/agent-start.js +84 -2
  78. package/dist/tools/agent-wait.js +53 -2
  79. package/dist/tools/apply-patch.js +478 -2
  80. package/dist/tools/code-mode.js +370 -2
  81. package/dist/tools/codex-image-gen.js +312 -2
  82. package/dist/tools/exec.js +455 -2
  83. package/dist/tools/execution-limits.d.ts +1 -0
  84. package/dist/tools/execution-limits.js +44 -2
  85. package/dist/tools/local-registry.js +233 -2
  86. package/dist/tools/local-workspace-tools.js +28 -2
  87. package/dist/tools/question.js +124 -2
  88. package/dist/tools/read-context.d.ts +16 -0
  89. package/dist/tools/read-context.js +85 -0
  90. package/dist/tools/read.d.ts +13 -0
  91. package/dist/tools/read.js +294 -2
  92. package/dist/tools/registry.js +76 -2
  93. package/dist/tools/runtime-context.d.ts +9 -2
  94. package/dist/tools/runtime-context.js +1 -2
  95. package/dist/tools/runtime.js +21 -2
  96. package/dist/tools/scheduler.js +41 -2
  97. package/dist/tools/shared/path-resolver.js +133 -2
  98. package/dist/tools/skill.js +37 -2
  99. package/dist/tools/todo-read.js +10 -2
  100. package/dist/tools/todo-write.js +65 -2
  101. package/dist/tools/todo.js +67 -2
  102. package/dist/tools/view-image.js +81 -2
  103. package/dist/tools/web-fetch.js +11 -2
  104. package/dist/tools/web-search.js +14 -2
  105. package/package.json +9 -7
  106. package/README.md +0 -3
  107. package/dist/runtime/prompt.d.ts +0 -9
  108. package/dist/runtime/prompt.js +0 -2
  109. package/dist/tools/shared/secret-redaction.d.ts +0 -4
  110. package/dist/tools/shared/secret-redaction.js +0 -2
  111. package/dist/tools/task-cancel.d.ts +0 -7
  112. package/dist/tools/task-cancel.js +0 -2
  113. package/dist/tools/task-output.d.ts +0 -7
  114. package/dist/tools/task-output.js +0 -2
  115. package/dist/tools/task-tools.d.ts +0 -36
  116. package/dist/tools/task-tools.js +0 -2
  117. package/dist/tools/task.js +0 -2
  118. package/dist/tools/todo-tools.d.ts +0 -25
  119. package/dist/tools/todo-tools.js +0 -2
@@ -0,0 +1,675 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { MarAgentError, describeMarAgentError } from '../error.js';
3
+ import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
4
+ export class SubagentSessionController {
5
+ parentSessionId;
6
+ rootSessionId;
7
+ backend;
8
+ limits;
9
+ #agents = new Map();
10
+ #queue = [];
11
+ #running = 0;
12
+ #disposed = false;
13
+ #createdInExecution = 0;
14
+ #execution;
15
+ constructor(parentSessionId, rootSessionId, backend, limits = { maxTasks: 8, maxConcurrent: 4 }) {
16
+ this.parentSessionId = parentSessionId;
17
+ this.rootSessionId = rootSessionId;
18
+ this.backend = backend;
19
+ this.limits = limits;
20
+ }
21
+ static async open(input) {
22
+ const controller = new SubagentSessionController(input.parentSessionId, input.rootSessionId, input.backend, input.limits);
23
+ for (const session of await input.backend.listChildren(input.parentSessionId))
24
+ controller.#agents.set(session.id, restoreAgentState(session));
25
+ return controller;
26
+ }
27
+ async beginParentExecution(execution) {
28
+ if (this.#disposed)
29
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_LIMIT', 'Subagent controller is disposed.');
30
+ this.#execution = execution;
31
+ this.#createdInExecution = 0;
32
+ for (const state of this.#agents.values())
33
+ await this.#publishTerminal(state);
34
+ }
35
+ endParentExecution(executionId) {
36
+ if (this.#execution?.executionId === executionId)
37
+ this.#execution = undefined;
38
+ }
39
+ async run(input, options = {}) {
40
+ options.signal?.throwIfAborted();
41
+ const execution = this.#requireExecution();
42
+ validateTaskInput(input);
43
+ if (this.#createdInExecution >= this.limits.maxTasks)
44
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_LIMIT', 'Subagent task limit was reached.');
45
+ const source = {
46
+ type: 'subagent',
47
+ parentSessionId: this.parentSessionId,
48
+ rootSessionId: this.rootSessionId,
49
+ parentExecutionId: execution.executionId,
50
+ role: input.role,
51
+ description: input.description.trim()
52
+ };
53
+ const created = await this.backend.createSession({
54
+ title: input.description.trim(),
55
+ source
56
+ });
57
+ try {
58
+ if (this.#disposed)
59
+ throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
60
+ options.signal?.throwIfAborted();
61
+ }
62
+ catch (error) {
63
+ await this.#deleteUnstartedSession(created.id, error);
64
+ }
65
+ const state = {
66
+ agentId: created.id,
67
+ description: source.description,
68
+ role: input.role,
69
+ status: 'queued',
70
+ changedFiles: [],
71
+ verification: [],
72
+ evidence: [],
73
+ terminalPending: false
74
+ };
75
+ this.#createdInExecution++;
76
+ this.#agents.set(state.agentId, state);
77
+ const knownModel = execution.models.get(input.modelId ?? execution.modelId);
78
+ if (knownModel) {
79
+ state.modelId = knownModel.id;
80
+ state.modelName = knownModel.name;
81
+ }
82
+ try {
83
+ const selected = resolveModel(execution, input);
84
+ state.modelId = selected.id;
85
+ state.modelName = selected.name;
86
+ state.reasoningEffort = selected.reasoningEffort;
87
+ }
88
+ catch (error) {
89
+ this.#setFailure(state, error);
90
+ }
91
+ try {
92
+ await options.onCreated?.(this.#snapshot(state));
93
+ if (this.#disposed)
94
+ throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
95
+ options.signal?.throwIfAborted();
96
+ }
97
+ catch (error) {
98
+ await this.#deleteUnstartedSession(state.agentId, error);
99
+ }
100
+ if (state.status === 'failed') {
101
+ await this.#publishTerminal(state);
102
+ return this.#snapshot(state);
103
+ }
104
+ this.#queueTurn(state, input.prompt, execution.startTurn, []);
105
+ const detachParentAbort = !input.background && options.signal
106
+ ? linkAbort(options.signal, () => {
107
+ void this.cancel(state.agentId);
108
+ })
109
+ : undefined;
110
+ try {
111
+ if (!input.background)
112
+ await state.turn.completion;
113
+ return this.#snapshot(state);
114
+ }
115
+ finally {
116
+ detachParentAbort?.();
117
+ }
118
+ }
119
+ async output(agentId, options = {}) {
120
+ options.signal?.throwIfAborted();
121
+ const state = await this.#find(agentId);
122
+ if (options.waitMs !== undefined &&
123
+ (!Number.isInteger(options.waitMs) ||
124
+ options.waitMs < TOOL_EXECUTION_LIMITS.agentWaitMinWaitMs ||
125
+ options.waitMs > TOOL_EXECUTION_LIMITS.agentWaitMaxWaitMs))
126
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Task wait is invalid.');
127
+ const waitMs = options.waitMs === undefined || options.waitMs === 0
128
+ ? undefined
129
+ : Math.max(options.waitMs, TOOL_EXECUTION_LIMITS.agentWaitWaitFloorMs);
130
+ if (waitMs !== undefined &&
131
+ state.turn &&
132
+ (state.status === 'queued' || state.status === 'running'))
133
+ await waitForCompletion(state.turn.completion, waitMs, options.signal);
134
+ return this.#snapshot(state);
135
+ }
136
+ async message(agentId, message, options = {}) {
137
+ const execution = this.#requireExecution();
138
+ if (typeof message !== 'string' ||
139
+ message.trim().length === 0 ||
140
+ Buffer.byteLength(message) > TOOL_EXECUTION_LIMITS.subagentMessageMaxBytes ||
141
+ (options.delivery !== undefined &&
142
+ options.delivery !== 'append' &&
143
+ options.delivery !== 'replace'))
144
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Agent message input is invalid.');
145
+ const state = await this.#find(agentId);
146
+ return this.#control(state, async () => {
147
+ this.#requireExecution();
148
+ options.signal?.throwIfAborted();
149
+ // Resolve before cancelling so an unavailable model does not stop valid work.
150
+ const selected = resolveExistingModel(execution, state);
151
+ if (options.delivery === 'replace')
152
+ await this.#cancelState(state);
153
+ this.#requireExecution();
154
+ options.signal?.throwIfAborted();
155
+ const turnId = state.turn?.turnId ?? randomUUID();
156
+ const receipt = { messageId: randomUUID(), turnId, deliveryStatus: 'accepted' };
157
+ const mutableReceipt = receipt;
158
+ const delivery = {
159
+ messageId: receipt.messageId,
160
+ applied: () => {
161
+ mutableReceipt.deliveryStatus = 'applied';
162
+ }
163
+ };
164
+ if (state.status === 'running' && state.turn) {
165
+ const turn = state.turn;
166
+ if (!turn.handle)
167
+ await turn.handleReady;
168
+ this.#requireExecution();
169
+ options.signal?.throwIfAborted();
170
+ if (state.status === 'running' &&
171
+ state.turn === turn &&
172
+ turn.handle?.enqueueMessage(message, delivery)) {
173
+ state.receipt = mutableReceipt;
174
+ return this.#snapshot(state);
175
+ }
176
+ await turn.completion;
177
+ }
178
+ else if (state.status === 'queued' && state.turn) {
179
+ state.turn.pendingMessages.push({ text: message, delivery });
180
+ state.receipt = mutableReceipt;
181
+ return this.#snapshot(state);
182
+ }
183
+ this.#requireExecution();
184
+ options.signal?.throwIfAborted();
185
+ state.modelId = selected.id;
186
+ state.modelName = selected.name;
187
+ state.reasoningEffort = selected.reasoningEffort;
188
+ // A closing turn may have rejected append; reserve a fresh identity for its successor.
189
+ mutableReceipt.turnId = randomUUID();
190
+ state.receipt = mutableReceipt;
191
+ this.#queueTurn(state, message, execution.startTurn, [], mutableReceipt.turnId, delivery);
192
+ return this.#snapshot(state);
193
+ });
194
+ }
195
+ async cancel(agentId) {
196
+ const state = await this.#find(agentId);
197
+ return this.#control(state, () => this.#cancelState(state));
198
+ }
199
+ #control(state, operation) {
200
+ const result = (state.controlTail ?? Promise.resolve()).then(operation);
201
+ state.controlTail = result.catch(() => undefined);
202
+ return result;
203
+ }
204
+ async #cancelState(state) {
205
+ if (state.status === 'queued' && state.turn) {
206
+ const index = this.#queue.indexOf(state);
207
+ if (index >= 0)
208
+ this.#queue.splice(index, 1);
209
+ state.status = 'cancelled';
210
+ this.#closeReceipt(state);
211
+ state.terminalPending = true;
212
+ await this.#publishTerminal(state);
213
+ state.turn.resolve();
214
+ delete state.turn;
215
+ this.#drain();
216
+ }
217
+ else if (state.status === 'running' && state.turn) {
218
+ const turn = state.turn;
219
+ if (turn.handle)
220
+ turn.handle.cancel();
221
+ else {
222
+ turn.cancelRequested = true;
223
+ await turn.handleReady;
224
+ }
225
+ await turn.completion;
226
+ }
227
+ return this.#snapshot(state);
228
+ }
229
+ listSessionResources() {
230
+ return [...this.#agents.values()].map((state) => ({
231
+ agentId: state.agentId,
232
+ status: state.status,
233
+ description: state.description
234
+ }));
235
+ }
236
+ async dispose() {
237
+ if (this.#disposed)
238
+ return;
239
+ this.#disposed = true;
240
+ for (const state of this.#queue.splice(0)) {
241
+ state.status = 'cancelled';
242
+ this.#closeReceipt(state);
243
+ state.turn?.resolve();
244
+ delete state.turn;
245
+ }
246
+ for (const state of this.#agents.values()) {
247
+ if (state.status !== 'running' || !state.turn)
248
+ continue;
249
+ if (state.turn.handle)
250
+ state.turn.handle.cancel();
251
+ else
252
+ state.turn.cancelRequested = true;
253
+ }
254
+ await Promise.allSettled([...this.#agents.values()].flatMap((state) => state.turn === undefined ? [] : [state.turn.completion]));
255
+ this.#execution = undefined;
256
+ }
257
+ #queueTurn(state, prompt, start, pendingMessages, turnId = randomUUID(), delivery) {
258
+ let resolve;
259
+ let resolveHandleReady;
260
+ const completion = new Promise((done) => (resolve = done));
261
+ const handleReady = new Promise((done) => (resolveHandleReady = done));
262
+ state.status = 'queued';
263
+ delete state.summary;
264
+ delete state.errorCode;
265
+ delete state.errorMessage;
266
+ state.changedFiles = [];
267
+ state.verification = [];
268
+ state.evidence = [];
269
+ state.terminalPending = false;
270
+ state.turn = {
271
+ turnId,
272
+ ...(delivery === undefined ? {} : { delivery }),
273
+ prompt,
274
+ start,
275
+ pendingMessages,
276
+ handleReady,
277
+ completion,
278
+ resolve,
279
+ resolveHandleReady
280
+ };
281
+ this.#queue.push(state);
282
+ this.#drain();
283
+ }
284
+ #drain() {
285
+ while (!this.#disposed && this.#running < this.limits.maxConcurrent && this.#queue.length > 0) {
286
+ const state = this.#queue.shift();
287
+ const turn = state.turn;
288
+ if (!turn || state.status !== 'queued')
289
+ continue;
290
+ state.status = 'running';
291
+ this.#running++;
292
+ void this.#startAndConsumeTurn(state, turn);
293
+ }
294
+ }
295
+ async #startAndConsumeTurn(state, turn) {
296
+ try {
297
+ turn.handle = await turn.start({
298
+ turnId: turn.turnId,
299
+ ...(turn.delivery === undefined ? {} : { delivery: turn.delivery }),
300
+ sessionId: state.agentId,
301
+ prompt: turn.prompt,
302
+ modelId: state.modelId,
303
+ reasoningEffort: state.reasoningEffort,
304
+ role: state.role,
305
+ initialMailbox: turn.pendingMessages.splice(0)
306
+ });
307
+ }
308
+ catch (error) {
309
+ turn.resolveHandleReady();
310
+ await this.#failTurn(state, error);
311
+ return;
312
+ }
313
+ if (turn.cancelRequested)
314
+ turn.handle.cancel();
315
+ turn.resolveHandleReady();
316
+ await this.#consumeTurn(state, turn);
317
+ }
318
+ async #consumeTurn(state, turn) {
319
+ try {
320
+ const consume = (async () => {
321
+ for await (const event of turn.handle)
322
+ observeAgentEvent(state, event);
323
+ })();
324
+ const result = await turn.handle.result();
325
+ await consume;
326
+ if (result.status === 'completed') {
327
+ state.status = 'completed';
328
+ state.summary = truncateUtf8(result.finalAnswer, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes);
329
+ }
330
+ else if (result.status === 'cancelled')
331
+ state.status = 'cancelled';
332
+ else {
333
+ state.status = 'failed';
334
+ state.errorCode = result.errorCode ?? 'MAR_AGENT_SUBAGENT_FAILED';
335
+ state.errorMessage = result.errorMessage ?? 'Subagent execution failed.';
336
+ }
337
+ state.terminalPending = true;
338
+ this.#closeReceipt(state);
339
+ await this.#publishTerminal(state);
340
+ }
341
+ catch (error) {
342
+ this.#setFailure(state, error);
343
+ this.#closeReceipt(state);
344
+ await this.#publishTerminal(state);
345
+ }
346
+ finally {
347
+ this.#closeReceipt(state);
348
+ this.#running--;
349
+ turn.resolve();
350
+ if (state.turn === turn)
351
+ delete state.turn;
352
+ this.#drain();
353
+ }
354
+ }
355
+ async #failTurn(state, error) {
356
+ this.#setFailure(state, error);
357
+ this.#closeReceipt(state);
358
+ const turn = state.turn;
359
+ delete state.turn;
360
+ this.#running--;
361
+ turn?.resolve();
362
+ await this.#publishTerminal(state);
363
+ this.#drain();
364
+ }
365
+ #setFailure(state, error) {
366
+ const diagnostic = describeMarAgentError(error, 'MAR_AGENT_SUBAGENT_FAILED');
367
+ state.status = 'failed';
368
+ state.errorCode = diagnostic.code;
369
+ state.errorMessage = diagnostic.message;
370
+ state.terminalPending = true;
371
+ }
372
+ #closeReceipt(state) {
373
+ if (state.receipt?.deliveryStatus === 'accepted' && state.receipt.turnId === state.turn?.turnId)
374
+ state.receipt.deliveryStatus = state.status === 'cancelled' ? 'cancelled' : 'failed';
375
+ }
376
+ async #deleteUnstartedSession(agentId, error) {
377
+ this.#agents.delete(agentId);
378
+ try {
379
+ await this.backend.deleteSession(agentId);
380
+ }
381
+ catch (cleanupError) {
382
+ const original = describeMarAgentError(error, 'MAR_AGENT_SUBAGENT_FAILED');
383
+ const cleanup = describeMarAgentError(cleanupError, 'MAR_AGENT_SUBAGENT_FAILED');
384
+ throw new MarAgentError(original.code, `${original.message} Cleanup also failed: ${cleanup.message}`, { cause: cleanupError });
385
+ }
386
+ throw error;
387
+ }
388
+ async #find(agentId) {
389
+ const known = this.#agents.get(agentId);
390
+ if (known)
391
+ return known;
392
+ const persisted = await this.backend.loadChild(this.parentSessionId, agentId);
393
+ if (!persisted)
394
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Subagent was not found.');
395
+ const restored = restoreAgentState(persisted);
396
+ this.#agents.set(agentId, restored);
397
+ return restored;
398
+ }
399
+ #requireExecution() {
400
+ if (this.#disposed)
401
+ throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
402
+ if (!this.#execution)
403
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Subagent control requires an active parent execution.');
404
+ return this.#execution;
405
+ }
406
+ #snapshot(state) {
407
+ return {
408
+ agentId: state.agentId,
409
+ description: state.description,
410
+ status: state.status,
411
+ ...state.receipt,
412
+ ...(state.modelId === undefined ? {} : { modelId: state.modelId }),
413
+ ...(state.modelName === undefined ? {} : { modelName: state.modelName }),
414
+ ...(state.reasoningEffort === undefined ? {} : { reasoningEffort: state.reasoningEffort }),
415
+ ...(state.latestMessage === undefined ? {} : { latestMessage: { ...state.latestMessage } }),
416
+ ...(state.summary === undefined ? {} : { summary: state.summary }),
417
+ ...(state.changedFiles.length === 0 ? {} : { changedFiles: [...state.changedFiles] }),
418
+ ...(state.verification.length === 0 ? {} : { verification: [...state.verification] }),
419
+ ...(state.evidence.length === 0
420
+ ? {}
421
+ : { evidence: state.evidence.map((item) => ({ ...item })) }),
422
+ ...(state.errorCode === undefined ? {} : { errorCode: state.errorCode }),
423
+ ...(state.errorMessage === undefined ? {} : { errorMessage: state.errorMessage })
424
+ };
425
+ }
426
+ async #publishTerminal(state) {
427
+ if (!state.terminalPending || !this.#execution)
428
+ return;
429
+ try {
430
+ await this.#execution.emitTerminal(this.#snapshot(state));
431
+ state.terminalPending = false;
432
+ }
433
+ catch {
434
+ // Keep the terminal transition pending for a later active parent Execution.
435
+ }
436
+ }
437
+ }
438
+ function validateTaskInput(input) {
439
+ if (input.parentSubagentId ||
440
+ !input.description.trim() ||
441
+ input.description.length > TOOL_EXECUTION_LIMITS.subagentDescriptionMaxCharacters ||
442
+ !input.prompt.trim() ||
443
+ Buffer.byteLength(input.prompt) > TOOL_EXECUTION_LIMITS.subagentPromptMaxBytes)
444
+ throw new MarAgentError(input.parentSubagentId
445
+ ? 'MAR_AGENT_SUBAGENT_RECURSION_DISABLED'
446
+ : 'MAR_AGENT_TOOL_INPUT_INVALID', input.parentSubagentId ? 'Subagents cannot create subagents.' : 'Subagent task is invalid.');
447
+ }
448
+ function resolveModel(execution, input) {
449
+ const modelId = input.modelId ?? execution.modelId;
450
+ const model = execution.models.get(modelId);
451
+ if (!model)
452
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_MODEL_UNAVAILABLE', 'Selected model is unavailable for subagents.');
453
+ if (input.reasoningEffort !== undefined &&
454
+ !model.reasoningEfforts.includes(input.reasoningEffort))
455
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_REASONING_EFFORT_UNAVAILABLE', 'Selected reasoning effort is unavailable for the subagent model.', {
456
+ details: {
457
+ modelId,
458
+ reasoningEffort: input.reasoningEffort,
459
+ supportedReasoningEfforts: model.reasoningEfforts
460
+ }
461
+ });
462
+ const reasoningEffort = input.reasoningEffort ??
463
+ (model.reasoningEfforts.includes(execution.reasoningEffort)
464
+ ? execution.reasoningEffort
465
+ : model.defaultReasoningEffort);
466
+ return { ...model, reasoningEffort };
467
+ }
468
+ function resolveExistingModel(execution, state) {
469
+ const model = execution.models.get(state.modelId ?? execution.modelId);
470
+ if (!model)
471
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_MODEL_UNAVAILABLE', 'The subagent model is no longer available.');
472
+ return {
473
+ ...model,
474
+ reasoningEffort: state.reasoningEffort && model.reasoningEfforts.includes(state.reasoningEffort)
475
+ ? state.reasoningEffort
476
+ : model.defaultReasoningEffort
477
+ };
478
+ }
479
+ function restoreAgentState(session) {
480
+ const delivered = session.records.findLast((record) => record.type === 'turn.user' &&
481
+ isRecord(record.payload) &&
482
+ typeof record.payload.messageId === 'string' &&
483
+ typeof record.turnId === 'string');
484
+ const latestExecutionStart = session.records.findLastIndex((record) => eventType(record) === 'execution.started');
485
+ const currentRecords = latestExecutionStart < 0 ? session.records : session.records.slice(latestExecutionStart);
486
+ const header = session.records.findLast((record) => record.type === 'execution.header' && isRecord(record.payload))?.payload;
487
+ const terminal = currentRecords.findLast((record) => ['execution.completed', 'execution.failed', 'execution.cancelled'].includes(eventType(record) ?? ''));
488
+ const terminalType = eventType(terminal);
489
+ const latestMessageRecord = session.records.findLast((record) => eventType(record) === 'message.completed');
490
+ const latestMessage = latestMessageRecord && isRecord(latestMessageRecord.payload)
491
+ ? latestMessageFromPayload(latestMessageRecord.payload)
492
+ : undefined;
493
+ const state = {
494
+ agentId: session.id,
495
+ description: session.source.description,
496
+ role: session.source.role,
497
+ status: terminalType === 'execution.completed'
498
+ ? 'completed'
499
+ : terminalType === 'execution.failed'
500
+ ? 'failed'
501
+ : terminalType === 'execution.cancelled'
502
+ ? 'cancelled'
503
+ : 'interrupted',
504
+ ...(delivered && isRecord(delivered.payload)
505
+ ? {
506
+ receipt: {
507
+ messageId: delivered.payload.messageId,
508
+ turnId: delivered.turnId,
509
+ deliveryStatus: 'applied'
510
+ }
511
+ }
512
+ : {}),
513
+ ...(typeof header?.modelId === 'string' ? { modelId: header.modelId } : {}),
514
+ ...(typeof header?.modelName === 'string' ? { modelName: header.modelName } : {}),
515
+ ...(isReasoningEffort(header?.reasoningEffort)
516
+ ? { reasoningEffort: header.reasoningEffort }
517
+ : {}),
518
+ ...(latestMessage === undefined ? {} : { latestMessage }),
519
+ changedFiles: [],
520
+ verification: [],
521
+ evidence: [],
522
+ terminalPending: false
523
+ };
524
+ if (state.status === 'completed' && latestMessage?.phase === 'final_answer')
525
+ state.summary = truncateUtf8(latestMessage.text, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes);
526
+ if (state.status === 'failed' && terminal && isRecord(terminal.payload)) {
527
+ if (typeof terminal.payload.code === 'string')
528
+ state.errorCode = terminal.payload.code;
529
+ if (typeof terminal.payload.message === 'string')
530
+ state.errorMessage = terminal.payload.message;
531
+ }
532
+ for (const record of currentRecords) {
533
+ if (!isRecord(record.payload) || eventType(record) !== 'tool.completed')
534
+ continue;
535
+ const event = record.payload;
536
+ if (event.type === 'tool.completed')
537
+ observeAgentEvent(state, event);
538
+ }
539
+ return state;
540
+ }
541
+ function observeAgentEvent(state, event) {
542
+ if (event.type === 'message.completed') {
543
+ state.latestMessage = latestMessageFromEvent(event);
544
+ return;
545
+ }
546
+ if (event.type !== 'tool.completed')
547
+ return;
548
+ if (event.toolName === 'apply_patch' && isRecord(event.data)) {
549
+ const data = event.data;
550
+ const files = Array.isArray(data.files) ? data.files : [];
551
+ for (const file of files) {
552
+ if (!isRecord(file))
553
+ continue;
554
+ for (const path of [file.path, file.targetPath])
555
+ if (typeof path === 'string' &&
556
+ path.length > 0 &&
557
+ !state.changedFiles.includes(path) &&
558
+ state.changedFiles.length < TOOL_EXECUTION_LIMITS.subagentChangedFilesMaxItems)
559
+ state.changedFiles.push(path);
560
+ }
561
+ }
562
+ else if (event.toolName === 'read' && isRecord(event.data)) {
563
+ const data = event.data;
564
+ const results = Array.isArray(data.results) ? data.results : [];
565
+ for (const result of results) {
566
+ if (!isRecord(result) ||
567
+ typeof result.path !== 'string' ||
568
+ result.error !== undefined ||
569
+ state.evidence.length >= TOOL_EXECUTION_LIMITS.subagentEvidenceMaxItems)
570
+ continue;
571
+ const evidence = {
572
+ path: result.path,
573
+ ...(isPositiveInteger(result.startLine) ? { startLine: result.startLine } : {}),
574
+ ...(isPositiveInteger(result.endLine) ? { endLine: result.endLine } : {})
575
+ };
576
+ if (!state.evidence.some((item) => JSON.stringify(item) === JSON.stringify(evidence)))
577
+ state.evidence.push(evidence);
578
+ }
579
+ }
580
+ else if (event.toolName === 'exec' &&
581
+ state.verification.length < TOOL_EXECUTION_LIMITS.subagentVerificationMaxItems)
582
+ state.verification.push(truncateUtf8(event.summary, 2_000));
583
+ }
584
+ function eventType(record) {
585
+ return record && isRecord(record.payload) && typeof record.payload.type === 'string'
586
+ ? record.payload.type
587
+ : undefined;
588
+ }
589
+ function latestMessageFromEvent(event) {
590
+ const text = truncateCharacters(event.text, 10_000);
591
+ return {
592
+ itemId: event.itemId,
593
+ turnId: event.turnId ?? '',
594
+ phase: event.phase,
595
+ text,
596
+ timestamp: event.timestamp,
597
+ truncated: text !== event.text
598
+ };
599
+ }
600
+ function latestMessageFromPayload(payload) {
601
+ if (typeof payload.itemId !== 'string' ||
602
+ typeof payload.turnId !== 'string' ||
603
+ (payload.phase !== 'commentary' && payload.phase !== 'final_answer') ||
604
+ typeof payload.text !== 'string' ||
605
+ typeof payload.timestamp !== 'string')
606
+ return undefined;
607
+ const text = truncateCharacters(payload.text, 10_000);
608
+ return {
609
+ itemId: payload.itemId,
610
+ turnId: payload.turnId,
611
+ phase: payload.phase,
612
+ text,
613
+ timestamp: payload.timestamp,
614
+ truncated: text !== payload.text
615
+ };
616
+ }
617
+ function isReasoningEffort(value) {
618
+ return (value === 'low' ||
619
+ value === 'medium' ||
620
+ value === 'high' ||
621
+ value === 'xhigh' ||
622
+ value === 'max' ||
623
+ value === 'ultra');
624
+ }
625
+ function isRecord(value) {
626
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
627
+ }
628
+ function isPositiveInteger(value) {
629
+ return typeof value === 'number' && Number.isInteger(value) && value >= 1;
630
+ }
631
+ function truncateUtf8(value, maximumBytes) {
632
+ const bytes = Buffer.from(value);
633
+ if (bytes.length <= maximumBytes)
634
+ return value;
635
+ let end = maximumBytes;
636
+ while (end > 0 && (bytes[end] & 0xc0) === 0x80)
637
+ end--;
638
+ return bytes.subarray(0, end).toString('utf8');
639
+ }
640
+ function truncateCharacters(value, maximumCharacters) {
641
+ const characters = [...value];
642
+ return characters.length <= maximumCharacters
643
+ ? value
644
+ : characters.slice(0, maximumCharacters).join('');
645
+ }
646
+ function waitForCompletion(completion, waitMs, signal) {
647
+ return new Promise((resolve, reject) => {
648
+ let settled = false;
649
+ const finish = (error) => {
650
+ if (settled)
651
+ return;
652
+ settled = true;
653
+ clearTimeout(timer);
654
+ signal?.removeEventListener('abort', abort);
655
+ if (error === undefined)
656
+ resolve();
657
+ else
658
+ reject(error);
659
+ };
660
+ const abort = () => finish(signal?.reason ?? new MarAgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.'));
661
+ const timer = setTimeout(finish, waitMs);
662
+ timer.unref();
663
+ signal?.addEventListener('abort', abort, { once: true });
664
+ void completion.then(() => finish());
665
+ if (signal?.aborted)
666
+ abort();
667
+ });
668
+ }
669
+ function linkAbort(signal, abort) {
670
+ const listener = () => abort();
671
+ signal.addEventListener('abort', listener, { once: true });
672
+ if (signal.aborted)
673
+ listener();
674
+ return () => signal.removeEventListener('abort', listener);
675
+ }