maka-agent 0.2.0-dev.75.20261008 → 0.2.0-dev.77.20261009
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/runtime/dist/agent-graph-supervisor-wake.js +115 -17
- package/node_modules/@maka/runtime/dist/agent-run.js +11 -0
- package/node_modules/@maka/runtime/dist/computer-use-observation-text.js +22 -0
- package/node_modules/@maka/runtime/dist/conversation-copy-agent-output.js +227 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +176 -46
- package/node_modules/@maka/runtime/dist/filesystem-worker/resource-resolver.js +3 -1
- package/node_modules/@maka/runtime/dist/owned-child-process.js +209 -0
- package/node_modules/@maka/runtime/dist/owned-process-main.js +183 -0
- package/node_modules/@maka/runtime/dist/pipe-process-driver.js +19 -25
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +124 -3
- package/node_modules/@maka/runtime/dist/session-manager.js +125 -12
- package/node_modules/@maka/runtime/dist/shell-exec.js +18 -20
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +2 -1
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +279 -8
- package/node_modules/@maka/runtime/dist/stream-graph-schedule-reconcile.js +209 -37
- package/node_modules/@maka/runtime/dist/tool-availability.js +37 -6
- package/node_modules/@maka/runtime/dist/tool-runtime.js +2 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +44 -13
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -0
- package/node_modules/@maka/runtime-host/dist/client/wsl-environment.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +4 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +46 -9
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +47 -40
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +71 -24
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +54 -13
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-runner.js +13 -6
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +41 -5
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/session-background-activity.js +71 -0
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +8 -4
- package/node_modules/@maka/runtime-host/dist/server/session-interaction-activity.js +78 -0
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +56 -0
- package/node_modules/@maka/runtime-host/dist/server/session-revision-graph-references.js +16 -2
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +10 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +3 -1
- package/node_modules/@maka/storage/dist/context-offload-store.js +8 -3
- package/node_modules/@maka/storage/dist/model-call-ledger.js +59 -1
- package/node_modules/@maka/storage/dist/model-call-usage-sql.js +23 -0
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +54 -19
- package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +17 -2
- package/node_modules/@maka/storage/dist/usage-stores.js +3 -0
- package/package.json +2 -2
- /package/node_modules/@maka/runtime/dist/workers/{filesystem-worker.js → filesystem-worker.mjs} +0 -0
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ https://incubator.apache.org/projects/maka.html
|
|
|
65
65
|
|
|
66
66
|
## Requirements
|
|
67
67
|
|
|
68
|
-
- Node.js 22.19.0 or newer;
|
|
68
|
+
- Node.js 22.19.0 or newer on the 22 line, or 24.0.0 or newer (Node 23 is not supported);
|
|
69
69
|
- a terminal with interactive input for the TUI;
|
|
70
70
|
- a configured model connection for agent turns; first-run setup currently supports API-key
|
|
71
71
|
providers.
|
package/README.zh-CN.md
CHANGED
|
@@ -39,7 +39,7 @@ release commit 中的 [DISCLAIMER-WIP](https://github.com/apache/maka/blob/main/
|
|
|
39
39
|
|
|
40
40
|
## 环境要求
|
|
41
41
|
|
|
42
|
-
- Node.js 22.19.0
|
|
42
|
+
- Node.js 22.19.0 或更高的 22.x 版本,或 24.0.0 及以上版本(不支持 Node 23);
|
|
43
43
|
- 使用 TUI 时需要支持交互输入的终端;
|
|
44
44
|
- 执行 Agent Turn 时需要已经配置的模型连接;首次设置目前支持使用 API Key 的供应商。
|
|
45
45
|
|
|
Binary file
|
|
@@ -83,6 +83,10 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
83
83
|
#tasks = new Set();
|
|
84
84
|
#tasksBySession = new Map();
|
|
85
85
|
#pendingWakeIds = new Set();
|
|
86
|
+
#sessionAttention = new Map();
|
|
87
|
+
#parkedWakeIds = new Map();
|
|
88
|
+
#activityVersions = new Map();
|
|
89
|
+
#publishedActivity = new Map();
|
|
86
90
|
#abortController = new AbortController();
|
|
87
91
|
#sessionAbortControllers = new Map();
|
|
88
92
|
#sessionWakeSuppressions = new Map();
|
|
@@ -95,18 +99,45 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
95
99
|
throw new Error('Agent graph supervisor wake attempts must be a positive safe integer');
|
|
96
100
|
}
|
|
97
101
|
}
|
|
102
|
+
/** Includes queued checkpoint delivery before a supervisor Turn is admitted. */
|
|
103
|
+
readSessionActivity(rootSessionId) {
|
|
104
|
+
if (this.#closed || this.#sessionWakesSuppressed(rootSessionId))
|
|
105
|
+
return 'idle';
|
|
106
|
+
const attention = this.#sessionAttention.get(rootSessionId);
|
|
107
|
+
if (attention === 'waiting_for_user')
|
|
108
|
+
return attention;
|
|
109
|
+
if (this.#tasksBySession.has(rootSessionId))
|
|
110
|
+
return 'running';
|
|
111
|
+
return attention ?? 'idle';
|
|
112
|
+
}
|
|
113
|
+
#publishSessionActivity(rootSessionId) {
|
|
114
|
+
const activity = this.readSessionActivity(rootSessionId);
|
|
115
|
+
if ((this.#publishedActivity.get(rootSessionId) ?? 'idle') === activity)
|
|
116
|
+
return;
|
|
117
|
+
if (activity === 'idle')
|
|
118
|
+
this.#publishedActivity.delete(rootSessionId);
|
|
119
|
+
else
|
|
120
|
+
this.#publishedActivity.set(rootSessionId, activity);
|
|
121
|
+
try {
|
|
122
|
+
this.#input.onSessionActivityChanged?.(rootSessionId);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// A catalog observer never controls wake delivery.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
98
128
|
notify(rootSessionId, result) {
|
|
99
129
|
if (this.#closed ||
|
|
100
130
|
this.#sessionWakesSuppressed(rootSessionId) ||
|
|
101
131
|
(!this.#input.shouldWake && (!result || !isAgentGraphSupervisorMilestone(result)))) {
|
|
102
132
|
return undefined;
|
|
103
133
|
}
|
|
104
|
-
return this.#runTracked(rootSessionId, async (abortSignal) => {
|
|
134
|
+
return this.#runTracked(rootSessionId, async (abortSignal, setAttention) => {
|
|
105
135
|
try {
|
|
106
|
-
await this.#wake(rootSessionId, abortSignal, result);
|
|
136
|
+
await this.#wake(rootSessionId, abortSignal, setAttention, result);
|
|
107
137
|
}
|
|
108
138
|
catch (error) {
|
|
109
139
|
if (!this.#closed && !isAbortError(error)) {
|
|
140
|
+
setAttention('blocked');
|
|
110
141
|
await notifyError(this.#input.onError, rootSessionId, error);
|
|
111
142
|
}
|
|
112
143
|
}
|
|
@@ -122,12 +153,15 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
122
153
|
notifyPermissionResponse(rootSessionId) {
|
|
123
154
|
if (this.#closed || this.#sessionWakesSuppressed(rootSessionId))
|
|
124
155
|
return undefined;
|
|
125
|
-
|
|
156
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
157
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
158
|
+
return this.#runTracked(rootSessionId, async (abortSignal, setAttention) => {
|
|
126
159
|
try {
|
|
127
|
-
await this.#settlePermissionResponse(rootSessionId, abortSignal);
|
|
160
|
+
await this.#settlePermissionResponse(rootSessionId, abortSignal, setAttention);
|
|
128
161
|
}
|
|
129
162
|
catch (error) {
|
|
130
163
|
if (!this.#closed && !isAbortError(error)) {
|
|
164
|
+
setAttention('blocked');
|
|
131
165
|
await notifyError(this.#input.onError, rootSessionId, error);
|
|
132
166
|
}
|
|
133
167
|
}
|
|
@@ -174,6 +208,11 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
174
208
|
return this.#input.activityRegistry.whenIdle(rootSessionId) !== undefined;
|
|
175
209
|
}
|
|
176
210
|
async retireSessions(rootSessionIds) {
|
|
211
|
+
for (const rootSessionId of rootSessionIds) {
|
|
212
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
213
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
214
|
+
this.#publishSessionActivity(rootSessionId);
|
|
215
|
+
}
|
|
177
216
|
return this.#input.wakeStore.supersedeAgentGraphSupervisorWakes({
|
|
178
217
|
rootSessionIds,
|
|
179
218
|
reason: 'session_retired',
|
|
@@ -184,19 +223,30 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
184
223
|
return;
|
|
185
224
|
this.#closed = true;
|
|
186
225
|
this.#abortController.abort();
|
|
226
|
+
for (const sessionId of [...this.#publishedActivity.keys()])
|
|
227
|
+
this.#publishSessionActivity(sessionId);
|
|
187
228
|
}
|
|
188
229
|
async close() {
|
|
189
230
|
this.beginDrain();
|
|
190
231
|
await this.waitForIdle();
|
|
191
232
|
this.#sessionAbortControllers.clear();
|
|
192
233
|
this.#tasksBySession.clear();
|
|
234
|
+
this.#sessionAttention.clear();
|
|
235
|
+
this.#parkedWakeIds.clear();
|
|
236
|
+
this.#activityVersions.clear();
|
|
193
237
|
}
|
|
194
|
-
async #wake(rootSessionId, abortSignal, result) {
|
|
238
|
+
async #wake(rootSessionId, abortSignal, setAttention, result) {
|
|
195
239
|
abortSignal.throwIfAborted();
|
|
196
240
|
if (!(await this.#isSessionDeliverable(rootSessionId)))
|
|
197
241
|
return;
|
|
198
242
|
const snapshot = await this.#input.readSnapshot(rootSessionId);
|
|
199
243
|
abortSignal.throwIfAborted();
|
|
244
|
+
const parkedWakeId = this.#parkedWakeIds.get(rootSessionId);
|
|
245
|
+
if (parkedWakeId &&
|
|
246
|
+
(snapshot.closed || parkedWakeId !== `${snapshot.graphId}:${snapshot.snapshotVersion}`)) {
|
|
247
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
248
|
+
setAttention('idle');
|
|
249
|
+
}
|
|
200
250
|
const wakeDecision = await this.#input.shouldWake?.(rootSessionId, result, snapshot);
|
|
201
251
|
abortSignal.throwIfAborted();
|
|
202
252
|
if (wakeDecision === false ||
|
|
@@ -217,12 +267,19 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
217
267
|
snapshotVersion: snapshot.snapshotVersion,
|
|
218
268
|
rootSessionId,
|
|
219
269
|
});
|
|
220
|
-
if (this.#closed ||
|
|
221
|
-
|
|
222
|
-
|
|
270
|
+
if (this.#closed || claimed.wake.status === 'exhausted')
|
|
271
|
+
return;
|
|
272
|
+
if (claimed.wake.status === 'delivered') {
|
|
273
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
274
|
+
setAttention('idle');
|
|
223
275
|
return;
|
|
276
|
+
}
|
|
277
|
+
if (claimed.wake.status === 'waiting_permission') {
|
|
278
|
+
this.#parkedWakeIds.set(rootSessionId, wakeId);
|
|
279
|
+
setAttention('waiting_for_user');
|
|
280
|
+
}
|
|
224
281
|
abortSignal.throwIfAborted();
|
|
225
|
-
await this.#deliverWake(claimed.wake, snapshot, abortSignal, result);
|
|
282
|
+
await this.#deliverWake(claimed.wake, snapshot, abortSignal, setAttention, result);
|
|
226
283
|
}
|
|
227
284
|
finally {
|
|
228
285
|
this.#pendingWakeIds.delete(wakeId);
|
|
@@ -232,12 +289,13 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
232
289
|
if (this.#closed || this.#pendingWakeIds.has(wake.wakeId))
|
|
233
290
|
return;
|
|
234
291
|
this.#pendingWakeIds.add(wake.wakeId);
|
|
235
|
-
void this.#runTracked(wake.rootSessionId, async (abortSignal) => {
|
|
292
|
+
void this.#runTracked(wake.rootSessionId, async (abortSignal, setAttention) => {
|
|
236
293
|
try {
|
|
237
|
-
await this.#resumeWake(wake, abortSignal);
|
|
294
|
+
await this.#resumeWake(wake, abortSignal, setAttention);
|
|
238
295
|
}
|
|
239
296
|
catch (error) {
|
|
240
297
|
if (!this.#closed && !isAbortError(error)) {
|
|
298
|
+
setAttention('blocked');
|
|
241
299
|
await notifyError(this.#input.onError, wake.rootSessionId, error);
|
|
242
300
|
}
|
|
243
301
|
}
|
|
@@ -247,19 +305,39 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
247
305
|
});
|
|
248
306
|
}
|
|
249
307
|
#runTracked(rootSessionId, operation) {
|
|
308
|
+
// Duplicate checkpoint notifications share the in-flight generation. They
|
|
309
|
+
// cannot invalidate the outcome of the attempt already holding the wake.
|
|
310
|
+
const version = (this.#activityVersions.get(rootSessionId) ?? 0) +
|
|
311
|
+
(this.#tasksBySession.has(rootSessionId) ? 0 : 1);
|
|
312
|
+
this.#activityVersions.set(rootSessionId, version);
|
|
313
|
+
if (this.#sessionAttention.get(rootSessionId) !== 'waiting_for_user') {
|
|
314
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
315
|
+
}
|
|
316
|
+
const setAttention = (activity) => {
|
|
317
|
+
if (this.#closed ||
|
|
318
|
+
this.#sessionWakesSuppressed(rootSessionId) ||
|
|
319
|
+
this.#activityVersions.get(rootSessionId) !== version)
|
|
320
|
+
return;
|
|
321
|
+
if (activity === 'idle')
|
|
322
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
323
|
+
else
|
|
324
|
+
this.#sessionAttention.set(rootSessionId, activity);
|
|
325
|
+
this.#publishSessionActivity(rootSessionId);
|
|
326
|
+
};
|
|
250
327
|
const residency = this.#input.acquireResidency?.(rootSessionId);
|
|
251
328
|
const abortSignal = this.#sessionAbortSignal(rootSessionId);
|
|
252
329
|
const task = Promise.resolve()
|
|
253
|
-
.then(() => operation(abortSignal))
|
|
330
|
+
.then(() => operation(abortSignal, setAttention))
|
|
254
331
|
.finally(() => residency?.release());
|
|
255
332
|
this.#tasks.add(task);
|
|
256
333
|
const sessionTasks = this.#tasksBySession.get(rootSessionId) ?? new Set();
|
|
257
334
|
sessionTasks.add(task);
|
|
258
335
|
this.#tasksBySession.set(rootSessionId, sessionTasks);
|
|
336
|
+
this.#publishSessionActivity(rootSessionId);
|
|
259
337
|
void task.then(() => this.#forgetTask(rootSessionId, task), () => this.#forgetTask(rootSessionId, task));
|
|
260
338
|
return task;
|
|
261
339
|
}
|
|
262
|
-
async #resumeWake(wake, abortSignal) {
|
|
340
|
+
async #resumeWake(wake, abortSignal, setAttention) {
|
|
263
341
|
abortSignal.throwIfAborted();
|
|
264
342
|
if (!(await this.#isSessionDeliverable(wake.rootSessionId))) {
|
|
265
343
|
await this.#supersedeSession(wake.rootSessionId, 'session_unavailable');
|
|
@@ -278,9 +356,9 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
278
356
|
return;
|
|
279
357
|
}
|
|
280
358
|
abortSignal.throwIfAborted();
|
|
281
|
-
await this.#deliverWake(wake, snapshot, abortSignal);
|
|
359
|
+
await this.#deliverWake(wake, snapshot, abortSignal, setAttention);
|
|
282
360
|
}
|
|
283
|
-
async #deliverWake(wake, snapshot, abortSignal, result) {
|
|
361
|
+
async #deliverWake(wake, snapshot, abortSignal, setAttention, result) {
|
|
284
362
|
if (wake.status === 'exhausted')
|
|
285
363
|
return;
|
|
286
364
|
if (wake.attemptCount >= this.#maxDeliveryAttempts) {
|
|
@@ -323,6 +401,8 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
323
401
|
}
|
|
324
402
|
return;
|
|
325
403
|
}
|
|
404
|
+
this.#parkedWakeIds.delete(wake.rootSessionId);
|
|
405
|
+
setAttention('idle');
|
|
326
406
|
if (this.#sessionWakesSuppressed(wake.rootSessionId))
|
|
327
407
|
return;
|
|
328
408
|
if (this.#closed) {
|
|
@@ -351,6 +431,8 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
351
431
|
attemptId,
|
|
352
432
|
status: 'delivered',
|
|
353
433
|
});
|
|
434
|
+
this.#parkedWakeIds.delete(wake.rootSessionId);
|
|
435
|
+
setAttention('idle');
|
|
354
436
|
return;
|
|
355
437
|
}
|
|
356
438
|
if (outcome.kind === 'superseded') {
|
|
@@ -370,6 +452,8 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
370
452
|
attemptId,
|
|
371
453
|
status: 'waiting_permission',
|
|
372
454
|
});
|
|
455
|
+
this.#parkedWakeIds.set(wake.rootSessionId, wake.wakeId);
|
|
456
|
+
setAttention('waiting_for_user');
|
|
373
457
|
return;
|
|
374
458
|
}
|
|
375
459
|
lastFailure = wakeOutcomeFailure(outcome);
|
|
@@ -501,6 +585,9 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
501
585
|
`${snapshot.graphId}:${snapshot.snapshotVersion}` === wake.wakeId);
|
|
502
586
|
}
|
|
503
587
|
#supersedeSession(rootSessionId, reason) {
|
|
588
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
589
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
590
|
+
this.#publishSessionActivity(rootSessionId);
|
|
504
591
|
return this.#input.wakeStore.supersedeAgentGraphSupervisorWakes({
|
|
505
592
|
rootSessionIds: [rootSessionId],
|
|
506
593
|
reason,
|
|
@@ -527,7 +614,7 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
527
614
|
: `host_restart:${runStatus}`);
|
|
528
615
|
return 1;
|
|
529
616
|
}
|
|
530
|
-
async #settlePermissionResponse(rootSessionId, abortSignal) {
|
|
617
|
+
async #settlePermissionResponse(rootSessionId, abortSignal, setAttention) {
|
|
531
618
|
const activity = await this.#input.activityRegistry.acquire(rootSessionId, abortSignal);
|
|
532
619
|
const retryable = [];
|
|
533
620
|
try {
|
|
@@ -548,6 +635,8 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
548
635
|
attemptId,
|
|
549
636
|
status: 'delivered',
|
|
550
637
|
});
|
|
638
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
639
|
+
setAttention('idle');
|
|
551
640
|
continue;
|
|
552
641
|
}
|
|
553
642
|
retryable.push(await this.#markRetryable(wake.graphId, wake.wakeId, attemptId, `permission_waiter_lost:${runStatus}`));
|
|
@@ -562,6 +651,10 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
562
651
|
#beginSessionWakeSuppression(rootSessionId) {
|
|
563
652
|
const count = this.#sessionWakeSuppressions.get(rootSessionId) ?? 0;
|
|
564
653
|
this.#sessionWakeSuppressions.set(rootSessionId, count + 1);
|
|
654
|
+
this.#activityVersions.set(rootSessionId, (this.#activityVersions.get(rootSessionId) ?? 0) + 1);
|
|
655
|
+
this.#sessionAttention.delete(rootSessionId);
|
|
656
|
+
this.#parkedWakeIds.delete(rootSessionId);
|
|
657
|
+
this.#publishSessionActivity(rootSessionId);
|
|
565
658
|
if (count === 0) {
|
|
566
659
|
const controller = this.#sessionAbortControllers.get(rootSessionId) ?? new AbortController();
|
|
567
660
|
this.#sessionAbortControllers.set(rootSessionId, controller);
|
|
@@ -577,6 +670,7 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
577
670
|
return;
|
|
578
671
|
}
|
|
579
672
|
this.#sessionWakeSuppressions.delete(rootSessionId);
|
|
673
|
+
this.#publishSessionActivity(rootSessionId);
|
|
580
674
|
if (!this.#tasksBySession.has(rootSessionId)) {
|
|
581
675
|
this.#sessionAbortControllers.delete(rootSessionId);
|
|
582
676
|
}
|
|
@@ -601,11 +695,15 @@ export class AgentGraphSupervisorWakeCoordinator {
|
|
|
601
695
|
this.#tasks.delete(task);
|
|
602
696
|
const sessionTasks = this.#tasksBySession.get(rootSessionId);
|
|
603
697
|
sessionTasks?.delete(task);
|
|
604
|
-
if (sessionTasks?.size === 0)
|
|
698
|
+
if (sessionTasks?.size === 0) {
|
|
605
699
|
this.#tasksBySession.delete(rootSessionId);
|
|
700
|
+
if (!this.#sessionAttention.has(rootSessionId))
|
|
701
|
+
this.#activityVersions.delete(rootSessionId);
|
|
702
|
+
}
|
|
606
703
|
if (!this.#sessionWakesSuppressed(rootSessionId) && !this.#tasksBySession.has(rootSessionId)) {
|
|
607
704
|
this.#sessionAbortControllers.delete(rootSessionId);
|
|
608
705
|
}
|
|
706
|
+
this.#publishSessionActivity(rootSessionId);
|
|
609
707
|
}
|
|
610
708
|
}
|
|
611
709
|
export function isAgentGraphSupervisorMilestone(result) {
|
|
@@ -328,6 +328,17 @@ export class AgentRun {
|
|
|
328
328
|
const runStore = this.input.runStore;
|
|
329
329
|
if (!this.input.runtimeEventStore || !runStore)
|
|
330
330
|
return;
|
|
331
|
+
// A stream may have attempted finalization after an earlier stop failed.
|
|
332
|
+
// Join that write, but let this explicit retry reuse its reserved fact if
|
|
333
|
+
// the write failed; a rejected promise is not durable terminal evidence.
|
|
334
|
+
const previousWrite = this.terminalClaim.write;
|
|
335
|
+
try {
|
|
336
|
+
await previousWrite;
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
if (this.terminalClaim.write === previousWrite)
|
|
340
|
+
this.terminalClaim.write = undefined;
|
|
341
|
+
}
|
|
331
342
|
await this.flushRuntimePartialBuffer(true);
|
|
332
343
|
// The claim only fences writers inside this Run. Another owner — a Host
|
|
333
344
|
// recovery, a resumed continuation — may have sealed the ledger already,
|
|
@@ -366,8 +366,30 @@ function header(observation, elementCount, query, shownCount) {
|
|
|
366
366
|
if (observation.truncated === true) {
|
|
367
367
|
parts.push('truncated=true(the tree was cut short; an element you expect may exist but not be listed)');
|
|
368
368
|
}
|
|
369
|
+
const selectedText = selectedTextCaption(observation);
|
|
370
|
+
if (selectedText)
|
|
371
|
+
parts.push(selectedText);
|
|
369
372
|
return parts.join(' ');
|
|
370
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Window selection is application text. A password field that currently holds
|
|
376
|
+
* keys must not leak that text through this caption; otherwise truncate like
|
|
377
|
+
* `value` and keep the executor's own truncated flag.
|
|
378
|
+
*/
|
|
379
|
+
function selectedTextCaption(observation) {
|
|
380
|
+
const selected = observation.selectedText;
|
|
381
|
+
if (!selected)
|
|
382
|
+
return undefined;
|
|
383
|
+
const focused = observation.elements.find((element) => element.focused === true);
|
|
384
|
+
const role = `${focused?.role ?? ''} ${focused?.subrole ?? ''}`;
|
|
385
|
+
if (/secure/i.test(role)) {
|
|
386
|
+
return 'selected_text=withheld(secure_field)';
|
|
387
|
+
}
|
|
388
|
+
const shown = truncate(selected.text);
|
|
389
|
+
return selected.truncated === true
|
|
390
|
+
? `selected_text=${quote(shown)}(truncated=true)`
|
|
391
|
+
: `selected_text=${quote(shown)}`;
|
|
392
|
+
}
|
|
371
393
|
/**
|
|
372
394
|
* A subrole earns its place when it is telling the model something the rest of
|
|
373
395
|
* the line does not.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { isRecord, isStringArray } from '@maka/core/record-schema';
|
|
20
|
+
import { decodeAgentRunEvent } from '@maka/core/agent-run';
|
|
21
|
+
import { decodeRuntimeEvent, decodeRuntimeInvocationOpened, } from '@maka/core/runtime-event';
|
|
22
|
+
import { runtimeInvocationOutcome } from '@maka/core/runtime-invocation';
|
|
23
|
+
import { stableJsonStringify } from '@maka/core/tool-args-identity';
|
|
24
|
+
import { runtimeInvocationFailureClass } from './runtime-event-read-model.js';
|
|
25
|
+
export function isConversationCopyAgentOutputSnapshot(value) {
|
|
26
|
+
return (isRecord(value) &&
|
|
27
|
+
value.kind === 'maka.agent_output_snapshot' &&
|
|
28
|
+
isResultPayload(value) &&
|
|
29
|
+
Object.keys(value).every((key) => [
|
|
30
|
+
'kind',
|
|
31
|
+
'schemaVersion',
|
|
32
|
+
'status',
|
|
33
|
+
'text',
|
|
34
|
+
'textTruncated',
|
|
35
|
+
'artifactIds',
|
|
36
|
+
'omittedArtifactIds',
|
|
37
|
+
'failureClass',
|
|
38
|
+
].includes(key)));
|
|
39
|
+
}
|
|
40
|
+
function isResultPayload(value) {
|
|
41
|
+
return (value.schemaVersion === 1 &&
|
|
42
|
+
resultStatus(value.status) &&
|
|
43
|
+
isStringArray(value.artifactIds) &&
|
|
44
|
+
value.artifactIds.every(nonempty) &&
|
|
45
|
+
typeof value.textTruncated === 'boolean' &&
|
|
46
|
+
typeof value.omittedArtifactIds === 'number' &&
|
|
47
|
+
Number.isSafeInteger(value.omittedArtifactIds) &&
|
|
48
|
+
value.omittedArtifactIds >= 0 &&
|
|
49
|
+
(value.text === undefined || typeof value.text === 'string') &&
|
|
50
|
+
(value.failureClass === undefined || typeof value.failureClass === 'string'));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Recognize the defined result envelope, never arbitrary nested JSON ids.
|
|
54
|
+
* These are reference claims, not authority: the Host still checks them against
|
|
55
|
+
* the retained child's metadata, invocation ledger, and Artifact ownership.
|
|
56
|
+
*/
|
|
57
|
+
export function conversationCopyAgentOutput(value) {
|
|
58
|
+
if (!isRecord(value))
|
|
59
|
+
return undefined;
|
|
60
|
+
const { execution, invocation, budget } = value;
|
|
61
|
+
if (!isRecord(execution) ||
|
|
62
|
+
execution.kind !== 'child_session' ||
|
|
63
|
+
!nonempty(execution.sessionId) ||
|
|
64
|
+
!isRecord(invocation) ||
|
|
65
|
+
invocation.sessionId !== execution.sessionId ||
|
|
66
|
+
!nonempty(invocation.runId) ||
|
|
67
|
+
!nonempty(invocation.turnId) ||
|
|
68
|
+
!nonempty(invocation.invocationId) ||
|
|
69
|
+
(execution.currentRunId !== undefined && execution.currentRunId !== invocation.runId) ||
|
|
70
|
+
!isRecord(budget) ||
|
|
71
|
+
typeof budget.view !== 'string' ||
|
|
72
|
+
!['result', 'events', 'runtime_events', 'all'].includes(budget.view) ||
|
|
73
|
+
!Array.isArray(value.events) ||
|
|
74
|
+
!Array.isArray(value.runtimeEvents) ||
|
|
75
|
+
!Array.isArray(value.diagnostics) ||
|
|
76
|
+
!Array.isArray(value.artifacts))
|
|
77
|
+
return undefined;
|
|
78
|
+
try {
|
|
79
|
+
const opening = decodeRuntimeInvocationOpened(invocation.opening);
|
|
80
|
+
const terminal = invocation.terminalEvent === undefined
|
|
81
|
+
? undefined
|
|
82
|
+
: decodeRuntimeEvent(invocation.terminalEvent);
|
|
83
|
+
if (terminal &&
|
|
84
|
+
(terminal.sessionId !== execution.sessionId ||
|
|
85
|
+
terminal.runId !== invocation.runId ||
|
|
86
|
+
terminal.turnId !== invocation.turnId ||
|
|
87
|
+
terminal.invocationId !== invocation.invocationId ||
|
|
88
|
+
terminal.partial))
|
|
89
|
+
return undefined;
|
|
90
|
+
let result = value.result;
|
|
91
|
+
if (budget.view === 'result') {
|
|
92
|
+
if (value.events.length ||
|
|
93
|
+
value.runtimeEvents.length ||
|
|
94
|
+
value.diagnostics.length ||
|
|
95
|
+
value.artifacts.length)
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Diagnostic views have no committed-result field. Their invocation
|
|
100
|
+
// still identifies the observation, and their Artifact list can contain
|
|
101
|
+
// outputs omitted by a separately bounded view=result read.
|
|
102
|
+
if (result !== undefined ||
|
|
103
|
+
(budget.view === 'events' && value.runtimeEvents.length > 0) ||
|
|
104
|
+
(budget.view === 'runtime_events' && value.events.length > 0))
|
|
105
|
+
return undefined;
|
|
106
|
+
const sameRun = (event) => event.sessionId === execution.sessionId &&
|
|
107
|
+
event.runId === invocation.runId &&
|
|
108
|
+
event.turnId === invocation.turnId;
|
|
109
|
+
const events = value.events.map(decodeAgentRunEvent);
|
|
110
|
+
const runtimeEvents = value.runtimeEvents.map(decodeRuntimeEvent);
|
|
111
|
+
if (!events.every(sameRun) ||
|
|
112
|
+
!runtimeEvents.every((event) => sameRun(event) && event.invocationId === invocation.invocationId) ||
|
|
113
|
+
!value.diagnostics.every((item) => isRecord(item) &&
|
|
114
|
+
item.runId === invocation.runId &&
|
|
115
|
+
item.turnId === invocation.turnId &&
|
|
116
|
+
nonempty(item.code) &&
|
|
117
|
+
typeof item.message === 'string') ||
|
|
118
|
+
!value.artifacts.every((item) => isRecord(item) &&
|
|
119
|
+
nonempty(item.id) &&
|
|
120
|
+
item.sessionId === execution.sessionId &&
|
|
121
|
+
item.turnId === invocation.turnId))
|
|
122
|
+
return undefined;
|
|
123
|
+
const text = [
|
|
124
|
+
...runtimeEvents.flatMap(diagnosticRuntimeEventText),
|
|
125
|
+
...events.flatMap((event) => (event.message ? [event.message] : [])),
|
|
126
|
+
...value.diagnostics.map((item) => item.message),
|
|
127
|
+
].join('\n');
|
|
128
|
+
const failureClass = runtimeInvocationFailureClass({ terminalEvent: terminal });
|
|
129
|
+
result = {
|
|
130
|
+
schemaVersion: 1,
|
|
131
|
+
status: runtimeInvocationOutcome({ terminalEvent: terminal }) ?? 'running',
|
|
132
|
+
...(text ? { text } : {}),
|
|
133
|
+
// The bounded diagnostic view cannot establish a complete final text
|
|
134
|
+
// or count omitted Artifacts. Retain its readable excerpt and known ids.
|
|
135
|
+
textTruncated: true,
|
|
136
|
+
artifactIds: value.artifacts.map((item) => item.id),
|
|
137
|
+
omittedArtifactIds: 0,
|
|
138
|
+
...(failureClass ? { failureClass } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (!isRecord(result) || !isResultPayload(result))
|
|
142
|
+
return undefined;
|
|
143
|
+
if (terminal &&
|
|
144
|
+
(runtimeInvocationOutcome({ terminalEvent: terminal }) !== result.status ||
|
|
145
|
+
(result.terminalRuntimeEventId !== undefined &&
|
|
146
|
+
result.terminalRuntimeEventId !== terminal.id)))
|
|
147
|
+
return undefined;
|
|
148
|
+
if (!terminal && result.status !== 'running' && result.status !== 'waiting_for_user')
|
|
149
|
+
return undefined;
|
|
150
|
+
let graph;
|
|
151
|
+
if (result.graph !== undefined) {
|
|
152
|
+
if (!isRecord(result.graph) ||
|
|
153
|
+
!nonempty(result.graph.graphId) ||
|
|
154
|
+
!nonempty(result.graph.workId) ||
|
|
155
|
+
!nonempty(result.graph.operatorId))
|
|
156
|
+
return undefined;
|
|
157
|
+
graph = {
|
|
158
|
+
graphId: result.graph.graphId,
|
|
159
|
+
workId: result.graph.workId,
|
|
160
|
+
operatorId: result.graph.operatorId,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
reference: {
|
|
165
|
+
childSessionId: execution.sessionId,
|
|
166
|
+
runId: invocation.runId,
|
|
167
|
+
turnId: invocation.turnId,
|
|
168
|
+
status: result.status,
|
|
169
|
+
artifactIds: result.artifactIds,
|
|
170
|
+
...(result.failureClass ? { failureClass: result.failureClass } : {}),
|
|
171
|
+
...(opening.lineage?.resumedFromRunId
|
|
172
|
+
? { resumedFromRunId: opening.lineage.resumedFromRunId }
|
|
173
|
+
: {}),
|
|
174
|
+
...(terminal ? { terminalEventId: terminal.id } : {}),
|
|
175
|
+
...(graph ? { graph } : {}),
|
|
176
|
+
},
|
|
177
|
+
// An independent Side Conversation keeps a result snapshot, without the
|
|
178
|
+
// original execution, invocation, Graph, or event identities.
|
|
179
|
+
snapshot: {
|
|
180
|
+
kind: 'maka.agent_output_snapshot',
|
|
181
|
+
schemaVersion: 1,
|
|
182
|
+
status: result.status,
|
|
183
|
+
...(typeof result.text === 'string' ? { text: result.text } : {}),
|
|
184
|
+
textTruncated: result.textTruncated,
|
|
185
|
+
artifactIds: result.artifactIds,
|
|
186
|
+
omittedArtifactIds: result.omittedArtifactIds,
|
|
187
|
+
...(result.failureClass ? { failureClass: result.failureClass } : {}),
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Keep tool evidence as static text without copying event or tool-call identities. */
|
|
196
|
+
function diagnosticRuntimeEventText(event) {
|
|
197
|
+
if (event.partial)
|
|
198
|
+
return [];
|
|
199
|
+
const content = event.content;
|
|
200
|
+
switch (content?.kind) {
|
|
201
|
+
case 'text':
|
|
202
|
+
return [content.text];
|
|
203
|
+
case 'function_call':
|
|
204
|
+
return [stableJsonStringify({ kind: content.kind, name: content.name, args: content.args })];
|
|
205
|
+
case 'function_response':
|
|
206
|
+
return [
|
|
207
|
+
stableJsonStringify({
|
|
208
|
+
kind: content.kind,
|
|
209
|
+
name: content.name,
|
|
210
|
+
result: content.result,
|
|
211
|
+
...(content.isError !== undefined ? { isError: content.isError } : {}),
|
|
212
|
+
}),
|
|
213
|
+
];
|
|
214
|
+
default:
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function nonempty(value) {
|
|
219
|
+
return typeof value === 'string' && value.length > 0;
|
|
220
|
+
}
|
|
221
|
+
function resultStatus(value) {
|
|
222
|
+
return (value === 'completed' ||
|
|
223
|
+
value === 'failed' ||
|
|
224
|
+
value === 'cancelled' ||
|
|
225
|
+
value === 'running' ||
|
|
226
|
+
value === 'waiting_for_user');
|
|
227
|
+
}
|