@myagentroam/agent 0.11.34 → 0.11.35
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/session/store/jsonl/fork-session.d.ts +19 -0
- package/dist/session/store/jsonl/fork-session.js +142 -0
- package/dist/session/store/jsonl/jsonl-migration.js +1 -1
- package/dist/session/store/jsonl/jsonl-store.js +70 -157
- package/dist/subagent/session-controller.d.ts +2 -63
- package/dist/subagent/session-controller.js +35 -453
- package/dist/subagent/task.d.ts +134 -0
- package/dist/subagent/task.js +436 -0
- package/dist/subagent/turn-contracts.d.ts +64 -0
- package/dist/subagent/turn-contracts.js +1 -0
- package/package.json +1 -1
|
@@ -4,295 +4,8 @@ import { randomUUID } from 'node:crypto';
|
|
|
4
4
|
import { AgentError, describeAgentError } from '../error.js';
|
|
5
5
|
import { SUBAGENT_LIMITS } from './limits.js';
|
|
6
6
|
import { parseAgentWaitTargets, SubagentWaitCoordinator, waitForAgentGroup } from './wait.js';
|
|
7
|
+
import { SubagentTask, observeAgentEvent } from './task.js';
|
|
7
8
|
const RECENT_TERMINAL_RESOURCE_LIMIT = 16;
|
|
8
|
-
/** Owns the mutable state and transitions of one child task. */
|
|
9
|
-
class SubagentTask {
|
|
10
|
-
state;
|
|
11
|
-
get detailsEvicted() {
|
|
12
|
-
return this.state.detailsEvicted;
|
|
13
|
-
}
|
|
14
|
-
get restorable() {
|
|
15
|
-
return this.state.restorable;
|
|
16
|
-
}
|
|
17
|
-
get permissionStop() {
|
|
18
|
-
return this.state.permissionStop;
|
|
19
|
-
}
|
|
20
|
-
get agentId() {
|
|
21
|
-
return this.state.agentId;
|
|
22
|
-
}
|
|
23
|
-
get description() {
|
|
24
|
-
return this.state.description;
|
|
25
|
-
}
|
|
26
|
-
get role() {
|
|
27
|
-
return this.state.role;
|
|
28
|
-
}
|
|
29
|
-
get ownerInstanceId() {
|
|
30
|
-
return this.state.ownerInstanceId;
|
|
31
|
-
}
|
|
32
|
-
get extensionId() {
|
|
33
|
-
return this.state.extensionId;
|
|
34
|
-
}
|
|
35
|
-
get status() {
|
|
36
|
-
return this.state.status;
|
|
37
|
-
}
|
|
38
|
-
get modelId() {
|
|
39
|
-
return this.state.modelId;
|
|
40
|
-
}
|
|
41
|
-
get modelName() {
|
|
42
|
-
return this.state.modelName;
|
|
43
|
-
}
|
|
44
|
-
get reasoningEffort() {
|
|
45
|
-
return this.state.reasoningEffort;
|
|
46
|
-
}
|
|
47
|
-
get latestMessage() {
|
|
48
|
-
return this.state.latestMessage;
|
|
49
|
-
}
|
|
50
|
-
get latestActivity() {
|
|
51
|
-
return this.state.latestActivity;
|
|
52
|
-
}
|
|
53
|
-
get messageRevision() {
|
|
54
|
-
return this.state.messageRevision;
|
|
55
|
-
}
|
|
56
|
-
get observedMessageRevision() {
|
|
57
|
-
return this.state.observedMessageRevision;
|
|
58
|
-
}
|
|
59
|
-
get latestMessageActivityRevision() {
|
|
60
|
-
return this.state.latestMessageActivityRevision;
|
|
61
|
-
}
|
|
62
|
-
get summary() {
|
|
63
|
-
return this.state.summary;
|
|
64
|
-
}
|
|
65
|
-
get changedFiles() {
|
|
66
|
-
return this.state.changedFiles;
|
|
67
|
-
}
|
|
68
|
-
get verification() {
|
|
69
|
-
return this.state.verification;
|
|
70
|
-
}
|
|
71
|
-
get evidence() {
|
|
72
|
-
return this.state.evidence;
|
|
73
|
-
}
|
|
74
|
-
get errorCode() {
|
|
75
|
-
return this.state.errorCode;
|
|
76
|
-
}
|
|
77
|
-
get errorMessage() {
|
|
78
|
-
return this.state.errorMessage;
|
|
79
|
-
}
|
|
80
|
-
get terminalPending() {
|
|
81
|
-
return this.state.terminalPending;
|
|
82
|
-
}
|
|
83
|
-
get observedTerminalRevision() {
|
|
84
|
-
return this.state.observedTerminalRevision;
|
|
85
|
-
}
|
|
86
|
-
get terminalRevision() {
|
|
87
|
-
return this.state.terminalRevision;
|
|
88
|
-
}
|
|
89
|
-
get resourceRevision() {
|
|
90
|
-
return this.state.resourceRevision;
|
|
91
|
-
}
|
|
92
|
-
get turn() {
|
|
93
|
-
return this.state.turn;
|
|
94
|
-
}
|
|
95
|
-
get controlTail() {
|
|
96
|
-
return this.state.controlTail;
|
|
97
|
-
}
|
|
98
|
-
get receipt() {
|
|
99
|
-
return this.state.receipt;
|
|
100
|
-
}
|
|
101
|
-
constructor(state) {
|
|
102
|
-
this.state = state;
|
|
103
|
-
}
|
|
104
|
-
acknowledgeMessage(revision) {
|
|
105
|
-
this.state.observedMessageRevision = Math.max(this.state.observedMessageRevision, revision);
|
|
106
|
-
}
|
|
107
|
-
acknowledgeTerminal(revision) {
|
|
108
|
-
this.state.observedTerminalRevision = Math.max(this.state.observedTerminalRevision, revision);
|
|
109
|
-
}
|
|
110
|
-
selectModel(model) {
|
|
111
|
-
this.state.modelId = model.id;
|
|
112
|
-
this.state.modelName = model.name;
|
|
113
|
-
if (model.reasoningEffort !== undefined)
|
|
114
|
-
this.state.reasoningEffort = model.reasoningEffort;
|
|
115
|
-
}
|
|
116
|
-
setReceipt(receipt) {
|
|
117
|
-
this.state.receipt = receipt;
|
|
118
|
-
}
|
|
119
|
-
updateDelivery(receipt, status) {
|
|
120
|
-
if (status !== 'applied' && receipt.deliveryStatus !== 'accepted')
|
|
121
|
-
return false;
|
|
122
|
-
receipt.deliveryStatus = status;
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
requestCancel(expected) {
|
|
126
|
-
const turn = this.state.turn;
|
|
127
|
-
if (turn !== expected)
|
|
128
|
-
return;
|
|
129
|
-
if (turn.handle)
|
|
130
|
-
turn.handle.cancel();
|
|
131
|
-
else
|
|
132
|
-
turn.cancelRequested = true;
|
|
133
|
-
}
|
|
134
|
-
attachHandle(expected, handle) {
|
|
135
|
-
const turn = this.state.turn;
|
|
136
|
-
if (turn !== expected)
|
|
137
|
-
return;
|
|
138
|
-
turn.handle = handle;
|
|
139
|
-
}
|
|
140
|
-
cancelPending(expected) {
|
|
141
|
-
const turn = this.state.turn;
|
|
142
|
-
if (turn === expected && turn.cancelRequested)
|
|
143
|
-
turn.handle.cancel();
|
|
144
|
-
}
|
|
145
|
-
takePendingMessages(expected) {
|
|
146
|
-
const turn = this.state.turn;
|
|
147
|
-
return turn === expected ? turn.pendingMessages.splice(0) : [];
|
|
148
|
-
}
|
|
149
|
-
settleHandleReady(expected) {
|
|
150
|
-
if (this.state.turn === expected)
|
|
151
|
-
this.state.turn.resolveHandleReady();
|
|
152
|
-
}
|
|
153
|
-
settleCompletion(expected) {
|
|
154
|
-
if (this.state.turn === expected)
|
|
155
|
-
this.state.turn.resolve();
|
|
156
|
-
}
|
|
157
|
-
appendMessage(message) {
|
|
158
|
-
this.state.turn.pendingMessages.push(message);
|
|
159
|
-
}
|
|
160
|
-
control(operation) {
|
|
161
|
-
const result = (this.state.controlTail ?? Promise.resolve()).then(operation);
|
|
162
|
-
const tail = result.then(() => undefined, () => undefined);
|
|
163
|
-
this.state.controlTail = tail;
|
|
164
|
-
void tail.then(() => {
|
|
165
|
-
if (this.state.controlTail === tail)
|
|
166
|
-
delete this.state.controlTail;
|
|
167
|
-
});
|
|
168
|
-
return result;
|
|
169
|
-
}
|
|
170
|
-
cancelQueued(publish) {
|
|
171
|
-
this.state.status = 'cancelled';
|
|
172
|
-
if (publish)
|
|
173
|
-
this.state.terminalPending = true;
|
|
174
|
-
}
|
|
175
|
-
queueTurn(input, onComplete) {
|
|
176
|
-
let resolve;
|
|
177
|
-
let resolveHandleReady;
|
|
178
|
-
const completion = new Promise((done) => (resolve = done));
|
|
179
|
-
const handleReady = new Promise((done) => (resolveHandleReady = done));
|
|
180
|
-
const turn = {
|
|
181
|
-
...input,
|
|
182
|
-
handleReady,
|
|
183
|
-
completion,
|
|
184
|
-
resolve: () => {
|
|
185
|
-
onComplete();
|
|
186
|
-
resolve();
|
|
187
|
-
},
|
|
188
|
-
resolveHandleReady
|
|
189
|
-
};
|
|
190
|
-
this.state.status = 'queued';
|
|
191
|
-
this.state.restorable = false;
|
|
192
|
-
delete this.state.summary;
|
|
193
|
-
delete this.state.permissionStop;
|
|
194
|
-
delete this.state.errorCode;
|
|
195
|
-
delete this.state.errorMessage;
|
|
196
|
-
this.state.changedFiles = [];
|
|
197
|
-
this.state.verification = [];
|
|
198
|
-
this.state.evidence = [];
|
|
199
|
-
this.state.terminalPending = false;
|
|
200
|
-
this.state.turn = turn;
|
|
201
|
-
return turn;
|
|
202
|
-
}
|
|
203
|
-
markRunning() {
|
|
204
|
-
this.state.status = 'running';
|
|
205
|
-
}
|
|
206
|
-
clearTurn(expected) {
|
|
207
|
-
if (expected === undefined || this.state.turn === expected)
|
|
208
|
-
delete this.state.turn;
|
|
209
|
-
}
|
|
210
|
-
observeEvent(event, revision) {
|
|
211
|
-
if (!observeAgentEvent(this.state, event))
|
|
212
|
-
return false;
|
|
213
|
-
this.state.messageRevision++;
|
|
214
|
-
this.state.latestMessageActivityRevision = revision;
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
complete(result, terminal, persisted, previousActivity, previousRevision) {
|
|
218
|
-
if (result.status === 'completed') {
|
|
219
|
-
this.state.status = 'completed';
|
|
220
|
-
this.state.summary = result.finalAnswer;
|
|
221
|
-
if (result.permissionStop)
|
|
222
|
-
this.state.permissionStop = result.permissionStop;
|
|
223
|
-
else
|
|
224
|
-
delete this.state.permissionStop;
|
|
225
|
-
}
|
|
226
|
-
else if (result.status === 'cancelled')
|
|
227
|
-
this.state.status = 'cancelled';
|
|
228
|
-
else {
|
|
229
|
-
this.state.status = 'failed';
|
|
230
|
-
this.state.errorCode = result.errorCode ?? 'MAR_AGENT_SUBAGENT_FAILED';
|
|
231
|
-
this.state.errorMessage = result.errorMessage ?? 'Subagent execution failed.';
|
|
232
|
-
}
|
|
233
|
-
this.state.restorable =
|
|
234
|
-
persisted &&
|
|
235
|
-
terminal === this.state.status &&
|
|
236
|
-
(previousActivity === undefined || this.state.latestActivity !== previousActivity) &&
|
|
237
|
-
(this.state.latestMessage === undefined || this.state.messageRevision > previousRevision) &&
|
|
238
|
-
(this.state.status !== 'completed' ||
|
|
239
|
-
(this.state.latestMessage?.phase === 'final_answer' &&
|
|
240
|
-
this.state.summary === this.state.latestMessage.text));
|
|
241
|
-
this.state.terminalPending = true;
|
|
242
|
-
}
|
|
243
|
-
fail(error) {
|
|
244
|
-
const diagnostic = describeAgentError(error, 'MAR_AGENT_SUBAGENT_FAILED');
|
|
245
|
-
this.state.status = 'failed';
|
|
246
|
-
this.state.restorable = false;
|
|
247
|
-
this.state.errorCode = diagnostic.code;
|
|
248
|
-
this.state.errorMessage = diagnostic.message;
|
|
249
|
-
this.state.terminalPending = true;
|
|
250
|
-
}
|
|
251
|
-
touch(revision, terminal = false) {
|
|
252
|
-
this.state.resourceRevision = revision;
|
|
253
|
-
if (terminal)
|
|
254
|
-
this.state.terminalRevision = revision;
|
|
255
|
-
}
|
|
256
|
-
terminalPublished(revision) {
|
|
257
|
-
if (this.state.terminalRevision === revision)
|
|
258
|
-
this.state.terminalPending = false;
|
|
259
|
-
}
|
|
260
|
-
evictDetails() {
|
|
261
|
-
for (const key of DETAIL_FIELDS)
|
|
262
|
-
delete this.state[key];
|
|
263
|
-
this.state.changedFiles = [];
|
|
264
|
-
this.state.verification = [];
|
|
265
|
-
this.state.evidence = [];
|
|
266
|
-
this.state.detailsEvicted = true;
|
|
267
|
-
}
|
|
268
|
-
restoreDetails(restored) {
|
|
269
|
-
for (const key of DETAIL_FIELDS) {
|
|
270
|
-
if (key in restored)
|
|
271
|
-
Object.assign(this.state, { [key]: restored[key] });
|
|
272
|
-
else
|
|
273
|
-
delete this.state[key];
|
|
274
|
-
}
|
|
275
|
-
delete this.state.detailsEvicted;
|
|
276
|
-
}
|
|
277
|
-
copyWaitState() {
|
|
278
|
-
return {
|
|
279
|
-
...this.state,
|
|
280
|
-
changedFiles: [...this.state.changedFiles],
|
|
281
|
-
verification: [...this.state.verification],
|
|
282
|
-
evidence: this.state.evidence.map((item) => ({ ...item }))
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
const DETAIL_FIELDS = [
|
|
287
|
-
'latestMessage',
|
|
288
|
-
'latestActivity',
|
|
289
|
-
'summary',
|
|
290
|
-
'changedFiles',
|
|
291
|
-
'verification',
|
|
292
|
-
'evidence',
|
|
293
|
-
'permissionStop',
|
|
294
|
-
'errorMessage'
|
|
295
|
-
];
|
|
296
9
|
export class SubagentSessionController {
|
|
297
10
|
parentSessionId;
|
|
298
11
|
rootSessionId;
|
|
@@ -872,20 +585,24 @@ export class SubagentSessionController {
|
|
|
872
585
|
options.signal?.throwIfAborted();
|
|
873
586
|
// Resolve before cancelling so an unavailable model does not stop valid work.
|
|
874
587
|
const selected = resolveExistingModel(execution, state);
|
|
875
|
-
if (options.delivery === 'replace')
|
|
876
|
-
await this.#cancelState(state);
|
|
877
|
-
this.#requireExecution();
|
|
878
|
-
options.signal?.throwIfAborted();
|
|
879
588
|
const turnId = state.turn?.turnId ?? randomUUID();
|
|
880
589
|
const receipt = { messageId: randomUUID(), turnId, deliveryStatus: 'accepted' };
|
|
881
590
|
const mutableReceipt = receipt;
|
|
882
|
-
const delivery = this.#createDelivery(state, execution, mutableReceipt, options.signal);
|
|
591
|
+
const { delivery, waitSignal } = this.#createDelivery(state, execution, mutableReceipt, options.signal);
|
|
883
592
|
try {
|
|
593
|
+
const sendSignal = options.signal === undefined
|
|
594
|
+
? waitSignal
|
|
595
|
+
: AbortSignal.any([waitSignal, options.signal]);
|
|
596
|
+
sendSignal.throwIfAborted();
|
|
597
|
+
if (options.delivery === 'replace')
|
|
598
|
+
await waitForPromise(this.#cancelState(state), sendSignal);
|
|
599
|
+
this.#requireExecution();
|
|
600
|
+
sendSignal.throwIfAborted();
|
|
601
|
+
if (state.turn && state.status !== 'running' && state.status !== 'queued') {
|
|
602
|
+
await waitForPromise(state.turn.completion, sendSignal);
|
|
603
|
+
}
|
|
884
604
|
if (state.status === 'running' && state.turn) {
|
|
885
605
|
const turn = state.turn;
|
|
886
|
-
const sendSignal = delivery.signal && options.signal
|
|
887
|
-
? AbortSignal.any([delivery.signal, options.signal])
|
|
888
|
-
: (delivery.signal ?? options.signal);
|
|
889
606
|
if (!turn.handle)
|
|
890
607
|
await waitForPromise(turn.handleReady, sendSignal);
|
|
891
608
|
this.#requireExecution();
|
|
@@ -949,10 +666,9 @@ export class SubagentSessionController {
|
|
|
949
666
|
this.#touchResource(state, true);
|
|
950
667
|
this.#closeReceipt(state);
|
|
951
668
|
await this.#publishTerminal(state);
|
|
952
|
-
state.
|
|
953
|
-
state.settleCompletion(turn);
|
|
669
|
+
state.settleQueuedTurn(turn);
|
|
954
670
|
this.#notifyCompletion(state);
|
|
955
|
-
state.
|
|
671
|
+
state.releaseTurn(turn);
|
|
956
672
|
this.#drain();
|
|
957
673
|
}
|
|
958
674
|
else if (state.status === 'running' && state.turn) {
|
|
@@ -991,12 +707,11 @@ export class SubagentSessionController {
|
|
|
991
707
|
state.cancelQueued(false);
|
|
992
708
|
this.#closeReceipt(state);
|
|
993
709
|
if (turn) {
|
|
994
|
-
state.
|
|
995
|
-
state.settleCompletion(turn);
|
|
710
|
+
state.settleQueuedTurn(turn);
|
|
996
711
|
}
|
|
997
712
|
this.#notifyCompletion(state);
|
|
998
713
|
if (turn)
|
|
999
|
-
state.
|
|
714
|
+
state.releaseTurn(turn);
|
|
1000
715
|
}
|
|
1001
716
|
for (const state of this.#agents.values()) {
|
|
1002
717
|
if (state.status !== 'running' || !state.turn)
|
|
@@ -1048,96 +763,43 @@ export class SubagentSessionController {
|
|
|
1048
763
|
while (!this.#disposed && this.#running < this.limits.maxConcurrent && this.#queue.length > 0) {
|
|
1049
764
|
const state = this.#queue.shift();
|
|
1050
765
|
const turn = state.turn;
|
|
1051
|
-
if (!turn || state.
|
|
766
|
+
if (!turn || !state.markRunning(turn))
|
|
1052
767
|
continue;
|
|
1053
|
-
state.markRunning();
|
|
1054
768
|
this.#touchResource(state);
|
|
1055
769
|
this.#running++;
|
|
1056
770
|
void this.#startAndConsumeTurn(state, turn);
|
|
1057
771
|
}
|
|
1058
772
|
}
|
|
1059
773
|
async #startAndConsumeTurn(state, turn) {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
sessionId: state.agentId,
|
|
1066
|
-
prompt: turn.prompt,
|
|
1067
|
-
modelId: state.modelId,
|
|
1068
|
-
reasoningEffort: state.reasoningEffort,
|
|
1069
|
-
role: state.role,
|
|
1070
|
-
initialMailbox: state.takePendingMessages(turn)
|
|
1071
|
-
});
|
|
1072
|
-
state.attachHandle(turn, handle);
|
|
1073
|
-
}
|
|
1074
|
-
catch (error) {
|
|
1075
|
-
state.settleHandleReady(turn);
|
|
1076
|
-
await this.#failTurn(state, turn, error);
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
state.cancelPending(turn);
|
|
1080
|
-
state.settleHandleReady(turn);
|
|
1081
|
-
await this.#consumeTurn(state, turn);
|
|
1082
|
-
}
|
|
1083
|
-
async #consumeTurn(state, turn) {
|
|
1084
|
-
const previousActivity = state.latestActivity;
|
|
1085
|
-
const previousMessageRevision = state.messageRevision;
|
|
1086
|
-
let terminal;
|
|
1087
|
-
try {
|
|
1088
|
-
const consume = (async () => {
|
|
1089
|
-
for await (const event of turn.handle) {
|
|
1090
|
-
if (event.type === 'execution.completed')
|
|
1091
|
-
terminal = 'completed';
|
|
1092
|
-
else if (event.type === 'execution.failed')
|
|
1093
|
-
terminal = 'failed';
|
|
1094
|
-
else if (event.type === 'execution.cancelled')
|
|
1095
|
-
terminal = 'cancelled';
|
|
1096
|
-
if (!state.observeEvent(event, this.#activityRevision + 1))
|
|
1097
|
-
continue;
|
|
1098
|
-
this.#activityRevision++;
|
|
1099
|
-
this.#touchResource(state);
|
|
1100
|
-
this.#notifyActivity({ state, kind: 'message' });
|
|
1101
|
-
}
|
|
1102
|
-
})();
|
|
1103
|
-
const [resultOutcome, consumeOutcome] = await Promise.allSettled([
|
|
1104
|
-
Promise.resolve().then(() => turn.handle.result()),
|
|
1105
|
-
consume
|
|
1106
|
-
]);
|
|
1107
|
-
if (resultOutcome.status === 'rejected')
|
|
1108
|
-
throw resultOutcome.reason;
|
|
1109
|
-
if (consumeOutcome.status === 'rejected')
|
|
1110
|
-
throw consumeOutcome.reason;
|
|
1111
|
-
const result = resultOutcome.value;
|
|
1112
|
-
state.complete(result, terminal, turn.handle.persistedEvents === true, previousActivity, previousMessageRevision);
|
|
774
|
+
const outcome = await state.runTurn(turn, () => this.#activityRevision + 1, () => {
|
|
775
|
+
this.#activityRevision++;
|
|
776
|
+
this.#touchResource(state);
|
|
777
|
+
this.#notifyActivity({ state, kind: 'message' });
|
|
778
|
+
}, () => {
|
|
1113
779
|
this.#touchResource(state, true);
|
|
1114
780
|
this.#closeReceipt(state);
|
|
781
|
+
});
|
|
782
|
+
if (outcome === 'opening-failed') {
|
|
783
|
+
this.#running--;
|
|
784
|
+
state.settleTurn(turn);
|
|
785
|
+
state.releaseTurn(turn);
|
|
786
|
+
this.#notifyCompletion(state);
|
|
1115
787
|
await this.#publishTerminal(state);
|
|
788
|
+
this.#drain();
|
|
789
|
+
return;
|
|
1116
790
|
}
|
|
1117
|
-
|
|
1118
|
-
this.#setFailure(state, error);
|
|
1119
|
-
this.#closeReceipt(state);
|
|
791
|
+
try {
|
|
1120
792
|
await this.#publishTerminal(state);
|
|
1121
793
|
}
|
|
1122
794
|
finally {
|
|
1123
795
|
this.#closeReceipt(state);
|
|
1124
796
|
this.#running--;
|
|
1125
|
-
state.
|
|
797
|
+
state.settleTurn(turn);
|
|
1126
798
|
this.#notifyCompletion(state);
|
|
1127
|
-
state.
|
|
799
|
+
state.releaseTurn(turn);
|
|
1128
800
|
this.#drain();
|
|
1129
801
|
}
|
|
1130
802
|
}
|
|
1131
|
-
async #failTurn(state, turn, error) {
|
|
1132
|
-
this.#setFailure(state, error);
|
|
1133
|
-
this.#closeReceipt(state);
|
|
1134
|
-
this.#running--;
|
|
1135
|
-
state.settleCompletion(turn);
|
|
1136
|
-
state.clearTurn(turn);
|
|
1137
|
-
this.#notifyCompletion(state);
|
|
1138
|
-
await this.#publishTerminal(state);
|
|
1139
|
-
this.#drain();
|
|
1140
|
-
}
|
|
1141
803
|
#setFailure(state, error) {
|
|
1142
804
|
state.fail(error);
|
|
1143
805
|
this.#touchResource(state, true);
|
|
@@ -1255,7 +917,7 @@ export class SubagentSessionController {
|
|
|
1255
917
|
sourceSignal?.addEventListener('abort', abort, { once: true });
|
|
1256
918
|
if (sourceSignal?.aborted)
|
|
1257
919
|
abort();
|
|
1258
|
-
return delivery;
|
|
920
|
+
return { delivery, waitSignal: controller.signal };
|
|
1259
921
|
}
|
|
1260
922
|
#closeReceipt(state) {
|
|
1261
923
|
for (const pending of this.#pendingDeliveries)
|
|
@@ -1601,88 +1263,11 @@ function restoreAgentState(session) {
|
|
|
1601
1263
|
}
|
|
1602
1264
|
return state;
|
|
1603
1265
|
}
|
|
1604
|
-
function observeAgentEvent(state, event) {
|
|
1605
|
-
if (event.type === 'message.completed') {
|
|
1606
|
-
state.latestMessage = latestMessageFromEvent(event);
|
|
1607
|
-
state.latestActivity = {
|
|
1608
|
-
kind: 'message',
|
|
1609
|
-
timestamp: event.timestamp,
|
|
1610
|
-
phase: event.phase
|
|
1611
|
-
};
|
|
1612
|
-
return true;
|
|
1613
|
-
}
|
|
1614
|
-
if (event.type === 'tool.started') {
|
|
1615
|
-
state.latestActivity = {
|
|
1616
|
-
kind: 'tool',
|
|
1617
|
-
timestamp: event.timestamp,
|
|
1618
|
-
toolName: event.toolName,
|
|
1619
|
-
status: 'started'
|
|
1620
|
-
};
|
|
1621
|
-
return false;
|
|
1622
|
-
}
|
|
1623
|
-
if (event.type === 'tool.failed') {
|
|
1624
|
-
state.latestActivity = {
|
|
1625
|
-
kind: 'tool',
|
|
1626
|
-
timestamp: event.timestamp,
|
|
1627
|
-
toolName: event.toolName,
|
|
1628
|
-
status: 'failed'
|
|
1629
|
-
};
|
|
1630
|
-
return false;
|
|
1631
|
-
}
|
|
1632
|
-
if (event.type !== 'tool.completed')
|
|
1633
|
-
return false;
|
|
1634
|
-
state.latestActivity = {
|
|
1635
|
-
kind: 'tool',
|
|
1636
|
-
timestamp: event.timestamp,
|
|
1637
|
-
toolName: event.toolName,
|
|
1638
|
-
status: 'completed'
|
|
1639
|
-
};
|
|
1640
|
-
if (event.toolName === 'apply_patch' && isRecord(event.data)) {
|
|
1641
|
-
const data = event.data;
|
|
1642
|
-
const files = Array.isArray(data.files) ? data.files : [];
|
|
1643
|
-
for (const file of files) {
|
|
1644
|
-
if (!isRecord(file))
|
|
1645
|
-
continue;
|
|
1646
|
-
for (const path of [file.path, file.targetPath])
|
|
1647
|
-
if (typeof path === 'string' && path.length > 0 && !state.changedFiles.includes(path))
|
|
1648
|
-
state.changedFiles.push(path);
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
else if (event.toolName === 'read' && isRecord(event.data)) {
|
|
1652
|
-
const data = event.data;
|
|
1653
|
-
const results = Array.isArray(data.results) ? data.results : [];
|
|
1654
|
-
for (const result of results) {
|
|
1655
|
-
if (!isRecord(result) || typeof result.path !== 'string' || result.error !== undefined)
|
|
1656
|
-
continue;
|
|
1657
|
-
const evidence = {
|
|
1658
|
-
path: result.path,
|
|
1659
|
-
...(isPositiveInteger(result.startLine) ? { startLine: result.startLine } : {}),
|
|
1660
|
-
...(isPositiveInteger(result.endLine) ? { endLine: result.endLine } : {})
|
|
1661
|
-
};
|
|
1662
|
-
if (!state.evidence.some((item) => JSON.stringify(item) === JSON.stringify(evidence)))
|
|
1663
|
-
state.evidence.push(evidence);
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
else if (event.toolName === 'exec')
|
|
1667
|
-
state.verification.push(event.summary);
|
|
1668
|
-
return false;
|
|
1669
|
-
}
|
|
1670
1266
|
function eventType(record) {
|
|
1671
1267
|
return record && isRecord(record.payload) && typeof record.payload.type === 'string'
|
|
1672
1268
|
? record.payload.type
|
|
1673
1269
|
: undefined;
|
|
1674
1270
|
}
|
|
1675
|
-
function latestMessageFromEvent(event) {
|
|
1676
|
-
const text = event.text;
|
|
1677
|
-
return {
|
|
1678
|
-
itemId: event.itemId,
|
|
1679
|
-
turnId: event.turnId ?? '',
|
|
1680
|
-
phase: event.phase,
|
|
1681
|
-
text,
|
|
1682
|
-
timestamp: event.timestamp,
|
|
1683
|
-
truncated: text !== event.text
|
|
1684
|
-
};
|
|
1685
|
-
}
|
|
1686
1271
|
function isReasoningEffort(value) {
|
|
1687
1272
|
return (value === 'low' ||
|
|
1688
1273
|
value === 'medium' ||
|
|
@@ -1694,9 +1279,6 @@ function isReasoningEffort(value) {
|
|
|
1694
1279
|
function isRecord(value) {
|
|
1695
1280
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
1696
1281
|
}
|
|
1697
|
-
function isPositiveInteger(value) {
|
|
1698
|
-
return typeof value === 'number' && Number.isInteger(value) && value >= 1;
|
|
1699
|
-
}
|
|
1700
1282
|
function truncateUtf8(value, maximumBytes) {
|
|
1701
1283
|
const bytes = Buffer.from(value);
|
|
1702
1284
|
if (bytes.length <= maximumBytes)
|