@myagentroam/agent 0.9.158 → 0.9.160

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 (130) hide show
  1. package/dist/agent-core/extensions/agents.d.ts +69 -0
  2. package/dist/agent-core/extensions/agents.js +160 -0
  3. package/dist/agent-core/extensions/contracts.d.ts +260 -0
  4. package/dist/agent-core/extensions/contracts.js +1 -0
  5. package/dist/agent-core/extensions/data.d.ts +84 -0
  6. package/dist/agent-core/extensions/data.js +83 -0
  7. package/dist/agent-core/extensions/environment.d.ts +27 -0
  8. package/dist/agent-core/extensions/environment.js +52 -0
  9. package/dist/agent-core/extensions/files.d.ts +64 -0
  10. package/dist/agent-core/extensions/files.js +26 -0
  11. package/dist/agent-core/extensions/hooks.d.ts +32 -0
  12. package/dist/agent-core/extensions/hooks.js +135 -0
  13. package/dist/agent-core/extensions/manager.d.ts +39 -0
  14. package/dist/agent-core/extensions/manager.js +458 -0
  15. package/dist/agent-core/extensions/mcp.d.ts +99 -0
  16. package/dist/agent-core/extensions/mcp.js +433 -0
  17. package/dist/agent-core/extensions/model-request.d.ts +60 -0
  18. package/dist/agent-core/extensions/model-request.js +167 -0
  19. package/dist/agent-core/extensions/model-stream.d.ts +9 -0
  20. package/dist/agent-core/extensions/model-stream.js +101 -0
  21. package/dist/agent-core/extensions/models.d.ts +43 -0
  22. package/dist/agent-core/extensions/models.js +1 -0
  23. package/dist/agent-core/extensions/network.d.ts +32 -0
  24. package/dist/agent-core/extensions/network.js +97 -0
  25. package/dist/agent-core/extensions/operation.d.ts +29 -0
  26. package/dist/agent-core/extensions/operation.js +98 -0
  27. package/dist/agent-core/extensions/processes.d.ts +52 -0
  28. package/dist/agent-core/extensions/processes.js +283 -0
  29. package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
  30. package/dist/agent-core/extensions/prompt-composition.js +106 -0
  31. package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
  32. package/dist/agent-core/extensions/prompt-input.js +46 -0
  33. package/dist/agent-core/extensions/resources.d.ts +27 -0
  34. package/dist/agent-core/extensions/resources.js +221 -0
  35. package/dist/agent-core/extensions/services.d.ts +31 -0
  36. package/dist/agent-core/extensions/services.js +61 -0
  37. package/dist/agent-core/extensions/session-input.d.ts +151 -0
  38. package/dist/agent-core/extensions/session-input.js +351 -0
  39. package/dist/agent-core/extensions/session-messages.d.ts +41 -0
  40. package/dist/agent-core/extensions/session-messages.js +76 -0
  41. package/dist/agent-core/extensions/session.d.ts +68 -0
  42. package/dist/agent-core/extensions/session.js +593 -0
  43. package/dist/agent-core/extensions/settings.d.ts +59 -0
  44. package/dist/agent-core/extensions/settings.js +115 -0
  45. package/dist/agent-core/extensions/store.d.ts +10 -0
  46. package/dist/agent-core/extensions/store.js +131 -0
  47. package/dist/agent-core/extensions/tools.d.ts +61 -0
  48. package/dist/agent-core/extensions/tools.js +266 -0
  49. package/dist/agent-core/host/contracts.d.ts +25 -0
  50. package/dist/agent-core/host/local-agent-host.d.ts +10 -1
  51. package/dist/agent-core/host/local-agent-host.js +132 -20
  52. package/dist/agent-core/host/local-file-access.d.ts +30 -0
  53. package/dist/agent-core/host/local-file-access.js +107 -0
  54. package/dist/agent-core/host/local-network-access.d.ts +6 -0
  55. package/dist/agent-core/host/local-network-access.js +97 -0
  56. package/dist/agent-core/host/local-process-executor.d.ts +5 -1
  57. package/dist/agent-core/host/local-session-environment.d.ts +18 -0
  58. package/dist/agent-core/host/local-session-environment.js +102 -0
  59. package/dist/agent-core/model/anthropic-messages.js +73 -23
  60. package/dist/agent-core/model/contracts.d.ts +21 -0
  61. package/dist/agent-core/model/openai-responses.d.ts +3 -1
  62. package/dist/agent-core/model/openai-responses.js +248 -97
  63. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  64. package/dist/agent-core/model/text-blocks.js +40 -0
  65. package/dist/agent-core/prompts/index.d.ts +6 -2
  66. package/dist/agent-core/prompts/index.js +38 -24
  67. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  68. package/dist/agent-core/runtime/context/normalize.js +21 -1
  69. package/dist/agent-core/runtime/context/projection.js +35 -1
  70. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  71. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  72. package/dist/agent-core/runtime/context/restore.js +47 -4
  73. package/dist/agent-core/runtime/context/usage.js +2 -3
  74. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  75. package/dist/agent-core/runtime/execution.js +5 -2
  76. package/dist/agent-core/sdk/agent.js +177 -40
  77. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  78. package/dist/agent-core/sdk/extension-agents.js +51 -0
  79. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  80. package/dist/agent-core/sdk/extension-models.js +128 -0
  81. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  82. package/dist/agent-core/sdk/extension-session.js +103 -0
  83. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  84. package/dist/agent-core/sdk/extension-tools.js +126 -0
  85. package/dist/agent-core/sdk/session.d.ts +26 -2
  86. package/dist/agent-core/sdk/session.js +1215 -506
  87. package/dist/agent-core/sdk/types.d.ts +3 -0
  88. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  89. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  90. package/dist/agent-core/session/jsonl-store.js +124 -84
  91. package/dist/agent-core/session/types.d.ts +4 -1
  92. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  93. package/dist/agent-core/storage/file-lock.js +83 -0
  94. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  95. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  96. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  97. package/dist/agent-core/subagent/session-controller.js +355 -98
  98. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  99. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  100. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  101. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  102. package/dist/agent-core/tools/code-mode/index.js +1 -2
  103. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  104. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  105. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  106. package/dist/agent-core/tools/execution.d.ts +11 -2
  107. package/dist/agent-core/tools/execution.js +58 -21
  108. package/dist/agent-core/tools/input.d.ts +2 -0
  109. package/dist/agent-core/tools/input.js +36 -0
  110. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  111. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  112. package/dist/agent-core/tools/local/exec.js +234 -62
  113. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  114. package/dist/agent-core/tools/local/registry.js +60 -9
  115. package/dist/agent-core/tools/local/view-image.js +108 -2
  116. package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
  117. package/dist/agent-core/tools/mcp/catalog.js +4 -4
  118. package/dist/agent-core/tools/mcp/history.js +2 -2
  119. package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
  120. package/dist/agent-core/tools/mcp/tool-search.js +2 -2
  121. package/dist/agent-core/tools/output-projection.d.ts +3 -1
  122. package/dist/agent-core/tools/output-projection.js +11 -2
  123. package/dist/agent-core/tools/runtime-context.d.ts +1 -0
  124. package/dist/agent-core/tools/runtime.d.ts +3 -1
  125. package/dist/agent-core/tools/runtime.js +22 -7
  126. package/dist/agent-core/tools/scheduler.d.ts +2 -0
  127. package/dist/agent-core/tools/scheduler.js +43 -6
  128. package/dist/index.d.ts +21 -0
  129. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
  130. package/package.json +1 -1
@@ -11,6 +11,7 @@ export class SubagentSessionController {
11
11
  #queue = [];
12
12
  #completionWaiters = new Set();
13
13
  #activityWaiters = new Set();
14
+ #nativeObservations = new Map();
14
15
  #activityRevision = 0;
15
16
  #resourceRevision = 0;
16
17
  #running = 0;
@@ -18,6 +19,7 @@ export class SubagentSessionController {
18
19
  #creationBudget = { count: 0 };
19
20
  #retainedExecution = new AsyncLocalStorage();
20
21
  #execution;
22
+ #pendingDeliveries = new Set();
21
23
  constructor(parentSessionId, rootSessionId, backend, limits = { maxTasks: 8, maxConcurrent: 4 }) {
22
24
  this.parentSessionId = parentSessionId;
23
25
  this.rootSessionId = rootSessionId;
@@ -43,11 +45,11 @@ export class SubagentSessionController {
43
45
  await this.#publishTerminal(state);
44
46
  }
45
47
  /** A retained cell must not borrow a later model Execution's identity or task budget. */
46
- captureExecution() {
47
- const context = { execution: this.#requireExecution(), budget: this.#creationBudget };
48
+ captureExecution(executionId) {
49
+ const enter = this.captureParentFrame(executionId);
48
50
  return {
49
- run: (input, options) => this.#retainedExecution.run(context, () => this.run(input, options)),
50
- message: (id, message, options) => this.#retainedExecution.run(context, () => this.message(id, message, options)),
51
+ run: (input, options) => enter(() => this.run(input, options)),
52
+ message: (id, message, options) => enter(() => this.message(id, message, options)),
51
53
  output: (id, options) => this.output(id, options),
52
54
  outputAny: (options) => this.outputAny(options),
53
55
  cancel: (id) => this.cancel(id),
@@ -55,22 +57,84 @@ export class SubagentSessionController {
55
57
  dispose: () => this.dispose()
56
58
  };
57
59
  }
60
+ /** SDK physical tool ingress shares the same captured parent as its model controls. */
61
+ captureParentFrame(executionId) {
62
+ const execution = executionId === undefined ? this.#requireExecution() : this.#execution;
63
+ if (!execution || (executionId !== undefined && execution.executionId !== executionId))
64
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Parent Execution source is unavailable.');
65
+ const context = (executionId === undefined
66
+ ? this.#retainedExecution.getStore()
67
+ : undefined) ?? {
68
+ execution,
69
+ budget: this.#creationBudget
70
+ };
71
+ return (run) => this.#retainedExecution.run(context, run);
72
+ }
73
+ withOperation(operation, run) {
74
+ operation.signal.throwIfAborted();
75
+ if (!operation.operationId.trim())
76
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Parent operation identity is invalid.');
77
+ return this.#retainedExecution.run({ execution: operation, budget: { count: 0 }, native: true }, run);
78
+ }
79
+ async inspect(agentId) {
80
+ return this.#snapshot(await this.#find(agentId), { full: true });
81
+ }
82
+ inspectAll() {
83
+ if (this.#disposed)
84
+ throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
85
+ return [...this.#agents.values()].map((state) => this.#snapshot(state, { full: true }));
86
+ }
87
+ async cancelOwned(ownerInstanceId) {
88
+ for (const pending of this.#pendingDeliveries)
89
+ if (pending.ownerInstanceId === ownerInstanceId)
90
+ pending.delivery.settled?.('cancelled');
91
+ const results = await Promise.allSettled([...this.#agents.values()]
92
+ .filter((state) => state.turn?.ownerInstanceId === ownerInstanceId)
93
+ .map((state) => this.#control(state, async () => {
94
+ if (state.turn?.ownerInstanceId === ownerInstanceId)
95
+ await this.#cancelState(state);
96
+ })));
97
+ const failure = results.find((result) => result.status === 'rejected');
98
+ this.#nativeObservations.delete(ownerInstanceId);
99
+ if (failure?.status === 'rejected')
100
+ throw failure.reason;
101
+ }
58
102
  endParentExecution(executionId) {
59
103
  if (this.#execution?.executionId === executionId)
60
104
  this.#execution = undefined;
61
105
  }
106
+ parentExecution(executionId) {
107
+ const execution = this.#retainedExecution.getStore()?.execution ?? this.#execution;
108
+ return execution?.executionId === executionId ? { ...execution, executionId } : undefined;
109
+ }
62
110
  async run(input, options = {}) {
63
111
  options.signal?.throwIfAborted();
112
+ options.turnSignal?.throwIfAborted();
113
+ if (options.waitFor !== undefined &&
114
+ options.waitFor !== 'start' &&
115
+ options.waitFor !== 'completion')
116
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Task start wait is invalid.');
64
117
  const execution = this.#requireExecution();
65
- validateTaskInput(input);
66
- if ((this.#retainedExecution.getStore()?.budget ?? this.#creationBudget).count >=
67
- this.limits.maxTasks)
118
+ const native = this.#retainedExecution.getStore()?.native === true;
119
+ validateTaskInput(input, native);
120
+ if (!native &&
121
+ (this.#retainedExecution.getStore()?.budget ?? this.#creationBudget).count >=
122
+ this.limits.maxTasks)
68
123
  throw new MarAgentError('MAR_AGENT_SUBAGENT_LIMIT', 'Subagent task limit was reached.');
69
124
  const source = {
70
125
  type: 'subagent',
71
126
  parentSessionId: this.parentSessionId,
72
127
  rootSessionId: this.rootSessionId,
73
- parentExecutionId: execution.executionId,
128
+ ...(execution.executionId === undefined ? {} : { parentExecutionId: execution.executionId }),
129
+ ...(native && 'operationId' in execution
130
+ ? {
131
+ parentOperationId: execution.operationId,
132
+ ...(execution.ownerInstanceId === undefined
133
+ ? {}
134
+ : { ownerInstanceId: execution.ownerInstanceId }),
135
+ ...(execution.extensionId === undefined ? {} : { extensionId: execution.extensionId })
136
+ }
137
+ : {}),
74
138
  role: input.role,
75
139
  description: input.description.trim()
76
140
  };
@@ -81,7 +145,9 @@ export class SubagentSessionController {
81
145
  try {
82
146
  if (this.#disposed)
83
147
  throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
148
+ this.#requireExecution();
84
149
  options.signal?.throwIfAborted();
150
+ options.turnSignal?.throwIfAborted();
85
151
  }
86
152
  catch (error) {
87
153
  await this.#deleteUnstartedSession(created.id, error);
@@ -90,6 +156,8 @@ export class SubagentSessionController {
90
156
  agentId: created.id,
91
157
  description: source.description,
92
158
  role: input.role,
159
+ ...(source.ownerInstanceId === undefined ? {} : { ownerInstanceId: source.ownerInstanceId }),
160
+ ...(source.extensionId === undefined ? {} : { extensionId: source.extensionId }),
93
161
  status: 'queued',
94
162
  messageRevision: 0,
95
163
  observedMessageRevision: 0,
@@ -101,7 +169,8 @@ export class SubagentSessionController {
101
169
  terminalObserved: false,
102
170
  resourceRevision: ++this.#resourceRevision
103
171
  };
104
- (this.#retainedExecution.getStore()?.budget ?? this.#creationBudget).count++;
172
+ if (!native)
173
+ (this.#retainedExecution.getStore()?.budget ?? this.#creationBudget).count++;
105
174
  this.#agents.set(state.agentId, state);
106
175
  const knownModel = execution.models.get(input.modelId ?? execution.modelId);
107
176
  if (knownModel) {
@@ -121,6 +190,7 @@ export class SubagentSessionController {
121
190
  await options.onCreated?.(this.#receiptSnapshot(state));
122
191
  if (this.#disposed)
123
192
  throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
193
+ this.#requireExecution();
124
194
  options.signal?.throwIfAborted();
125
195
  }
126
196
  catch (error) {
@@ -128,11 +198,11 @@ export class SubagentSessionController {
128
198
  }
129
199
  if (state.status === 'failed') {
130
200
  await this.#publishTerminal(state);
131
- state.terminalObserved = true;
201
+ this.#observeTerminal(state);
132
202
  return this.#snapshot(state);
133
203
  }
134
- this.#queueTurn(state, input.prompt, execution.startTurn, []);
135
- if (input.background)
204
+ this.#queueTurn(state, input.prompt, execution, [], undefined, undefined, options.signal, options.turnSignal);
205
+ if (options.waitFor === 'start' || (options.waitFor === undefined && input.background))
136
206
  return this.#receiptSnapshot(state);
137
207
  await waitForPromise(state.turn.completion, options.signal);
138
208
  const includeLatestMessage = this.#hasUnreadMessage(state);
@@ -144,7 +214,19 @@ export class SubagentSessionController {
144
214
  options.signal?.throwIfAborted();
145
215
  const state = await this.#find(agentId);
146
216
  options.signal?.throwIfAborted();
147
- validateOutputOptions(options);
217
+ validateOutputOptions(options, this.#retainedExecution.getStore()?.native === true);
218
+ if (options.untilSettled) {
219
+ if (state.turn)
220
+ await waitForPromise(state.turn.completion, options.signal);
221
+ options.signal?.throwIfAborted();
222
+ const includeLatestMessage = this.#hasUnreadMessage(state);
223
+ const includeTerminalDetails = this.#observeTerminal(state);
224
+ this.#observeLatestMessage(state);
225
+ return {
226
+ ...this.#snapshot(state, { includeLatestMessage, includeTerminalDetails }),
227
+ waitOutcome: 'completion'
228
+ };
229
+ }
148
230
  if (options.waitMs === undefined || options.waitMs === 0)
149
231
  return {
150
232
  ...this.#snapshot(state, {
@@ -167,7 +249,9 @@ export class SubagentSessionController {
167
249
  this.#observeLatestMessage(state);
168
250
  return { ...this.#snapshot(state), waitOutcome: 'message' };
169
251
  }
170
- const waitMs = Math.max(options.waitMs, TOOL_EXECUTION_LIMITS.agentWaitWaitFloorMs);
252
+ const waitMs = this.#retainedExecution.getStore()?.native
253
+ ? options.waitMs
254
+ : Math.max(options.waitMs, TOOL_EXECUTION_LIMITS.agentWaitWaitFloorMs);
171
255
  if (waitFor === 'completion') {
172
256
  const completed = await this.#waitForCompletion(state, waitMs, options.signal);
173
257
  options.signal?.throwIfAborted();
@@ -201,7 +285,9 @@ export class SubagentSessionController {
201
285
  }
202
286
  async outputAny(options = {}) {
203
287
  options.signal?.throwIfAborted();
204
- validateOutputOptions(options);
288
+ if (options.untilSettled)
289
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'An unbounded wait requires an agent id.');
290
+ validateOutputOptions(options, this.#retainedExecution.getStore()?.native === true);
205
291
  const states = [...this.#agents.values()];
206
292
  if (states.length === 0)
207
293
  throw new MarAgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Subagent was not found.');
@@ -232,7 +318,9 @@ export class SubagentSessionController {
232
318
  return { ...this.#snapshot(pending), waitOutcome: 'message' };
233
319
  }
234
320
  }
235
- const waitMs = Math.max(options.waitMs, TOOL_EXECUTION_LIMITS.agentWaitWaitFloorMs);
321
+ const waitMs = this.#retainedExecution.getStore()?.native
322
+ ? options.waitMs
323
+ : Math.max(options.waitMs, TOOL_EXECUTION_LIMITS.agentWaitWaitFloorMs);
236
324
  if (waitFor === 'message') {
237
325
  const activity = await this.#waitForMessageOrCompletion(live, waitMs, options.signal);
238
326
  options.signal?.throwIfAborted();
@@ -272,7 +360,8 @@ export class SubagentSessionController {
272
360
  const execution = this.#requireExecution();
273
361
  if (typeof message !== 'string' ||
274
362
  message.trim().length === 0 ||
275
- Buffer.byteLength(message) > TOOL_EXECUTION_LIMITS.subagentMessageMaxBytes ||
363
+ (this.#retainedExecution.getStore()?.native !== true &&
364
+ Buffer.byteLength(message) > TOOL_EXECUTION_LIMITS.subagentMessageMaxBytes) ||
276
365
  (options.delivery !== undefined &&
277
366
  options.delivery !== 'append' &&
278
367
  options.delivery !== 'replace'))
@@ -290,41 +379,49 @@ export class SubagentSessionController {
290
379
  const turnId = state.turn?.turnId ?? randomUUID();
291
380
  const receipt = { messageId: randomUUID(), turnId, deliveryStatus: 'accepted' };
292
381
  const mutableReceipt = receipt;
293
- const delivery = {
294
- messageId: receipt.messageId,
295
- applied: () => {
296
- mutableReceipt.deliveryStatus = 'applied';
382
+ const delivery = this.#createDelivery(state, execution, mutableReceipt, options.signal);
383
+ try {
384
+ if (state.status === 'running' && state.turn) {
385
+ const turn = state.turn;
386
+ if (!turn.handle)
387
+ await turn.handleReady;
388
+ this.#requireExecution();
389
+ options.signal?.throwIfAborted();
390
+ delivery.signal?.throwIfAborted();
391
+ if (state.status === 'running' &&
392
+ state.turn === turn &&
393
+ turn.handle?.enqueueMessage(message, delivery)) {
394
+ delivery.accepted();
395
+ state.receipt = mutableReceipt;
396
+ return this.#messageReceiptSnapshot(state);
397
+ }
398
+ await turn.completion;
297
399
  }
298
- };
299
- if (state.status === 'running' && state.turn) {
300
- const turn = state.turn;
301
- if (!turn.handle)
302
- await turn.handleReady;
303
- this.#requireExecution();
304
- options.signal?.throwIfAborted();
305
- if (state.status === 'running' &&
306
- state.turn === turn &&
307
- turn.handle?.enqueueMessage(message, delivery)) {
400
+ else if (state.status === 'queued' && state.turn) {
401
+ delivery.accepted();
402
+ state.turn.pendingMessages.push({ text: message, delivery });
308
403
  state.receipt = mutableReceipt;
309
404
  return this.#messageReceiptSnapshot(state);
310
405
  }
311
- await turn.completion;
312
- }
313
- else if (state.status === 'queued' && state.turn) {
314
- state.turn.pendingMessages.push({ text: message, delivery });
406
+ this.#requireExecution();
407
+ options.signal?.throwIfAborted();
408
+ delivery.signal?.throwIfAborted();
409
+ state.modelId = selected.id;
410
+ state.modelName = selected.name;
411
+ state.reasoningEffort = selected.reasoningEffort;
412
+ // A closing turn may have rejected append; reserve a fresh identity for its successor.
413
+ mutableReceipt.turnId = randomUUID();
414
+ delivery.accepted();
315
415
  state.receipt = mutableReceipt;
416
+ this.#queueTurn(state, message, execution, [], mutableReceipt.turnId, delivery, options.signal);
316
417
  return this.#messageReceiptSnapshot(state);
317
418
  }
318
- this.#requireExecution();
319
- options.signal?.throwIfAborted();
320
- state.modelId = selected.id;
321
- state.modelName = selected.name;
322
- state.reasoningEffort = selected.reasoningEffort;
323
- // A closing turn may have rejected append; reserve a fresh identity for its successor.
324
- mutableReceipt.turnId = randomUUID();
325
- state.receipt = mutableReceipt;
326
- this.#queueTurn(state, message, execution.startTurn, [], mutableReceipt.turnId, delivery);
327
- return this.#messageReceiptSnapshot(state);
419
+ catch (error) {
420
+ delivery.settled?.(options.signal?.aborted || ('signal' in execution && execution.signal.aborted)
421
+ ? 'cancelled'
422
+ : 'failed');
423
+ throw error;
424
+ }
328
425
  });
329
426
  }
330
427
  async cancel(agentId) {
@@ -350,6 +447,7 @@ export class SubagentSessionController {
350
447
  this.#closeReceipt(state);
351
448
  state.terminalPending = true;
352
449
  await this.#publishTerminal(state);
450
+ state.turn.resolveHandleReady();
353
451
  state.turn.resolve();
354
452
  this.#notifyCompletion(state);
355
453
  delete state.turn;
@@ -385,9 +483,12 @@ export class SubagentSessionController {
385
483
  if (this.#disposed)
386
484
  return;
387
485
  this.#disposed = true;
486
+ for (const pending of this.#pendingDeliveries)
487
+ pending.delivery.settled?.('cancelled');
388
488
  for (const state of this.#queue.splice(0)) {
389
489
  state.status = 'cancelled';
390
490
  this.#closeReceipt(state);
491
+ state.turn?.resolveHandleReady();
391
492
  state.turn?.resolve();
392
493
  this.#notifyCompletion(state);
393
494
  delete state.turn;
@@ -402,12 +503,22 @@ export class SubagentSessionController {
402
503
  }
403
504
  await Promise.allSettled([...this.#agents.values()].flatMap((state) => state.turn === undefined ? [] : [state.turn.completion]));
404
505
  this.#execution = undefined;
506
+ this.#nativeObservations.clear();
405
507
  }
406
- #queueTurn(state, prompt, start, pendingMessages, turnId = randomUUID(), delivery) {
508
+ #queueTurn(state, prompt, execution, pendingMessages, turnId = randomUUID(), delivery, signal, turnSignal) {
407
509
  let resolve;
408
510
  let resolveHandleReady;
409
511
  const completion = new Promise((done) => (resolve = done));
410
512
  const handleReady = new Promise((done) => (resolveHandleReady = done));
513
+ const native = this.#retainedExecution.getStore()?.native === true && 'operationId' in execution
514
+ ? execution
515
+ : undefined;
516
+ const sourceSignal = turnSignal ??
517
+ (native === undefined
518
+ ? undefined
519
+ : signal === undefined
520
+ ? native.signal
521
+ : AbortSignal.any([native.signal, signal]));
411
522
  state.status = 'queued';
412
523
  this.#touchResource(state);
413
524
  delete state.summary;
@@ -417,19 +528,37 @@ export class SubagentSessionController {
417
528
  state.verification = [];
418
529
  state.evidence = [];
419
530
  state.terminalPending = false;
531
+ state.terminalObserved = false;
420
532
  state.turn = {
421
533
  turnId,
534
+ ...(native?.ownerInstanceId === undefined ? {} : { ownerInstanceId: native.ownerInstanceId }),
535
+ ...(native?.origin === undefined
536
+ ? {}
537
+ : { origin: Object.freeze({ ...native.origin, signal: sourceSignal }) }),
422
538
  ...(delivery === undefined ? {} : { delivery }),
423
539
  prompt,
424
- start,
540
+ start: execution.startTurn,
425
541
  pendingMessages,
426
542
  handleReady,
427
543
  completion,
428
- resolve,
544
+ resolve: () => {
545
+ sourceSignal?.removeEventListener('abort', abort);
546
+ resolve();
547
+ },
429
548
  resolveHandleReady
430
549
  };
550
+ const turn = state.turn;
551
+ const abort = () => {
552
+ void this.#control(state, async () => {
553
+ if (state.turn === turn)
554
+ await this.#cancelState(state);
555
+ }).catch(() => undefined);
556
+ };
557
+ sourceSignal?.addEventListener('abort', abort, { once: true });
431
558
  this.#queue.push(state);
432
559
  this.#drain();
560
+ if (sourceSignal?.aborted)
561
+ abort();
433
562
  }
434
563
  #drain() {
435
564
  while (!this.#disposed && this.#running < this.limits.maxConcurrent && this.#queue.length > 0) {
@@ -448,6 +577,7 @@ export class SubagentSessionController {
448
577
  turn.handle = await turn.start({
449
578
  turnId: turn.turnId,
450
579
  ...(turn.delivery === undefined ? {} : { delivery: turn.delivery }),
580
+ ...(turn.origin === undefined ? {} : { origin: turn.origin }),
451
581
  sessionId: state.agentId,
452
582
  prompt: turn.prompt,
453
583
  modelId: state.modelId,
@@ -482,7 +612,7 @@ export class SubagentSessionController {
482
612
  await consume;
483
613
  if (result.status === 'completed') {
484
614
  state.status = 'completed';
485
- state.summary = truncateUtf8(result.finalAnswer, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes);
615
+ state.summary = result.finalAnswer;
486
616
  }
487
617
  else if (result.status === 'cancelled')
488
618
  state.status = 'cancelled';
@@ -558,7 +688,21 @@ export class SubagentSessionController {
558
688
  waiter(activity);
559
689
  }
560
690
  #hasUnreadMessage(state) {
561
- return state.messageRevision > state.observedMessageRevision;
691
+ return (state.messageRevision >
692
+ (this.#nativeObservation(state)?.messageRevision ?? state.observedMessageRevision));
693
+ }
694
+ #nativeObservation(state) {
695
+ const context = this.#retainedExecution.getStore();
696
+ if (!context?.native || !('operationId' in context.execution))
697
+ return undefined;
698
+ const owner = context.execution.ownerInstanceId ?? context.execution.operationId;
699
+ let observations = this.#nativeObservations.get(owner);
700
+ if (!observations)
701
+ this.#nativeObservations.set(owner, (observations = new Map()));
702
+ let observation = observations.get(state.agentId);
703
+ if (!observation)
704
+ observations.set(state.agentId, (observation = { messageRevision: 0, terminalRevision: 0 }));
705
+ return observation;
562
706
  }
563
707
  #oldestUnreadMessage(states) {
564
708
  return states
@@ -567,17 +711,79 @@ export class SubagentSessionController {
567
711
  .at(0);
568
712
  }
569
713
  #observeLatestMessage(state) {
570
- state.observedMessageRevision = state.messageRevision;
714
+ const native = this.#nativeObservation(state);
715
+ if (native)
716
+ native.messageRevision = state.messageRevision;
717
+ else
718
+ state.observedMessageRevision = state.messageRevision;
571
719
  }
572
720
  #observeTerminal(state) {
721
+ const native = this.#nativeObservation(state);
722
+ if (native) {
723
+ if (!isTerminalState(state) || native.terminalRevision === state.resourceRevision)
724
+ return false;
725
+ native.terminalRevision = state.resourceRevision;
726
+ return true;
727
+ }
573
728
  if (!isTerminalState(state) || state.terminalObserved)
574
729
  return false;
575
730
  state.terminalObserved = true;
576
731
  return true;
577
732
  }
733
+ #createDelivery(state, execution, receipt, signal) {
734
+ const native = this.#retainedExecution.getStore()?.native === true && 'operationId' in execution
735
+ ? execution
736
+ : undefined;
737
+ const sourceSignal = native === undefined
738
+ ? undefined
739
+ : signal === undefined
740
+ ? native.signal
741
+ : AbortSignal.any([native.signal, signal]);
742
+ const controller = new AbortController();
743
+ const release = () => {
744
+ sourceSignal?.removeEventListener('abort', abort);
745
+ this.#pendingDeliveries.delete(pending);
746
+ };
747
+ const delivery = {
748
+ accepted: () => {
749
+ pending.admitted = true;
750
+ },
751
+ messageId: receipt.messageId,
752
+ ...(native?.origin === undefined ? {} : { origin: native.origin }),
753
+ ...(native === undefined ? {} : { signal: controller.signal }),
754
+ applied: () => {
755
+ // A durable commit remains applied even if cancellation races its completion callback.
756
+ receipt.deliveryStatus = 'applied';
757
+ release();
758
+ },
759
+ settled: (status) => {
760
+ if (receipt.deliveryStatus !== 'accepted')
761
+ return;
762
+ receipt.deliveryStatus = status;
763
+ controller.abort(new MarAgentError('MAR_AGENT_CANCELLED', 'Agent message was cancelled.'));
764
+ release();
765
+ }
766
+ };
767
+ const pending = {
768
+ state,
769
+ receipt,
770
+ delivery,
771
+ admitted: false,
772
+ ...(native?.ownerInstanceId === undefined ? {} : { ownerInstanceId: native.ownerInstanceId })
773
+ };
774
+ const abort = () => delivery.settled?.('cancelled');
775
+ this.#pendingDeliveries.add(pending);
776
+ sourceSignal?.addEventListener('abort', abort, { once: true });
777
+ if (sourceSignal?.aborted)
778
+ abort();
779
+ return delivery;
780
+ }
578
781
  #closeReceipt(state) {
579
- if (state.receipt?.deliveryStatus === 'accepted' && state.receipt.turnId === state.turn?.turnId)
580
- state.receipt.deliveryStatus = state.status === 'cancelled' ? 'cancelled' : 'failed';
782
+ for (const pending of this.#pendingDeliveries)
783
+ if (pending.admitted &&
784
+ pending.state === state &&
785
+ pending.receipt.turnId === state.turn?.turnId)
786
+ pending.delivery.settled?.(state.status === 'cancelled' ? 'cancelled' : 'failed');
581
787
  }
582
788
  async #deleteUnstartedSession(agentId, error) {
583
789
  this.#agents.delete(agentId);
@@ -608,6 +814,8 @@ export class SubagentSessionController {
608
814
  const execution = this.#retainedExecution.getStore()?.execution ?? this.#execution;
609
815
  if (!execution)
610
816
  throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Subagent control requires an active parent execution.');
817
+ if ('signal' in execution)
818
+ execution.signal.throwIfAborted();
611
819
  return execution;
612
820
  }
613
821
  #receiptSnapshot(state) {
@@ -631,15 +839,22 @@ export class SubagentSessionController {
631
839
  const includeLatestMessage = options.includeLatestMessage ?? true;
632
840
  const includeLatestActivity = options.includeLatestActivity ?? true;
633
841
  const includeTerminalDetails = options.includeTerminalDetails ?? true;
842
+ const full = options.full ?? this.#retainedExecution.getStore()?.native === true;
634
843
  const latestMessage = !includeLatestMessage || state.latestMessage === undefined
635
844
  ? undefined
636
- : terminal
845
+ : full
637
846
  ? { ...state.latestMessage }
638
- : compactRunningLatestMessage(state.latestMessage);
847
+ : projectLatestMessage(state.latestMessage, terminal);
639
848
  return {
640
849
  agentId: state.agentId,
641
850
  description: state.description,
642
851
  status: state.status,
852
+ ...(full && this.#retainedExecution.getStore()?.native === true
853
+ ? {
854
+ role: state.role,
855
+ ...(state.extensionId === undefined ? {} : { extensionId: state.extensionId })
856
+ }
857
+ : {}),
643
858
  ...state.receipt,
644
859
  ...(state.modelId === undefined ? {} : { modelId: state.modelId }),
645
860
  ...(state.modelName === undefined ? {} : { modelName: state.modelName }),
@@ -648,16 +863,36 @@ export class SubagentSessionController {
648
863
  ...(!includeLatestActivity || state.latestActivity === undefined
649
864
  ? {}
650
865
  : { latestActivity: { ...state.latestActivity } }),
651
- ...(!includeTerminalDetails || state.summary === undefined ? {} : { summary: state.summary }),
866
+ ...(!includeTerminalDetails || state.summary === undefined
867
+ ? {}
868
+ : {
869
+ summary: full
870
+ ? state.summary
871
+ : truncateUtf8(state.summary, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes)
872
+ }),
652
873
  ...(!includeTerminalDetails || !terminal || state.changedFiles.length === 0
653
874
  ? {}
654
- : { changedFiles: [...state.changedFiles] }),
875
+ : {
876
+ changedFiles: full
877
+ ? [...state.changedFiles]
878
+ : state.changedFiles.slice(0, TOOL_EXECUTION_LIMITS.subagentChangedFilesMaxItems)
879
+ }),
655
880
  ...(!includeTerminalDetails || !terminal || state.verification.length === 0
656
881
  ? {}
657
- : { verification: [...state.verification] }),
882
+ : {
883
+ verification: full
884
+ ? [...state.verification]
885
+ : state.verification
886
+ .slice(0, TOOL_EXECUTION_LIMITS.subagentVerificationMaxItems)
887
+ .map((text) => truncateUtf8(text, 2_000))
888
+ }),
658
889
  ...(!includeTerminalDetails || !terminal || state.evidence.length === 0
659
890
  ? {}
660
- : { evidence: state.evidence.map((item) => ({ ...item })) }),
891
+ : {
892
+ evidence: (full
893
+ ? state.evidence
894
+ : state.evidence.slice(0, TOOL_EXECUTION_LIMITS.subagentEvidenceMaxItems)).map((item) => ({ ...item }))
895
+ }),
661
896
  ...(state.errorCode === undefined ? {} : { errorCode: state.errorCode }),
662
897
  ...(state.errorMessage === undefined ? {} : { errorMessage: state.errorMessage })
663
898
  };
@@ -666,7 +901,7 @@ export class SubagentSessionController {
666
901
  if (!state.terminalPending || !this.#execution)
667
902
  return;
668
903
  try {
669
- await this.#execution.emitTerminal(this.#snapshot(state));
904
+ await this.#execution.emitTerminal(this.#snapshot(state, { full: false }));
670
905
  state.terminalPending = false;
671
906
  }
672
907
  catch {
@@ -674,25 +909,31 @@ export class SubagentSessionController {
674
909
  }
675
910
  }
676
911
  }
677
- function compactRunningLatestMessage(message) {
678
- if (message.phase !== 'commentary')
679
- return { ...message };
680
- const text = truncateCharacters(message.text, TOOL_EXECUTION_LIMITS.subagentRunningLatestMessageMaxCharacters);
912
+ function projectLatestMessage(message, terminal) {
913
+ const text = truncateCharacters(message.text, !terminal && message.phase === 'commentary'
914
+ ? TOOL_EXECUTION_LIMITS.subagentRunningLatestMessageMaxCharacters
915
+ : 10_000);
681
916
  return {
682
917
  ...message,
683
918
  text,
684
919
  truncated: message.truncated === true || text !== message.text
685
920
  };
686
921
  }
687
- function validateTaskInput(input) {
688
- if (input.parentSubagentId ||
922
+ function validateTaskInput(input, native = false) {
923
+ if (!input ||
924
+ typeof input !== 'object' ||
925
+ typeof input.description !== 'string' ||
926
+ typeof input.prompt !== 'string' ||
927
+ (input.role !== 'general-purpose' && input.role !== 'explore') ||
928
+ input.parentSubagentId ||
689
929
  !input.description.trim() ||
690
- input.description.length > TOOL_EXECUTION_LIMITS.subagentDescriptionMaxCharacters ||
930
+ (!native &&
931
+ input.description.length > TOOL_EXECUTION_LIMITS.subagentDescriptionMaxCharacters) ||
691
932
  !input.prompt.trim() ||
692
- Buffer.byteLength(input.prompt) > TOOL_EXECUTION_LIMITS.subagentPromptMaxBytes)
693
- throw new MarAgentError(input.parentSubagentId
933
+ (!native && Buffer.byteLength(input.prompt) > TOOL_EXECUTION_LIMITS.subagentPromptMaxBytes))
934
+ throw new MarAgentError(input?.parentSubagentId
694
935
  ? 'MAR_AGENT_SUBAGENT_RECURSION_DISABLED'
695
- : 'MAR_AGENT_TOOL_INPUT_INVALID', input.parentSubagentId ? 'Subagents cannot create subagents.' : 'Subagent task is invalid.');
936
+ : 'MAR_AGENT_TOOL_INPUT_INVALID', input?.parentSubagentId ? 'Subagents cannot create subagents.' : 'Subagent task is invalid.');
696
937
  }
697
938
  function resolveModel(execution, input) {
698
939
  const modelId = input.modelId ?? execution.modelId;
@@ -726,7 +967,7 @@ function resolveExistingModel(execution, state) {
726
967
  };
727
968
  }
728
969
  function restoreAgentState(session) {
729
- const delivered = session.records.findLast((record) => record.type === 'turn.user' &&
970
+ const delivered = session.records.findLast((record) => (record.type === 'turn.user' || record.type === 'extension.input') &&
730
971
  isRecord(record.payload) &&
731
972
  typeof record.payload.messageId === 'string' &&
732
973
  typeof record.turnId === 'string');
@@ -739,6 +980,12 @@ function restoreAgentState(session) {
739
980
  agentId: session.id,
740
981
  description: session.source.description,
741
982
  role: session.source.role,
983
+ ...(session.source.ownerInstanceId === undefined
984
+ ? {}
985
+ : { ownerInstanceId: session.source.ownerInstanceId }),
986
+ ...(session.source.extensionId === undefined
987
+ ? {}
988
+ : { extensionId: session.source.extensionId }),
742
989
  status: terminalType === 'execution.completed'
743
990
  ? 'completed'
744
991
  : terminalType === 'execution.failed'
@@ -780,7 +1027,7 @@ function restoreAgentState(session) {
780
1027
  }
781
1028
  state.observedMessageRevision = state.messageRevision;
782
1029
  if (state.status === 'completed' && state.latestMessage?.phase === 'final_answer')
783
- state.summary = truncateUtf8(state.latestMessage.text, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes);
1030
+ state.summary = state.latestMessage.text;
784
1031
  if (state.status === 'failed' && terminal && isRecord(terminal.payload)) {
785
1032
  if (typeof terminal.payload.code === 'string')
786
1033
  state.errorCode = terminal.payload.code;
@@ -832,10 +1079,7 @@ function observeAgentEvent(state, event) {
832
1079
  if (!isRecord(file))
833
1080
  continue;
834
1081
  for (const path of [file.path, file.targetPath])
835
- if (typeof path === 'string' &&
836
- path.length > 0 &&
837
- !state.changedFiles.includes(path) &&
838
- state.changedFiles.length < TOOL_EXECUTION_LIMITS.subagentChangedFilesMaxItems)
1082
+ if (typeof path === 'string' && path.length > 0 && !state.changedFiles.includes(path))
839
1083
  state.changedFiles.push(path);
840
1084
  }
841
1085
  }
@@ -843,10 +1087,7 @@ function observeAgentEvent(state, event) {
843
1087
  const data = event.data;
844
1088
  const results = Array.isArray(data.results) ? data.results : [];
845
1089
  for (const result of results) {
846
- if (!isRecord(result) ||
847
- typeof result.path !== 'string' ||
848
- result.error !== undefined ||
849
- state.evidence.length >= TOOL_EXECUTION_LIMITS.subagentEvidenceMaxItems)
1090
+ if (!isRecord(result) || typeof result.path !== 'string' || result.error !== undefined)
850
1091
  continue;
851
1092
  const evidence = {
852
1093
  path: result.path,
@@ -857,9 +1098,8 @@ function observeAgentEvent(state, event) {
857
1098
  state.evidence.push(evidence);
858
1099
  }
859
1100
  }
860
- else if (event.toolName === 'exec' &&
861
- state.verification.length < TOOL_EXECUTION_LIMITS.subagentVerificationMaxItems)
862
- state.verification.push(truncateUtf8(event.summary, 2_000));
1101
+ else if (event.toolName === 'exec')
1102
+ state.verification.push(event.summary);
863
1103
  return false;
864
1104
  }
865
1105
  function eventType(record) {
@@ -868,7 +1108,7 @@ function eventType(record) {
868
1108
  : undefined;
869
1109
  }
870
1110
  function latestMessageFromEvent(event) {
871
- const text = truncateCharacters(event.text, 10_000);
1111
+ const text = event.text;
872
1112
  return {
873
1113
  itemId: event.itemId,
874
1114
  turnId: event.turnId ?? '',
@@ -885,7 +1125,7 @@ function latestMessageFromPayload(payload) {
885
1125
  typeof payload.text !== 'string' ||
886
1126
  typeof payload.timestamp !== 'string')
887
1127
  return undefined;
888
- const text = truncateCharacters(payload.text, 10_000);
1128
+ const text = payload.text;
889
1129
  return {
890
1130
  itemId: payload.itemId,
891
1131
  turnId: payload.turnId,
@@ -931,7 +1171,7 @@ function waitForCompletionNotification(waiters, accepts, waitMs, signal) {
931
1171
  if (settled)
932
1172
  return;
933
1173
  settled = true;
934
- clearTimeout(timer);
1174
+ cancelTimer();
935
1175
  signal?.removeEventListener('abort', abort);
936
1176
  waiters.delete(notify);
937
1177
  if (error === undefined)
@@ -943,14 +1183,13 @@ function waitForCompletionNotification(waiters, accepts, waitMs, signal) {
943
1183
  if (!accepts(value) || settled)
944
1184
  return;
945
1185
  settled = true;
946
- clearTimeout(timer);
1186
+ cancelTimer();
947
1187
  signal?.removeEventListener('abort', abort);
948
1188
  waiters.delete(notify);
949
1189
  resolve(value);
950
1190
  };
951
1191
  const abort = () => finish(signal?.reason ?? new MarAgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.'));
952
- const timer = setTimeout(finish, waitMs);
953
- timer.unref();
1192
+ const cancelTimer = waitDeadline(waitMs, () => finish());
954
1193
  waiters.add(notify);
955
1194
  signal?.addEventListener('abort', abort, { once: true });
956
1195
  if (signal?.aborted)
@@ -964,7 +1203,7 @@ function waitForNotification(waiters, accepts, current, waitMs, signal) {
964
1203
  if (settled)
965
1204
  return;
966
1205
  settled = true;
967
- clearTimeout(timer);
1206
+ cancelTimer();
968
1207
  signal?.removeEventListener('abort', abort);
969
1208
  waiters.delete(notify);
970
1209
  if (error === undefined)
@@ -977,8 +1216,7 @@ function waitForNotification(waiters, accepts, current, waitMs, signal) {
977
1216
  finish(value);
978
1217
  };
979
1218
  const abort = () => finish(undefined, signal?.reason ?? new MarAgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.'));
980
- const timer = setTimeout(() => finish(), waitMs);
981
- timer.unref();
1219
+ const cancelTimer = waitDeadline(waitMs, () => finish());
982
1220
  waiters.add(notify);
983
1221
  signal?.addEventListener('abort', abort, { once: true });
984
1222
  if (signal?.aborted)
@@ -990,14 +1228,16 @@ function waitForNotification(waiters, accepts, current, waitMs, signal) {
990
1228
  }
991
1229
  });
992
1230
  }
993
- function validateOutputOptions(options) {
994
- if ((options.waitFor !== undefined &&
995
- options.waitFor !== 'completion' &&
996
- options.waitFor !== 'message') ||
1231
+ function validateOutputOptions(options, native = false) {
1232
+ if ((options.untilSettled !== undefined && options.untilSettled !== true) ||
1233
+ (options.untilSettled === true && options.waitFor === 'message') ||
1234
+ (options.waitFor !== undefined &&
1235
+ options.waitFor !== 'completion' &&
1236
+ options.waitFor !== 'message') ||
997
1237
  (options.waitMs !== undefined &&
998
- (!Number.isInteger(options.waitMs) ||
1238
+ (!Number.isSafeInteger(options.waitMs) ||
999
1239
  options.waitMs < TOOL_EXECUTION_LIMITS.agentWaitMinWaitMs ||
1000
- options.waitMs > TOOL_EXECUTION_LIMITS.agentWaitMaxWaitMs)))
1240
+ (!native && options.waitMs > TOOL_EXECUTION_LIMITS.agentWaitMaxWaitMs))))
1001
1241
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Task wait is invalid.');
1002
1242
  }
1003
1243
  function isTerminalState(state) {
@@ -1032,3 +1272,20 @@ function waitForPromise(promise, signal) {
1032
1272
  abort();
1033
1273
  });
1034
1274
  }
1275
+ /** Explicit waits retain their deadline without overflowing the platform timer. */
1276
+ function waitDeadline(waitMs, finish) {
1277
+ const deadline = performance.now() + waitMs;
1278
+ let timer;
1279
+ const tick = () => {
1280
+ const remaining = deadline - performance.now();
1281
+ if (remaining <= 0)
1282
+ finish();
1283
+ else
1284
+ arm(remaining);
1285
+ };
1286
+ const arm = (remaining) => {
1287
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
1288
+ };
1289
+ arm(waitMs);
1290
+ return () => clearTimeout(timer);
1291
+ }