@myagentroam/node 0.9.2 → 0.9.3
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/connector.js +12 -13
- package/dist/control-outbound-scheduler.d.ts +4 -1
- package/dist/control-outbound-scheduler.js +39 -6
- package/dist/database.d.ts +0 -19
- package/dist/database.js +1 -62
- package/dist/event-buffer.d.ts +2 -5
- package/dist/event-buffer.js +3 -12
- package/dist/native-session-history.js +33 -12
- package/dist/runner/claude/managed-run-controller.d.ts +0 -1
- package/dist/runner/claude/managed-run-controller.js +17 -12
- package/dist/runner/codex/conversation-parser.d.ts +1 -1
- package/dist/runner/codex/conversation-parser.js +3 -0
- package/dist/runner/codex/managed-run-controller.d.ts +2 -2
- package/dist/runner/codex/managed-run-controller.js +22 -7
- package/dist/runner/codex-runner.d.ts +2 -0
- package/dist/runner/codex-runner.js +30 -2
- package/dist/runner/opencode/conversation-parser.js +1 -1
- package/dist/runner/opencode/managed-run-controller.d.ts +7 -2
- package/dist/runner/opencode/managed-run-controller.js +72 -14
- package/dist/runner/opencode-runner.d.ts +3 -0
- package/dist/runner/opencode-runner.js +25 -0
- package/dist/runner-profiles.js +10 -6
- package/dist/runtime-state.d.ts +5 -12
- package/dist/runtime-state.js +32 -46
- package/dist/service/attachment-domain-state.d.ts +2 -0
- package/dist/service/attachment-domain-state.js +2 -0
- package/dist/service/conversation-segment-service.js +10 -9
- package/dist/service/external-session-resume-service.js +9 -0
- package/dist/service/fake-managed-run-service.d.ts +0 -1
- package/dist/service/fake-managed-run-service.js +0 -2
- package/dist/service/native-session-projection-service.js +5 -0
- package/dist/service/node-connection-lifecycle-service.d.ts +0 -2
- package/dist/service/node-connection-lifecycle-service.js +0 -1
- package/dist/service/node-control-channel.js +1 -1
- package/dist/service/node-control-message-service.d.ts +0 -1
- package/dist/service/node-control-message-service.js +9 -6
- package/dist/service/node-workspace-coordinator.d.ts +1 -4
- package/dist/service/node-workspace-coordinator.js +2 -12
- package/dist/service/run-attachment-service.js +30 -6
- package/dist/service/run-event-service.d.ts +1 -0
- package/dist/service/run-event-service.js +69 -0
- package/dist/service/run-workbench-service.d.ts +1 -1
- package/dist/service/run-workbench-service.js +1 -5
- package/dist/service/runner-interaction-service.d.ts +1 -2
- package/dist/service/runner-interaction-service.js +1 -4
- package/dist/service/session-context-service.d.ts +1 -0
- package/dist/service/session-context-service.js +14 -0
- package/dist/service/session-domain-state.d.ts +1 -0
- package/dist/service/session-domain-state.js +1 -0
- package/dist/service/session-message-service.js +5 -10
- package/dist/service/skill-directory-service.d.ts +3 -1
- package/dist/service/skill-directory-service.js +28 -4
- package/dist/service/skill-node-operation-service.d.ts +1 -1
- package/dist/service/skill-node-operation-service.js +4 -2
- package/dist/service/workbench-event-service.d.ts +1 -13
- package/dist/service/workbench-event-service.js +2 -36
- package/dist/service/workbench-manifest-service.d.ts +0 -1
- package/dist/service/workbench-manifest-service.js +0 -1
- package/dist/service/workspace-domain-state.d.ts +1 -0
- package/dist/service/workspace-queue-workbench-service.d.ts +1 -0
- package/dist/service/workspace-queue-workbench-service.js +1 -0
- package/dist/service/workspace-upload-service.d.ts +2 -0
- package/dist/service/workspace-upload-service.js +28 -6
- package/dist/service/workspace-workbench-service.d.ts +0 -3
- package/dist/service/workspace-workbench-service.js +0 -17
- package/dist/util/runner-error.d.ts +6 -0
- package/dist/util/runner-error.js +52 -0
- package/dist/util/runner-native-session-parsers.js +43 -6
- package/package.json +2 -2
package/dist/connector.js
CHANGED
|
@@ -253,7 +253,7 @@ export class NodeConnector {
|
|
|
253
253
|
interrupted: (runId) => this.runState.interruptRequested.has(runId),
|
|
254
254
|
parseAttachments: parseRunnerImageAttachments,
|
|
255
255
|
materialize: (runId, attachments) => this.runAttachmentService.materializeCodex(runId, attachments),
|
|
256
|
-
completeCancelled: (runId
|
|
256
|
+
completeCancelled: (runId) => this.runnerInteractionService.completeCancelledRun(runId),
|
|
257
257
|
promoteSession: (sessionId, nativeSessionId) => this.sessionIdentityService.promote(sessionId, nativeSessionId),
|
|
258
258
|
send: (type, payload) => this.send(type, payload),
|
|
259
259
|
emit: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
@@ -262,7 +262,6 @@ export class NodeConnector {
|
|
|
262
262
|
publishApproval: (input) => this.runEventBridge.publishApproval(input),
|
|
263
263
|
emitItem: (runId, item) => this.emitConversationItem(runId, item),
|
|
264
264
|
appendImages: (runId, images) => this.runAttachmentService.append(runId, images.filter(isRunnerImageAttachment)),
|
|
265
|
-
captureSnapshot: (runId, cwd) => this.workspaceCoordinator.captureRunSnapshot(runId, cwd),
|
|
266
265
|
syncTitle: (sessionId, nativeSessionId, cwd) => this.managedRunnerSessions.refreshTitle(sessionId, nativeSessionId, cwd),
|
|
267
266
|
commandState: (sessionId, commandId) => this.commandStates.get(sessionId, commandId),
|
|
268
267
|
setCommandState: (sessionId, state) => this.commandStates.set(sessionId, state),
|
|
@@ -285,10 +284,16 @@ export class NodeConnector {
|
|
|
285
284
|
emit: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
286
285
|
emitItem: (runId, item) => this.emitConversationItem(runId, item),
|
|
287
286
|
adopt: (input) => this.runtime.adoptRun(input),
|
|
287
|
+
run: (runId) => {
|
|
288
|
+
const run = this.runtime.getRun(runId);
|
|
289
|
+
if (run === undefined)
|
|
290
|
+
throw new Error('RUN_NOT_FOUND');
|
|
291
|
+
return run;
|
|
292
|
+
},
|
|
293
|
+
hasActiveSessionLease: (sessionId) => this.runtime.hasActiveSessionLease(sessionId),
|
|
288
294
|
promote: (sessionId, nativeSessionId) => this.sessionIdentityService.promote(sessionId, nativeSessionId),
|
|
289
295
|
rememberNative: (nativeSessionId, sessionId) => this.managedRunnerSessions.remember(nativeSessionId, sessionId),
|
|
290
296
|
send: (type, payload) => this.send(type, payload),
|
|
291
|
-
captureSnapshot: (runId, cwd) => this.workspaceCoordinator.captureRunSnapshot(runId, cwd),
|
|
292
297
|
createApproval: (input) => this.runtime.createApproval(input),
|
|
293
298
|
expireApproval: (approvalId) => {
|
|
294
299
|
this.runtime.expireApproval(approvalId);
|
|
@@ -308,8 +313,7 @@ export class NodeConnector {
|
|
|
308
313
|
run: (runId) => this.runtime.getRun(runId),
|
|
309
314
|
hasActiveSessionLease: (sessionId) => this.runtime.hasActiveSessionLease(sessionId),
|
|
310
315
|
session: (sessionId) => this.runtime.getAgentSession(sessionId),
|
|
311
|
-
completeCancelled: (runId
|
|
312
|
-
captureSnapshot: (runId, cwd) => this.workspaceCoordinator.captureRunSnapshot(runId, cwd),
|
|
316
|
+
completeCancelled: (runId) => this.runnerInteractionService.completeCancelledRun(runId),
|
|
313
317
|
requestUserInput: (runId, input, toolUseId) => this.runnerInteractionService.requestClaudeUserInput(runId, input, toolUseId),
|
|
314
318
|
requestApproval: (runId, toolName, input) => this.runnerInteractionService.requestClaudeApproval(runId, toolName, input),
|
|
315
319
|
rememberNative: (nativeSessionId, sessionId) => this.managedRunnerSessions.remember(nativeSessionId, sessionId),
|
|
@@ -340,7 +344,6 @@ export class NodeConnector {
|
|
|
340
344
|
runners: this.runners,
|
|
341
345
|
emitRun: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
342
346
|
emitItem: (runId, item) => this.emitConversationItem(runId, item),
|
|
343
|
-
captureSnapshot: (runId, cwd) => this.workspaceCoordinator.captureRunSnapshot(runId, cwd),
|
|
344
347
|
approvalTimeoutMs: this.claudeApprovalTimeoutMs,
|
|
345
348
|
publishApproval: (input) => this.runEventBridge.publishApproval(input)
|
|
346
349
|
});
|
|
@@ -391,6 +394,7 @@ export class NodeConnector {
|
|
|
391
394
|
respondUserInput: (runId, requestId, answers) => this.runnerInteractionService.respondUserInput(runId, requestId, answers),
|
|
392
395
|
deleteQueuedStart: (runId) => this.workspaceQueueWorkbenchService.remove(runId),
|
|
393
396
|
clearImages: (runId) => this.runAttachmentService.clear(runId),
|
|
397
|
+
cleanupAttachments: (runId) => this.runAttachmentService.cleanup(runId),
|
|
394
398
|
emitQueue: (workspaceId, sessionId) => this.runEventBridge.emitQueue(workspaceId, sessionId),
|
|
395
399
|
emitRun: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
396
400
|
control: (operation, payload) => this.controlMessages.handle(JSON.stringify(createEnvelope(operation, payload))),
|
|
@@ -411,6 +415,7 @@ export class NodeConnector {
|
|
|
411
415
|
runtime: this.runtime,
|
|
412
416
|
emit: (workspaceId, sessionId) => this.runEventBridge.emitQueue(workspaceId, sessionId),
|
|
413
417
|
clearImages: (runId) => this.runAttachmentService.clear(runId),
|
|
418
|
+
cleanupAttachments: (runId) => this.runAttachmentService.cleanup(runId),
|
|
414
419
|
emitRun: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
415
420
|
emitConversation: (turn) => this.emitConversationTurn(turn),
|
|
416
421
|
control: (operation, payload) => this.controlMessages.handle(JSON.stringify(createEnvelope(operation, payload))),
|
|
@@ -565,7 +570,6 @@ export class NodeConnector {
|
|
|
565
570
|
state: this.runState,
|
|
566
571
|
emitRun: (runId, type, payload, status) => this.runEventBridge.emitRun(runId, type, payload, status),
|
|
567
572
|
publishApproval: (input) => this.runEventBridge.publishApproval(input),
|
|
568
|
-
captureSnapshot: (runId, cwd) => this.workspaceCoordinator.captureRunSnapshot(runId, cwd),
|
|
569
573
|
disconnect: () => this.controlChannel.close(4010, 'FAKE_RUNNER_DISCONNECT')
|
|
570
574
|
});
|
|
571
575
|
this.fakeRunner?.bindControl({
|
|
@@ -620,14 +624,13 @@ export class NodeConnector {
|
|
|
620
624
|
close: (code, reason) => this.controlChannel.close(code, reason),
|
|
621
625
|
inspectWorkspace: (envelope) => this.workspaceCoordinator.inspect(envelope),
|
|
622
626
|
handleNodeRequest: (envelope) => this.nodeRequests.handle(envelope),
|
|
623
|
-
acknowledgeWorkbenchEvent: (replyTo) => this.workbenchEventService.acknowledge(replyTo),
|
|
624
627
|
reconcileRuns: (envelope) => this.runEventBridge.reconcile(envelope),
|
|
625
628
|
runners: this.runners,
|
|
626
629
|
deliverSessionChannelMessage: (envelope) => this.runnerChannelMessages.handle(envelope),
|
|
627
630
|
completeUncontrolledCancellation: (runId) => {
|
|
628
631
|
const run = this.runtime.getRun(runId);
|
|
629
632
|
if (run?.status === 'CANCELLING')
|
|
630
|
-
this.runnerInteractionService.completeCancelledRun(runId
|
|
633
|
+
this.runnerInteractionService.completeCancelledRun(runId);
|
|
631
634
|
},
|
|
632
635
|
send: (type, payload, replyTo) => this.send(type, payload, replyTo)
|
|
633
636
|
});
|
|
@@ -640,7 +643,6 @@ export class NodeConnector {
|
|
|
640
643
|
this.workspaceCoordinator = new NodeWorkspaceCoordinator({
|
|
641
644
|
config: () => this.config,
|
|
642
645
|
database: () => this.database,
|
|
643
|
-
runtime: this.runtime,
|
|
644
646
|
watches: this.workspaceWatchService,
|
|
645
647
|
sessions: this.workspaceSessionService,
|
|
646
648
|
changes: this.workspaceChangeService,
|
|
@@ -672,7 +674,6 @@ export class NodeConnector {
|
|
|
672
674
|
}),
|
|
673
675
|
control: this.controlChannel,
|
|
674
676
|
terminal: this.terminalChannel,
|
|
675
|
-
events: this.workbenchEventService,
|
|
676
677
|
metrics: this.metrics,
|
|
677
678
|
heartbeatMs: HEARTBEAT_MS,
|
|
678
679
|
refreshMs: CAPABILITY_REFRESH_MS
|
|
@@ -701,7 +702,6 @@ export class NodeConnector {
|
|
|
701
702
|
});
|
|
702
703
|
this.workspaceWorkbenchService = new WorkspaceWorkbenchService({
|
|
703
704
|
requireWorkspace: (workspaceId) => this.workspaceCoordinator.require(workspaceId),
|
|
704
|
-
runtime: this.runtime,
|
|
705
705
|
watch: this.workspaceWatchService,
|
|
706
706
|
files: this.workspaceFileService,
|
|
707
707
|
uploads: this.workspaceUploadService,
|
|
@@ -715,7 +715,6 @@ export class NodeConnector {
|
|
|
715
715
|
this.operationRouter.registerAll(this.workspaceWorkbenchService.operations());
|
|
716
716
|
this.operationRouter.registerAll(this.runWorkbenchService.operations());
|
|
717
717
|
this.operationRouter.registerAll(this.nativeSessionWatchService.operations());
|
|
718
|
-
this.operationRouter.registerAll(this.workbenchEventService.operations());
|
|
719
718
|
this.operationRouter.registerAll(this.sessionQueryService.operations());
|
|
720
719
|
this.operationRouter.registerAll(this.sessionLifecycleService.operations());
|
|
721
720
|
this.operationRouter.registerAll(this.sessionMessageService.operations());
|
|
@@ -7,20 +7,23 @@ interface WritableControlSocket {
|
|
|
7
7
|
/** Keeps control responses responsive while reliable Workbench events are busy. */
|
|
8
8
|
export declare class ControlOutboundScheduler {
|
|
9
9
|
private readonly socket;
|
|
10
|
+
private readonly onExpired;
|
|
10
11
|
private readonly high;
|
|
11
12
|
private readonly normal;
|
|
12
13
|
private scheduled;
|
|
13
14
|
private retryTimer;
|
|
15
|
+
private expiryTimer;
|
|
14
16
|
private backpressureStartedAt;
|
|
15
17
|
private backpressureLogged;
|
|
16
18
|
private maximumHighDepth;
|
|
17
19
|
private maximumNormalDepth;
|
|
18
|
-
constructor(socket: () => WritableControlSocket | undefined);
|
|
20
|
+
constructor(socket: () => WritableControlSocket | undefined, onExpired?: () => void);
|
|
19
21
|
enqueue(envelope: NodeEnvelope, priority: 'high' | 'normal'): void;
|
|
20
22
|
reset(): void;
|
|
21
23
|
private schedule;
|
|
22
24
|
private flush;
|
|
23
25
|
private waitForBackpressure;
|
|
24
26
|
private finishBackpressure;
|
|
27
|
+
private scheduleExpiry;
|
|
25
28
|
}
|
|
26
29
|
export {};
|
|
@@ -4,19 +4,23 @@ const MAX_BYTES_PER_TURN = 256 * 1024;
|
|
|
4
4
|
const SOCKET_NORMAL_DIRECT_WATER_BYTES = 256 * 1024;
|
|
5
5
|
const SOCKET_HIGH_WATER_BYTES = 1024 * 1024;
|
|
6
6
|
const BACKPRESSURE_RETRY_MS = 5;
|
|
7
|
+
const OUTBOUND_QUEUE_TTL_MS = 30_000;
|
|
7
8
|
/** Keeps control responses responsive while reliable Workbench events are busy. */
|
|
8
9
|
export class ControlOutboundScheduler {
|
|
9
10
|
socket;
|
|
11
|
+
onExpired;
|
|
10
12
|
high = [];
|
|
11
13
|
normal = [];
|
|
12
14
|
scheduled = false;
|
|
13
15
|
retryTimer;
|
|
16
|
+
expiryTimer;
|
|
14
17
|
backpressureStartedAt;
|
|
15
18
|
backpressureLogged = false;
|
|
16
19
|
maximumHighDepth = 0;
|
|
17
20
|
maximumNormalDepth = 0;
|
|
18
|
-
constructor(socket) {
|
|
21
|
+
constructor(socket, onExpired = () => undefined) {
|
|
19
22
|
this.socket = socket;
|
|
23
|
+
this.onExpired = onExpired;
|
|
20
24
|
}
|
|
21
25
|
enqueue(envelope, priority) {
|
|
22
26
|
const socket = this.socket();
|
|
@@ -29,9 +33,13 @@ export class ControlOutboundScheduler {
|
|
|
29
33
|
socket.send(JSON.stringify(envelope));
|
|
30
34
|
return;
|
|
31
35
|
}
|
|
32
|
-
(priority === 'high' ? this.high : this.normal).push(
|
|
36
|
+
(priority === 'high' ? this.high : this.normal).push({
|
|
37
|
+
envelope,
|
|
38
|
+
expiresAt: Date.now() + OUTBOUND_QUEUE_TTL_MS
|
|
39
|
+
});
|
|
33
40
|
this.maximumHighDepth = Math.max(this.maximumHighDepth, this.high.length);
|
|
34
41
|
this.maximumNormalDepth = Math.max(this.maximumNormalDepth, this.normal.length);
|
|
42
|
+
this.scheduleExpiry();
|
|
35
43
|
this.schedule();
|
|
36
44
|
}
|
|
37
45
|
reset() {
|
|
@@ -41,6 +49,9 @@ export class ControlOutboundScheduler {
|
|
|
41
49
|
if (this.retryTimer !== undefined)
|
|
42
50
|
clearTimeout(this.retryTimer);
|
|
43
51
|
this.retryTimer = undefined;
|
|
52
|
+
if (this.expiryTimer !== undefined)
|
|
53
|
+
clearTimeout(this.expiryTimer);
|
|
54
|
+
this.expiryTimer = undefined;
|
|
44
55
|
this.finishBackpressure('reset');
|
|
45
56
|
}
|
|
46
57
|
schedule() {
|
|
@@ -62,8 +73,8 @@ export class ControlOutboundScheduler {
|
|
|
62
73
|
let bytes = 0;
|
|
63
74
|
while (frames < MAX_FRAMES_PER_TURN) {
|
|
64
75
|
const queue = this.high.length > 0 ? this.high : this.normal;
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
76
|
+
const queued = queue[0];
|
|
77
|
+
if (queued === undefined)
|
|
67
78
|
break;
|
|
68
79
|
// Reliable Workbench events may be replayed in a large burst after a
|
|
69
80
|
// reconnect. Stop normal traffic early so a later HTTP/control response
|
|
@@ -73,7 +84,7 @@ export class ControlOutboundScheduler {
|
|
|
73
84
|
this.waitForBackpressure(socket, 'normal');
|
|
74
85
|
return;
|
|
75
86
|
}
|
|
76
|
-
const serialized = JSON.stringify(envelope);
|
|
87
|
+
const serialized = JSON.stringify(queued.envelope);
|
|
77
88
|
const size = Buffer.byteLength(serialized);
|
|
78
89
|
if (frames > 0 && bytes + size > MAX_BYTES_PER_TURN)
|
|
79
90
|
break;
|
|
@@ -86,8 +97,12 @@ export class ControlOutboundScheduler {
|
|
|
86
97
|
}
|
|
87
98
|
if (this.high.length > 0 || this.normal.length > 0)
|
|
88
99
|
this.schedule();
|
|
89
|
-
else
|
|
100
|
+
else {
|
|
101
|
+
if (this.expiryTimer !== undefined)
|
|
102
|
+
clearTimeout(this.expiryTimer);
|
|
103
|
+
this.expiryTimer = undefined;
|
|
90
104
|
this.finishBackpressure('drained');
|
|
105
|
+
}
|
|
91
106
|
}
|
|
92
107
|
waitForBackpressure(socket, level) {
|
|
93
108
|
this.backpressureStartedAt ??= Date.now();
|
|
@@ -118,4 +133,22 @@ export class ControlOutboundScheduler {
|
|
|
118
133
|
this.maximumHighDepth = 0;
|
|
119
134
|
this.maximumNormalDepth = 0;
|
|
120
135
|
}
|
|
136
|
+
scheduleExpiry() {
|
|
137
|
+
if (this.expiryTimer !== undefined)
|
|
138
|
+
return;
|
|
139
|
+
const expiresAt = Math.min(this.high[0]?.expiresAt ?? Number.POSITIVE_INFINITY, this.normal[0]?.expiresAt ?? Number.POSITIVE_INFINITY);
|
|
140
|
+
if (!Number.isFinite(expiresAt))
|
|
141
|
+
return;
|
|
142
|
+
this.expiryTimer = setTimeout(() => {
|
|
143
|
+
this.expiryTimer = undefined;
|
|
144
|
+
if ((this.high[0]?.expiresAt ?? Number.POSITIVE_INFINITY) > Date.now() &&
|
|
145
|
+
(this.normal[0]?.expiresAt ?? Number.POSITIVE_INFINITY) > Date.now()) {
|
|
146
|
+
this.scheduleExpiry();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.reset();
|
|
150
|
+
this.onExpired();
|
|
151
|
+
}, Math.max(0, expiresAt - Date.now()));
|
|
152
|
+
this.expiryTimer.unref();
|
|
153
|
+
}
|
|
121
154
|
}
|
package/dist/database.d.ts
CHANGED
|
@@ -80,13 +80,6 @@ export interface NodeRunEvent {
|
|
|
80
80
|
readonly status?: AgentRunStatus;
|
|
81
81
|
readonly createdAt: number;
|
|
82
82
|
}
|
|
83
|
-
export interface WorkspaceActivityEvent {
|
|
84
|
-
readonly id: number;
|
|
85
|
-
readonly workspaceId: string;
|
|
86
|
-
readonly eventType: string;
|
|
87
|
-
readonly payload: unknown;
|
|
88
|
-
readonly createdAt: number;
|
|
89
|
-
}
|
|
90
83
|
export interface ConversationPage {
|
|
91
84
|
readonly turns: readonly NodeConversationTurn[];
|
|
92
85
|
readonly nextCursor: string | null;
|
|
@@ -132,10 +125,6 @@ export interface NodeMessageAttachmentInput {
|
|
|
132
125
|
readonly mime: string;
|
|
133
126
|
readonly dataBase64: string;
|
|
134
127
|
}
|
|
135
|
-
export interface WorkspaceActivityPage {
|
|
136
|
-
readonly events: readonly WorkspaceActivityEvent[];
|
|
137
|
-
readonly nextCursor: string | null;
|
|
138
|
-
}
|
|
139
128
|
export interface NodeApproval {
|
|
140
129
|
readonly id: string;
|
|
141
130
|
readonly runId: string;
|
|
@@ -288,11 +277,6 @@ export declare class NodeDatabase {
|
|
|
288
277
|
readonly cursor?: string;
|
|
289
278
|
readonly limit?: number;
|
|
290
279
|
}): ConversationItemPage;
|
|
291
|
-
listWorkspaceActivity(input: {
|
|
292
|
-
readonly workspaceId: string;
|
|
293
|
-
readonly cursor?: string;
|
|
294
|
-
readonly limit?: number;
|
|
295
|
-
}): WorkspaceActivityPage;
|
|
296
280
|
createApproval(input: {
|
|
297
281
|
readonly runId: string;
|
|
298
282
|
readonly approvalId: string;
|
|
@@ -307,13 +291,10 @@ export declare class NodeDatabase {
|
|
|
307
291
|
decideApproval(id: string, decision: 'APPROVED' | 'REJECTED'): NodeApproval;
|
|
308
292
|
listNonterminalRuns(): readonly NodeAgentRun[];
|
|
309
293
|
lastRunSequence(runId: string): number;
|
|
310
|
-
saveSnapshot(runId: string, snapshot: unknown): void;
|
|
311
|
-
getSnapshot(runId: string): unknown | undefined;
|
|
312
294
|
markActiveRunsSuspect(): void;
|
|
313
295
|
hasActiveWorkspaceLease(workspaceId: string): boolean;
|
|
314
296
|
reconcileRun(runId: string, active: boolean): void;
|
|
315
297
|
private releaseLease;
|
|
316
|
-
private recordWorkspaceActivity;
|
|
317
298
|
private recordConversationItemChange;
|
|
318
299
|
private projectApproval;
|
|
319
300
|
private readConversationTurn;
|
package/dist/database.js
CHANGED
|
@@ -552,11 +552,6 @@ export class NodeDatabase {
|
|
|
552
552
|
this.raw
|
|
553
553
|
.prepare('UPDATE legacy_agent_sessions SET last_activity_at = ?, updated_at = ? WHERE id = ?')
|
|
554
554
|
.run(value.createdAt, value.createdAt, value.sessionId);
|
|
555
|
-
this.recordWorkspaceActivity(value.workspaceId, 'run.created', {
|
|
556
|
-
runId: value.id,
|
|
557
|
-
sessionId: value.sessionId,
|
|
558
|
-
runner: value.runner
|
|
559
|
-
});
|
|
560
555
|
this.raw.exec('COMMIT');
|
|
561
556
|
return { run: value, created: true };
|
|
562
557
|
}
|
|
@@ -632,14 +627,6 @@ export class NodeDatabase {
|
|
|
632
627
|
.get(input.runId, input.sequence);
|
|
633
628
|
const value = event(stored);
|
|
634
629
|
this.projectRunEvent(value, input.status);
|
|
635
|
-
const run = this.getRun(input.runId);
|
|
636
|
-
if (run !== undefined) {
|
|
637
|
-
this.recordWorkspaceActivity(run.workspaceId, 'run.updated', {
|
|
638
|
-
runId: run.id,
|
|
639
|
-
eventType: input.eventType,
|
|
640
|
-
status: input.status ?? null
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
630
|
return value;
|
|
644
631
|
}
|
|
645
632
|
listRunEvents(runId, after = 0) {
|
|
@@ -709,25 +696,6 @@ export class NodeDatabase {
|
|
|
709
696
|
: null
|
|
710
697
|
};
|
|
711
698
|
}
|
|
712
|
-
listWorkspaceActivity(input) {
|
|
713
|
-
if (this.getWorkspace(input.workspaceId) === undefined)
|
|
714
|
-
throw new Error('WORKSPACE_NOT_FOUND');
|
|
715
|
-
const limit = pageLimit(input.limit);
|
|
716
|
-
const cursor = decodeCursor(input.cursor, 'workspace-activity', input.workspaceId);
|
|
717
|
-
const rows = this.raw
|
|
718
|
-
.prepare(`SELECT id, workspace_id, event_type, payload, created_at FROM workspace_activity_events
|
|
719
|
-
WHERE workspace_id = ? AND (? IS NULL OR id > ?)
|
|
720
|
-
ORDER BY id ASC LIMIT ?`)
|
|
721
|
-
.all(input.workspaceId, cursor === undefined ? null : cursor.value, cursor?.value ?? 0, limit + 1);
|
|
722
|
-
const page = rows.slice(0, limit);
|
|
723
|
-
const last = page.at(-1);
|
|
724
|
-
return {
|
|
725
|
-
events: page.map((row) => workspaceActivityEvent(row)),
|
|
726
|
-
nextCursor: rows.length > limit && last !== undefined
|
|
727
|
-
? encodeCursor('workspace-activity', input.workspaceId, numberValue(last, 'id'), '')
|
|
728
|
-
: null
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
699
|
createApproval(input) {
|
|
732
700
|
if (this.getRun(input.runId) === undefined)
|
|
733
701
|
throw new Error('RUN_NOT_FOUND');
|
|
@@ -828,18 +796,6 @@ export class NodeDatabase {
|
|
|
828
796
|
.get(runId);
|
|
829
797
|
return row['sequence'] === null ? 0 : numberValue(row, 'sequence');
|
|
830
798
|
}
|
|
831
|
-
saveSnapshot(runId, snapshot) {
|
|
832
|
-
this.raw
|
|
833
|
-
.prepare(`INSERT INTO run_snapshots (run_id, snapshot, created_at) VALUES (?, ?, ?)
|
|
834
|
-
ON CONFLICT(run_id) DO UPDATE SET snapshot = excluded.snapshot, created_at = excluded.created_at`)
|
|
835
|
-
.run(runId, JSON.stringify(snapshot), this.now());
|
|
836
|
-
}
|
|
837
|
-
getSnapshot(runId) {
|
|
838
|
-
const row = this.raw
|
|
839
|
-
.prepare('SELECT snapshot FROM run_snapshots WHERE run_id = ?')
|
|
840
|
-
.get(runId);
|
|
841
|
-
return row === undefined ? undefined : JSON.parse(stringValue(row, 'snapshot'));
|
|
842
|
-
}
|
|
843
799
|
markActiveRunsSuspect() {
|
|
844
800
|
this.raw
|
|
845
801
|
.prepare("UPDATE agent_runs SET recovery_state = 'SUSPECT', recovery_updated_at = ? WHERE status NOT IN ('SUCCEEDED', 'FAILED', 'CANCELLED', 'INTERRUPTED')")
|
|
@@ -877,12 +833,6 @@ export class NodeDatabase {
|
|
|
877
833
|
.prepare("UPDATE workspace_leases SET state = 'RELEASED', updated_at = ? WHERE run_id = ? AND state IN ('ACTIVE', 'SUSPECT')")
|
|
878
834
|
.run(this.now(), runId);
|
|
879
835
|
}
|
|
880
|
-
recordWorkspaceActivity(workspaceId, eventType, payload) {
|
|
881
|
-
this.raw
|
|
882
|
-
.prepare(`INSERT INTO workspace_activity_events (workspace_id, event_type, payload, created_at)
|
|
883
|
-
VALUES (?, ?, ?, ?)`)
|
|
884
|
-
.run(workspaceId, eventType, JSON.stringify(payload), this.now());
|
|
885
|
-
}
|
|
886
836
|
recordConversationItemChange(sessionId, itemId, revision) {
|
|
887
837
|
this.raw
|
|
888
838
|
.prepare(`INSERT INTO conversation_item_events (session_id, item_id, revision, created_at)
|
|
@@ -1206,10 +1156,8 @@ function initializeEphemeralRuntimeTables(database) {
|
|
|
1206
1156
|
'conversation_turns',
|
|
1207
1157
|
'conversation_items',
|
|
1208
1158
|
'conversation_item_events',
|
|
1209
|
-
'workspace_activity_events',
|
|
1210
1159
|
'approvals',
|
|
1211
|
-
'workspace_leases'
|
|
1212
|
-
'run_snapshots'
|
|
1160
|
+
'workspace_leases'
|
|
1213
1161
|
]);
|
|
1214
1162
|
for (const raw of NODE_SCHEMA_V1.split(';')) {
|
|
1215
1163
|
const statement = raw.trim();
|
|
@@ -1341,15 +1289,6 @@ function conversationItem(row) {
|
|
|
1341
1289
|
completedAt: nullableNumber(row, 'completed_at')
|
|
1342
1290
|
};
|
|
1343
1291
|
}
|
|
1344
|
-
function workspaceActivityEvent(row) {
|
|
1345
|
-
return {
|
|
1346
|
-
id: numberValue(row, 'id'),
|
|
1347
|
-
workspaceId: stringValue(row, 'workspace_id'),
|
|
1348
|
-
eventType: stringValue(row, 'event_type'),
|
|
1349
|
-
payload: JSON.parse(stringValue(row, 'payload')),
|
|
1350
|
-
createdAt: numberValue(row, 'created_at')
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
1292
|
function pageLimit(value) {
|
|
1354
1293
|
if (value === undefined)
|
|
1355
1294
|
return 100;
|
package/dist/event-buffer.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { type NodeEnvelope, type WorkbenchEvent } from '@myagentroam/protocol';
|
|
2
|
-
/**
|
|
3
|
-
* Keeps exactly the page-level business frames that have not received a Server
|
|
4
|
-
* ACK. Node-global sequences make a reconnect replay harmless.
|
|
5
|
-
*/
|
|
2
|
+
/** Keeps live page-level frames only until the current Server connection ACKs them. */
|
|
6
3
|
export declare class ReliableWorkbenchEventBuffer {
|
|
7
4
|
private readonly pending;
|
|
8
5
|
queue(event: WorkbenchEvent): NodeEnvelope;
|
|
9
6
|
acknowledge(replyTo: string | undefined): boolean;
|
|
10
|
-
|
|
7
|
+
clear(): void;
|
|
11
8
|
get size(): number;
|
|
12
9
|
}
|
package/dist/event-buffer.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
2
|
-
/**
|
|
3
|
-
* Keeps exactly the page-level business frames that have not received a Server
|
|
4
|
-
* ACK. Node-global sequences make a reconnect replay harmless.
|
|
5
|
-
*/
|
|
2
|
+
/** Keeps live page-level frames only until the current Server connection ACKs them. */
|
|
6
3
|
export class ReliableWorkbenchEventBuffer {
|
|
7
4
|
pending = new Map();
|
|
8
5
|
queue(event) {
|
|
@@ -13,14 +10,8 @@ export class ReliableWorkbenchEventBuffer {
|
|
|
13
10
|
acknowledge(replyTo) {
|
|
14
11
|
return replyTo !== undefined && this.pending.delete(replyTo);
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const a = left.payload;
|
|
19
|
-
const b = right.payload;
|
|
20
|
-
return a.sequence - b.sequence;
|
|
21
|
-
});
|
|
22
|
-
for (const envelope of ordered)
|
|
23
|
-
send(envelope);
|
|
13
|
+
clear() {
|
|
14
|
+
this.pending.clear();
|
|
24
15
|
}
|
|
25
16
|
get size() {
|
|
26
17
|
return this.pending.size;
|
|
@@ -9,7 +9,9 @@ const DISCOVERY_READ_BYTES = 32 * 1024;
|
|
|
9
9
|
const TRANSCRIPT_READ_BYTES = 4 * 1024 * 1024;
|
|
10
10
|
const CONTEXT_USAGE_READ_BYTES = 256 * 1024;
|
|
11
11
|
const CLAUDE_CONTEXT_USAGE_READ_BYTES = [CONTEXT_USAGE_READ_BYTES, 1024 * 1024, 4 * 1024 * 1024];
|
|
12
|
+
const TRANSCRIPT_INDEX_TTL_MS = 60 * 60_000;
|
|
12
13
|
const transcriptIndex = new Map();
|
|
14
|
+
const transcriptIndexTimers = new Map();
|
|
13
15
|
const discoveryReads = new Map();
|
|
14
16
|
const DEFAULT_CLAUDE_CONTEXT_WINDOW_TOKENS = 200_000;
|
|
15
17
|
const CLAUDE_CONTEXT_WINDOW_ENV = 'MAR_CLAUDE_CONTEXT_WINDOW_TOKENS';
|
|
@@ -53,7 +55,7 @@ async function performNativeSessionDiscovery(runner) {
|
|
|
53
55
|
if (parsed === undefined)
|
|
54
56
|
continue;
|
|
55
57
|
const cwd = canonical(parsed.cwd);
|
|
56
|
-
|
|
58
|
+
setTranscriptIndex(indexKey(runner, cwd, parsed.externalSessionId), files[index]);
|
|
57
59
|
found.set(`${cwd}\u0000${parsed.externalSessionId}`, parsed);
|
|
58
60
|
}
|
|
59
61
|
nodeLog('native.session.discovery.completed', {
|
|
@@ -83,14 +85,14 @@ async function readDiscoveryHeaders(files, runner) {
|
|
|
83
85
|
/** Reads one already-discovered external session again to follow appended JSONL records. */
|
|
84
86
|
export async function readNativeSession(runner, workspacePath, externalSessionId) {
|
|
85
87
|
const wanted = canonical(workspacePath);
|
|
86
|
-
const path =
|
|
88
|
+
const path = getTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
87
89
|
if (path === undefined)
|
|
88
90
|
return undefined;
|
|
89
91
|
const parsed = await readTranscript(path, runner, TRANSCRIPT_READ_BYTES);
|
|
90
92
|
if (parsed === undefined ||
|
|
91
93
|
parsed.externalSessionId !== externalSessionId ||
|
|
92
94
|
canonical(parsed.cwd) !== wanted) {
|
|
93
|
-
|
|
95
|
+
deleteTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
94
96
|
return undefined;
|
|
95
97
|
}
|
|
96
98
|
return parsed;
|
|
@@ -103,10 +105,10 @@ export async function readNativeSession(runner, workspacePath, externalSessionId
|
|
|
103
105
|
*/
|
|
104
106
|
export async function readCodexNativeContextUsage(workspacePath, externalSessionId) {
|
|
105
107
|
const wanted = canonical(workspacePath);
|
|
106
|
-
let path =
|
|
108
|
+
let path = getTranscriptIndex(indexKey('codex', wanted, externalSessionId));
|
|
107
109
|
if (path === undefined) {
|
|
108
110
|
await discoverNativeSessions('codex', workspacePath);
|
|
109
|
-
path =
|
|
111
|
+
path = getTranscriptIndex(indexKey('codex', wanted, externalSessionId));
|
|
110
112
|
}
|
|
111
113
|
if (path === undefined)
|
|
112
114
|
return undefined;
|
|
@@ -124,11 +126,11 @@ export async function readCodexNativeContextUsage(workspacePath, externalSession
|
|
|
124
126
|
/** Reads the latest actual Claude prompt occupancy without starting a CLI/SDK control stream. */
|
|
125
127
|
export async function readClaudeNativeContextUsage(workspacePath, externalSessionId, options = {}) {
|
|
126
128
|
const wanted = canonical(workspacePath);
|
|
127
|
-
let path =
|
|
129
|
+
let path = getTranscriptIndex(indexKey('claude-code', wanted, externalSessionId));
|
|
128
130
|
path ??= await locateClaudeTranscript(wanted, externalSessionId);
|
|
129
131
|
if (path === undefined) {
|
|
130
132
|
await discoverNativeSessions('claude-code', workspacePath);
|
|
131
|
-
path =
|
|
133
|
+
path = getTranscriptIndex(indexKey('claude-code', wanted, externalSessionId));
|
|
132
134
|
}
|
|
133
135
|
if (path === undefined)
|
|
134
136
|
return undefined;
|
|
@@ -178,7 +180,7 @@ async function locateClaudeTranscript(wantedCwd, externalSessionId) {
|
|
|
178
180
|
const match = parsed.find(({ history }) => history?.externalSessionId === externalSessionId && canonical(history.cwd) === wantedCwd);
|
|
179
181
|
if (match === undefined)
|
|
180
182
|
return undefined;
|
|
181
|
-
|
|
183
|
+
setTranscriptIndex(indexKey('claude-code', wantedCwd, externalSessionId), match.path);
|
|
182
184
|
return match.path;
|
|
183
185
|
}
|
|
184
186
|
export async function claudeContextWindowTokens(options = {}) {
|
|
@@ -207,10 +209,10 @@ export async function claudeContextWindowTokens(options = {}) {
|
|
|
207
209
|
*/
|
|
208
210
|
export async function removeNativeSession(runner, workspacePath, externalSessionId) {
|
|
209
211
|
const wanted = canonical(workspacePath);
|
|
210
|
-
let path =
|
|
212
|
+
let path = getTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
211
213
|
if (path === undefined) {
|
|
212
214
|
await discoverNativeSessions(runner, workspacePath);
|
|
213
|
-
path =
|
|
215
|
+
path = getTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
214
216
|
}
|
|
215
217
|
if (path === undefined)
|
|
216
218
|
throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
|
|
@@ -218,7 +220,7 @@ export async function removeNativeSession(runner, workspacePath, externalSession
|
|
|
218
220
|
if (parsed === undefined ||
|
|
219
221
|
parsed.externalSessionId !== externalSessionId ||
|
|
220
222
|
canonical(parsed.cwd) !== wanted) {
|
|
221
|
-
|
|
223
|
+
deleteTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
222
224
|
throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
|
|
223
225
|
}
|
|
224
226
|
try {
|
|
@@ -229,7 +231,7 @@ export async function removeNativeSession(runner, workspacePath, externalSession
|
|
|
229
231
|
throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
|
|
230
232
|
throw error;
|
|
231
233
|
}
|
|
232
|
-
|
|
234
|
+
deleteTranscriptIndex(indexKey(runner, wanted, externalSessionId));
|
|
233
235
|
}
|
|
234
236
|
function transcriptRoot(runner) {
|
|
235
237
|
return runner === 'codex'
|
|
@@ -886,6 +888,25 @@ function canonical(value) {
|
|
|
886
888
|
function indexKey(runner, cwd, externalSessionId) {
|
|
887
889
|
return `${runner}\u0000${cwd}\u0000${externalSessionId}`;
|
|
888
890
|
}
|
|
891
|
+
function setTranscriptIndex(key, path) {
|
|
892
|
+
const previousTimer = transcriptIndexTimers.get(key);
|
|
893
|
+
if (previousTimer !== undefined)
|
|
894
|
+
clearTimeout(previousTimer);
|
|
895
|
+
transcriptIndex.set(key, path);
|
|
896
|
+
const timer = setTimeout(() => deleteTranscriptIndex(key), TRANSCRIPT_INDEX_TTL_MS);
|
|
897
|
+
timer.unref();
|
|
898
|
+
transcriptIndexTimers.set(key, timer);
|
|
899
|
+
}
|
|
900
|
+
function getTranscriptIndex(key) {
|
|
901
|
+
return transcriptIndex.get(key);
|
|
902
|
+
}
|
|
903
|
+
function deleteTranscriptIndex(key) {
|
|
904
|
+
const timer = transcriptIndexTimers.get(key);
|
|
905
|
+
if (timer !== undefined)
|
|
906
|
+
clearTimeout(timer);
|
|
907
|
+
transcriptIndexTimers.delete(key);
|
|
908
|
+
transcriptIndex.delete(key);
|
|
909
|
+
}
|
|
889
910
|
function isRecord(value) {
|
|
890
911
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
891
912
|
}
|
|
@@ -26,7 +26,6 @@ export interface ClaudeManagedRunHost<TAttachment extends ClaudeRunnerImageAttac
|
|
|
26
26
|
hasActiveSessionLease(sessionId: string): boolean;
|
|
27
27
|
session(sessionId: string): NodeAgentSession | undefined;
|
|
28
28
|
completeCancelled(runId: string, cwd: string): boolean;
|
|
29
|
-
captureSnapshot(runId: string, cwd: string): Promise<void>;
|
|
30
29
|
requestUserInput(runId: string, input: Record<string, unknown>, toolUseId: string): Promise<ReturnType<typeof denyPermission>>;
|
|
31
30
|
requestApproval(runId: string, toolName: string, input: Record<string, unknown>): Promise<ReturnType<typeof denyPermission>>;
|
|
32
31
|
rememberNative(nativeSessionId: string, sessionId: string): void;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
2
2
|
import { ClaudeChannelGate } from '../../claude-channel.js';
|
|
3
3
|
import { boundedConversationItemId, claudePlanText, compactRunnerText, compactRunnerValue, extractClaudeText, extractClaudeThinkingSummaries, extractClaudeToolResults, extractClaudeToolUses, isClaudeCommandTool } from '../codex/conversation-parser.js';
|
|
4
|
-
import { isTerminalRunStatus } from '../../util/node-operation-parsers.js';
|
|
5
4
|
import { claudeContentBlocks } from '../../util/node-operation-parsers.js';
|
|
6
5
|
import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
7
6
|
import { safeErrorCode } from '../../util/safe-error.js';
|
|
7
|
+
import { runnerErrorProjection } from '../../util/runner-error.js';
|
|
8
8
|
export class ClaudeManagedRunController {
|
|
9
9
|
runner;
|
|
10
10
|
host;
|
|
@@ -116,16 +116,25 @@ export class ClaudeManagedRunController {
|
|
|
116
116
|
let terminal = false;
|
|
117
117
|
let handle;
|
|
118
118
|
const toolKinds = new Map();
|
|
119
|
-
const finish = (status, eventType, eventPayload) => {
|
|
119
|
+
const finish = (status, eventType, eventPayload, failure, fallbackCode = 'CLAUDE_RUN_FAILED') => {
|
|
120
120
|
if (terminal)
|
|
121
121
|
return;
|
|
122
122
|
terminal = true;
|
|
123
|
-
|
|
123
|
+
const projected = status === 'FAILED'
|
|
124
|
+
? runnerErrorProjection(failure ?? eventPayload, fallbackCode)
|
|
125
|
+
: undefined;
|
|
126
|
+
if (projected !== undefined) {
|
|
127
|
+
this.host.emitItem(runId, {
|
|
128
|
+
itemId: boundedConversationItemId('runner-error', runId),
|
|
129
|
+
kind: 'error',
|
|
130
|
+
status: 'FAILED',
|
|
131
|
+
payload: { ...projected, recoverable: true, retryAfterMs: null }
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
this.host.emit(runId, eventType, projected ?? eventPayload, status);
|
|
124
135
|
this.runner.execution.runs.delete(runId);
|
|
125
136
|
handle?.cancel();
|
|
126
137
|
this.host.active.delete(runId);
|
|
127
|
-
if (isTerminalRunStatus(status))
|
|
128
|
-
void this.host.captureSnapshot(runId, cwd);
|
|
129
138
|
};
|
|
130
139
|
try {
|
|
131
140
|
const gate = new ClaudeChannelGate(sessionId);
|
|
@@ -314,7 +323,7 @@ export class ClaudeManagedRunController {
|
|
|
314
323
|
// result completes this AgentRun even though that iterable can
|
|
315
324
|
// remain open waiting for another user turn; waiting only for
|
|
316
325
|
// stream closure would leave the WorkspaceLease permanently held.
|
|
317
|
-
finish(subtype === 'success' && record.is_error !== true ? 'SUCCEEDED' : 'FAILED', subtype === 'success' && record.is_error !== true ? 'run.completed' : 'run.failed', { subtype });
|
|
326
|
+
finish(subtype === 'success' && record.is_error !== true ? 'SUCCEEDED' : 'FAILED', subtype === 'success' && record.is_error !== true ? 'run.completed' : 'run.failed', { subtype }, record);
|
|
318
327
|
if (completedNativeSessionId !== undefined)
|
|
319
328
|
void this.host.syncTitle(sessionId, completedNativeSessionId, cwd);
|
|
320
329
|
}
|
|
@@ -324,14 +333,10 @@ export class ClaudeManagedRunController {
|
|
|
324
333
|
this.host.setChannelToken(sessionId, gate.token);
|
|
325
334
|
this.host.send('session.channel.ready', { sessionId, token: gate.token });
|
|
326
335
|
this.host.emit(runId, 'run.running', {}, 'RUNNING');
|
|
327
|
-
void handle.completed.then(() => finish('SUCCEEDED', 'run.completed', { subtype: 'stream_closed' }), (error) => finish('FAILED', 'run.failed', {
|
|
328
|
-
code: safeErrorCode(error, 'CLAUDE_RUN_FAILED')
|
|
329
|
-
}));
|
|
336
|
+
void handle.completed.then(() => finish('SUCCEEDED', 'run.completed', { subtype: 'stream_closed' }), (error) => finish('FAILED', 'run.failed', { code: safeErrorCode(error, 'CLAUDE_RUN_FAILED') }, error));
|
|
330
337
|
}
|
|
331
338
|
catch (error) {
|
|
332
|
-
finish('FAILED', 'run.failed', {
|
|
333
|
-
code: safeErrorCode(error, 'CLAUDE_RUN_START_FAILED')
|
|
334
|
-
});
|
|
339
|
+
finish('FAILED', 'run.failed', { code: safeErrorCode(error, 'CLAUDE_RUN_START_FAILED') }, error, 'CLAUDE_RUN_START_FAILED');
|
|
335
340
|
}
|
|
336
341
|
}
|
|
337
342
|
}
|