@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.
- package/dist/agent-core/extensions/agents.d.ts +69 -0
- package/dist/agent-core/extensions/agents.js +160 -0
- package/dist/agent-core/extensions/contracts.d.ts +260 -0
- package/dist/agent-core/extensions/contracts.js +1 -0
- package/dist/agent-core/extensions/data.d.ts +84 -0
- package/dist/agent-core/extensions/data.js +83 -0
- package/dist/agent-core/extensions/environment.d.ts +27 -0
- package/dist/agent-core/extensions/environment.js +52 -0
- package/dist/agent-core/extensions/files.d.ts +64 -0
- package/dist/agent-core/extensions/files.js +26 -0
- package/dist/agent-core/extensions/hooks.d.ts +32 -0
- package/dist/agent-core/extensions/hooks.js +135 -0
- package/dist/agent-core/extensions/manager.d.ts +39 -0
- package/dist/agent-core/extensions/manager.js +458 -0
- package/dist/agent-core/extensions/mcp.d.ts +99 -0
- package/dist/agent-core/extensions/mcp.js +433 -0
- package/dist/agent-core/extensions/model-request.d.ts +60 -0
- package/dist/agent-core/extensions/model-request.js +167 -0
- package/dist/agent-core/extensions/model-stream.d.ts +9 -0
- package/dist/agent-core/extensions/model-stream.js +101 -0
- package/dist/agent-core/extensions/models.d.ts +43 -0
- package/dist/agent-core/extensions/models.js +1 -0
- package/dist/agent-core/extensions/network.d.ts +32 -0
- package/dist/agent-core/extensions/network.js +97 -0
- package/dist/agent-core/extensions/operation.d.ts +29 -0
- package/dist/agent-core/extensions/operation.js +98 -0
- package/dist/agent-core/extensions/processes.d.ts +52 -0
- package/dist/agent-core/extensions/processes.js +283 -0
- package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
- package/dist/agent-core/extensions/prompt-composition.js +106 -0
- package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
- package/dist/agent-core/extensions/prompt-input.js +46 -0
- package/dist/agent-core/extensions/resources.d.ts +27 -0
- package/dist/agent-core/extensions/resources.js +221 -0
- package/dist/agent-core/extensions/services.d.ts +31 -0
- package/dist/agent-core/extensions/services.js +61 -0
- package/dist/agent-core/extensions/session-input.d.ts +151 -0
- package/dist/agent-core/extensions/session-input.js +351 -0
- package/dist/agent-core/extensions/session-messages.d.ts +41 -0
- package/dist/agent-core/extensions/session-messages.js +76 -0
- package/dist/agent-core/extensions/session.d.ts +68 -0
- package/dist/agent-core/extensions/session.js +593 -0
- package/dist/agent-core/extensions/settings.d.ts +59 -0
- package/dist/agent-core/extensions/settings.js +115 -0
- package/dist/agent-core/extensions/store.d.ts +10 -0
- package/dist/agent-core/extensions/store.js +131 -0
- package/dist/agent-core/extensions/tools.d.ts +61 -0
- package/dist/agent-core/extensions/tools.js +266 -0
- package/dist/agent-core/host/contracts.d.ts +25 -0
- package/dist/agent-core/host/local-agent-host.d.ts +10 -1
- package/dist/agent-core/host/local-agent-host.js +132 -20
- package/dist/agent-core/host/local-file-access.d.ts +30 -0
- package/dist/agent-core/host/local-file-access.js +107 -0
- package/dist/agent-core/host/local-network-access.d.ts +6 -0
- package/dist/agent-core/host/local-network-access.js +97 -0
- package/dist/agent-core/host/local-process-executor.d.ts +5 -1
- package/dist/agent-core/host/local-session-environment.d.ts +18 -0
- package/dist/agent-core/host/local-session-environment.js +102 -0
- package/dist/agent-core/model/anthropic-messages.js +73 -23
- package/dist/agent-core/model/contracts.d.ts +21 -0
- package/dist/agent-core/model/openai-responses.d.ts +3 -1
- package/dist/agent-core/model/openai-responses.js +248 -97
- package/dist/agent-core/model/text-blocks.d.ts +7 -0
- package/dist/agent-core/model/text-blocks.js +40 -0
- package/dist/agent-core/prompts/index.d.ts +6 -2
- package/dist/agent-core/prompts/index.js +38 -24
- package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
- package/dist/agent-core/runtime/context/normalize.js +21 -1
- package/dist/agent-core/runtime/context/projection.js +35 -1
- package/dist/agent-core/runtime/context/request-budget.js +5 -2
- package/dist/agent-core/runtime/context/restore.d.ts +5 -0
- package/dist/agent-core/runtime/context/restore.js +47 -4
- package/dist/agent-core/runtime/context/usage.js +2 -3
- package/dist/agent-core/runtime/contracts.d.ts +2 -0
- package/dist/agent-core/runtime/execution.js +5 -2
- package/dist/agent-core/sdk/agent.js +177 -40
- package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
- package/dist/agent-core/sdk/extension-agents.js +51 -0
- package/dist/agent-core/sdk/extension-models.d.ts +13 -0
- package/dist/agent-core/sdk/extension-models.js +128 -0
- package/dist/agent-core/sdk/extension-session.d.ts +30 -0
- package/dist/agent-core/sdk/extension-session.js +103 -0
- package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
- package/dist/agent-core/sdk/extension-tools.js +126 -0
- package/dist/agent-core/sdk/session.d.ts +26 -2
- package/dist/agent-core/sdk/session.js +1215 -506
- package/dist/agent-core/sdk/types.d.ts +3 -0
- package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
- package/dist/agent-core/session/jsonl-store.d.ts +28 -8
- package/dist/agent-core/session/jsonl-store.js +124 -84
- package/dist/agent-core/session/types.d.ts +4 -1
- package/dist/agent-core/storage/file-lock.d.ts +4 -0
- package/dist/agent-core/storage/file-lock.js +83 -0
- package/dist/agent-core/subagent/contracts.d.ts +11 -1
- package/dist/agent-core/subagent/scheduler.d.ts +8 -0
- package/dist/agent-core/subagent/session-controller.d.ts +22 -1
- package/dist/agent-core/subagent/session-controller.js +355 -98
- package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/cells.js +5 -15
- package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
- package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
- package/dist/agent-core/tools/code-mode/index.js +1 -2
- package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
- package/dist/agent-core/tools/code-mode/observations.js +16 -5
- package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
- package/dist/agent-core/tools/execution.d.ts +11 -2
- package/dist/agent-core/tools/execution.js +58 -21
- package/dist/agent-core/tools/input.d.ts +2 -0
- package/dist/agent-core/tools/input.js +36 -0
- package/dist/agent-core/tools/local/apply-patch.js +21 -17
- package/dist/agent-core/tools/local/exec.d.ts +4 -4
- package/dist/agent-core/tools/local/exec.js +234 -62
- package/dist/agent-core/tools/local/registry.d.ts +6 -0
- package/dist/agent-core/tools/local/registry.js +60 -9
- package/dist/agent-core/tools/local/view-image.js +108 -2
- package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
- package/dist/agent-core/tools/mcp/catalog.js +4 -4
- package/dist/agent-core/tools/mcp/history.js +2 -2
- package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
- package/dist/agent-core/tools/mcp/tool-search.js +2 -2
- package/dist/agent-core/tools/output-projection.d.ts +3 -1
- package/dist/agent-core/tools/output-projection.js +11 -2
- package/dist/agent-core/tools/runtime-context.d.ts +1 -0
- package/dist/agent-core/tools/runtime.d.ts +3 -1
- package/dist/agent-core/tools/runtime.js +22 -7
- package/dist/agent-core/tools/scheduler.d.ts +2 -0
- package/dist/agent-core/tools/scheduler.js +43 -6
- package/dist/index.d.ts +21 -0
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
- 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
|
|
48
|
+
captureExecution(executionId) {
|
|
49
|
+
const enter = this.captureParentFrame(executionId);
|
|
48
50
|
return {
|
|
49
|
-
run: (input, options) =>
|
|
50
|
-
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
(
|
|
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
|
|
201
|
+
this.#observeTerminal(state);
|
|
132
202
|
return this.#snapshot(state);
|
|
133
203
|
}
|
|
134
|
-
this.#queueTurn(state, input.prompt, execution
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
300
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
state.
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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,
|
|
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 =
|
|
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 >
|
|
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
|
-
|
|
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
|
-
|
|
580
|
-
|
|
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
|
-
:
|
|
845
|
+
: full
|
|
637
846
|
? { ...state.latestMessage }
|
|
638
|
-
:
|
|
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
|
|
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
|
-
: {
|
|
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
|
-
: {
|
|
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
|
-
: {
|
|
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
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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.
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
995
|
-
options.waitFor
|
|
996
|
-
options.waitFor !==
|
|
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.
|
|
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
|
+
}
|