@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NodeAgentSession, NodeConversationItem, NodeConversationTurn, NodeMessageAttachmentInput } from '../../database.js';
|
|
2
2
|
export type PublicConversationItem = {
|
|
3
3
|
readonly itemId: string;
|
|
4
|
-
readonly kind: 'reasoning_summary' | 'tool_call' | 'command_execution' | 'user_input_request' | 'plan' | 'context_compaction' | 'file_change' | 'unknown';
|
|
4
|
+
readonly kind: 'reasoning_summary' | 'tool_call' | 'command_execution' | 'user_input_request' | 'plan' | 'context_compaction' | 'file_change' | 'error' | 'unknown';
|
|
5
5
|
readonly status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'DECLINED';
|
|
6
6
|
readonly payload: Record<string, unknown>;
|
|
7
7
|
readonly merge?: boolean;
|
|
@@ -83,6 +83,7 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
83
83
|
const consumedOfficialIds = new Set();
|
|
84
84
|
const officialUser = officialItems.find((item) => item.kind === 'user_message');
|
|
85
85
|
const officialAssistants = officialItems.filter((item) => item.kind === 'assistant_message');
|
|
86
|
+
const officialContextCompactions = officialItems.filter((item) => item.kind === 'context_compaction');
|
|
86
87
|
const officialItemMarker = `:official:${value.id}:`;
|
|
87
88
|
const officialByNativeId = new Map(officialItems.flatMap((item) => {
|
|
88
89
|
const markerIndex = item.id.lastIndexOf(officialItemMarker);
|
|
@@ -95,6 +96,8 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
95
96
|
let replacement;
|
|
96
97
|
if (managedItem.kind === 'user_message')
|
|
97
98
|
replacement = officialUser;
|
|
99
|
+
else if (managedItem.kind === 'context_compaction')
|
|
100
|
+
replacement = officialContextCompactions.find((item) => !consumedOfficialIds.has(item.id));
|
|
98
101
|
else {
|
|
99
102
|
const nativeId = conversationItemNativeId(managedItem.id);
|
|
100
103
|
if (nativeId !== undefined)
|
|
@@ -44,14 +44,13 @@ export interface CodexManagedRunHost<TAttachment> {
|
|
|
44
44
|
}): void;
|
|
45
45
|
emitItem(runId: string, item: {
|
|
46
46
|
readonly itemId: string;
|
|
47
|
-
readonly kind: 'reasoning_summary' | 'tool_call' | 'command_execution' | 'user_input_request' | 'plan' | 'context_compaction' | 'file_change' | 'unknown';
|
|
47
|
+
readonly kind: 'reasoning_summary' | 'tool_call' | 'command_execution' | 'user_input_request' | 'plan' | 'context_compaction' | 'file_change' | 'error' | 'unknown';
|
|
48
48
|
readonly status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'DECLINED';
|
|
49
49
|
readonly payload: Record<string, unknown>;
|
|
50
50
|
readonly merge?: boolean;
|
|
51
51
|
readonly append?: 'sections' | 'outputPreview' | 'outputSummary';
|
|
52
52
|
}): void;
|
|
53
53
|
appendImages(runId: string, images: readonly NodeMessageAttachmentInput[]): readonly Record<string, unknown>[];
|
|
54
|
-
captureSnapshot(runId: string, cwd: string): Promise<void>;
|
|
55
54
|
syncTitle(sessionId: string, nativeSessionId: string, cwd: string): Promise<void>;
|
|
56
55
|
commandState(sessionId: string, commandId: string): RunnerCommandState | undefined;
|
|
57
56
|
setCommandState(sessionId: string, state: RunnerCommandState): void;
|
|
@@ -76,5 +75,6 @@ export declare class CodexManagedRunController<TAttachment> {
|
|
|
76
75
|
private requestUserInput;
|
|
77
76
|
start(envelope: NodeEnvelope): void;
|
|
78
77
|
private run;
|
|
78
|
+
private emitError;
|
|
79
79
|
}
|
|
80
80
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extractId } from '../../util/runner-native-session-parsers.js';
|
|
2
2
|
import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
3
3
|
import { safeErrorCode } from '../../util/safe-error.js';
|
|
4
|
+
import { runnerErrorProjection } from '../../util/runner-error.js';
|
|
4
5
|
import { boundedConversationItemId, codexImageAttachments, codexLiveConversationItem, codexUserInputQuestions, compactRunnerText, isPlainRecord } from './conversation-parser.js';
|
|
5
6
|
export class CodexManagedRunController {
|
|
6
7
|
runner;
|
|
@@ -84,7 +85,8 @@ export class CodexManagedRunController {
|
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
catch (error) {
|
|
87
|
-
this.
|
|
88
|
+
this.emitError(runId, error, 'CODEX_COMPACT_FAILED');
|
|
89
|
+
this.host.emit(runId, 'run.failed', runnerErrorProjection(error, 'CODEX_COMPACT_FAILED'), 'FAILED');
|
|
88
90
|
this.runner.execution.runs.delete(runId);
|
|
89
91
|
this.runner.releaseEnvironment(runId);
|
|
90
92
|
this.host.removeActive(runId);
|
|
@@ -184,16 +186,18 @@ export class CodexManagedRunController {
|
|
|
184
186
|
if (notification.method === 'turn/completed') {
|
|
185
187
|
const status = turn?.status;
|
|
186
188
|
const terminal = status === 'completed' ? 'SUCCEEDED' : status === 'interrupted' ? 'INTERRUPTED' : 'FAILED';
|
|
187
|
-
|
|
189
|
+
if (terminal === 'FAILED')
|
|
190
|
+
this.emitError(runId, turn?.error ?? turn, 'CODEX_RUN_FAILED');
|
|
191
|
+
const failure = terminal === 'FAILED'
|
|
192
|
+
? runnerErrorProjection(turn?.error ?? turn, 'CODEX_RUN_FAILED')
|
|
193
|
+
: { status: status ?? 'unknown' };
|
|
194
|
+
this.host.emit(runId, terminal === 'FAILED' ? 'run.failed' : 'run.completed', failure, terminal);
|
|
188
195
|
const active = this.runner.execution.runs.get(runId);
|
|
189
196
|
const titleRefresh = active === undefined ||
|
|
190
197
|
active.refreshTitle === false ||
|
|
191
198
|
this.host.session(active.sessionId)?.titleSource !== 'AUTO'
|
|
192
199
|
? undefined
|
|
193
200
|
: this.host.syncTitle(active.sessionId, active.threadId, active.cwd);
|
|
194
|
-
if (active !== undefined) {
|
|
195
|
-
void this.host.captureSnapshot(runId, active.cwd);
|
|
196
|
-
}
|
|
197
201
|
this.runner.execution.runs.delete(runId);
|
|
198
202
|
if (titleRefresh === undefined)
|
|
199
203
|
this.runner.releaseEnvironment(runId);
|
|
@@ -284,7 +288,8 @@ export class CodexManagedRunController {
|
|
|
284
288
|
secretEnvironment = parseSecretEnvironment(payload.secretEnvironment);
|
|
285
289
|
}
|
|
286
290
|
catch (error) {
|
|
287
|
-
this.
|
|
291
|
+
this.emitError(runId, error, 'CODEX_RUN_FAILED');
|
|
292
|
+
this.host.emit(runId, 'run.rejected', runnerErrorProjection(error, 'MESSAGE_ATTACHMENTS_INVALID'), 'FAILED');
|
|
288
293
|
return;
|
|
289
294
|
}
|
|
290
295
|
this.host.adopt({
|
|
@@ -382,10 +387,20 @@ export class CodexManagedRunController {
|
|
|
382
387
|
this.runner.releaseEnvironment(runId);
|
|
383
388
|
return;
|
|
384
389
|
}
|
|
385
|
-
this.
|
|
390
|
+
this.emitError(runId, error, 'CODEX_RUN_FAILED');
|
|
391
|
+
this.host.emit(runId, 'run.failed', runnerErrorProjection(error, 'CODEX_RUN_FAILED'), 'FAILED');
|
|
386
392
|
this.runner.execution.runs.delete(runId);
|
|
387
393
|
this.runner.releaseEnvironment(runId);
|
|
388
394
|
this.host.removeActive(runId);
|
|
389
395
|
}
|
|
390
396
|
}
|
|
397
|
+
emitError(runId, error, fallbackCode) {
|
|
398
|
+
const projected = runnerErrorProjection(error, fallbackCode);
|
|
399
|
+
this.host.emitItem(runId, {
|
|
400
|
+
itemId: boundedConversationItemId('runner-error', runId),
|
|
401
|
+
kind: 'error',
|
|
402
|
+
status: 'FAILED',
|
|
403
|
+
payload: { ...projected, recoverable: true, retryAfterMs: null }
|
|
404
|
+
});
|
|
405
|
+
}
|
|
391
406
|
}
|
|
@@ -13,6 +13,7 @@ export declare class CodexRunner extends AbstractRunner<'codex'> {
|
|
|
13
13
|
private environmentFingerprint;
|
|
14
14
|
private readonly environmentRuns;
|
|
15
15
|
private readonly profileCache;
|
|
16
|
+
private readonly profileCacheTimers;
|
|
16
17
|
private readonly profileRefreshes;
|
|
17
18
|
private readonly profileClients;
|
|
18
19
|
private readonly profileKeyByEnvironment;
|
|
@@ -23,6 +24,7 @@ export declare class CodexRunner extends AbstractRunner<'codex'> {
|
|
|
23
24
|
refreshProfile(capabilities: NodeCapabilities, workspace?: NodeWorkspace, environment?: Readonly<Record<string, string>>): Promise<RunnerProfile>;
|
|
24
25
|
private refreshCodexProfile;
|
|
25
26
|
private selectCachedProfile;
|
|
27
|
+
private cacheProfile;
|
|
26
28
|
private fetchCodexProfile;
|
|
27
29
|
available(capabilities: NodeCapabilities): boolean;
|
|
28
30
|
executeGlobalCommand(commandId: string, input: string): unknown;
|
|
@@ -11,6 +11,7 @@ import { codexSessionControl } from '../codex-app-server.js';
|
|
|
11
11
|
import { extractId } from '../util/runner-native-session-parsers.js';
|
|
12
12
|
import { nodeLog } from '../operational.js';
|
|
13
13
|
const CODEX_MODEL_CACHE_MS = 10 * 60 * 1_000;
|
|
14
|
+
const CODEX_PROFILE_ENTRY_TTL_MS = 30 * 60 * 1_000;
|
|
14
15
|
const CODEX_MODEL_FAILURE_RETRY_MS = 30 * 1_000;
|
|
15
16
|
const CODEX_MODEL_PAGE_SIZE = 100;
|
|
16
17
|
export class CodexRunner extends AbstractRunner {
|
|
@@ -23,6 +24,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
23
24
|
environmentFingerprint;
|
|
24
25
|
environmentRuns = new Set();
|
|
25
26
|
profileCache = new Map();
|
|
27
|
+
profileCacheTimers = new Map();
|
|
26
28
|
profileRefreshes = new Map();
|
|
27
29
|
profileClients = new Set();
|
|
28
30
|
profileKeyByEnvironment = new Map();
|
|
@@ -74,7 +76,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
74
76
|
successful: true,
|
|
75
77
|
retryAt: 0
|
|
76
78
|
};
|
|
77
|
-
this.
|
|
79
|
+
this.cacheProfile(key, cache);
|
|
78
80
|
if (this.selectedProfileKey === key)
|
|
79
81
|
this.latestProfileCache = cache;
|
|
80
82
|
return cache.profile;
|
|
@@ -88,7 +90,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
88
90
|
cached.retryAt = Date.now() + CODEX_MODEL_FAILURE_RETRY_MS;
|
|
89
91
|
return cached.profile;
|
|
90
92
|
}
|
|
91
|
-
this.
|
|
93
|
+
this.cacheProfile(key, {
|
|
92
94
|
profile: { ...base, models: [] },
|
|
93
95
|
defaultConfiguration: emptyCodexConfiguration(),
|
|
94
96
|
fetchedAt: 0,
|
|
@@ -106,6 +108,25 @@ export class CodexRunner extends AbstractRunner {
|
|
|
106
108
|
this.latestProfileCache = cached;
|
|
107
109
|
return cached.profile;
|
|
108
110
|
}
|
|
111
|
+
cacheProfile(key, cache) {
|
|
112
|
+
const previousTimer = this.profileCacheTimers.get(key);
|
|
113
|
+
if (previousTimer !== undefined)
|
|
114
|
+
clearTimeout(previousTimer);
|
|
115
|
+
this.profileCache.set(key, cache);
|
|
116
|
+
const timer = setTimeout(() => {
|
|
117
|
+
this.profileCacheTimers.delete(key);
|
|
118
|
+
this.profileCache.delete(key);
|
|
119
|
+
for (const [environmentKey, profileKey] of this.profileKeyByEnvironment)
|
|
120
|
+
if (profileKey === key)
|
|
121
|
+
this.profileKeyByEnvironment.delete(environmentKey);
|
|
122
|
+
if (this.selectedProfileKey === key) {
|
|
123
|
+
this.selectedProfileKey = undefined;
|
|
124
|
+
this.latestProfileCache = undefined;
|
|
125
|
+
}
|
|
126
|
+
}, CODEX_PROFILE_ENTRY_TTL_MS);
|
|
127
|
+
timer.unref();
|
|
128
|
+
this.profileCacheTimers.set(key, timer);
|
|
129
|
+
}
|
|
109
130
|
async fetchCodexProfile(base, environment) {
|
|
110
131
|
const client = this.profileClientFactory();
|
|
111
132
|
this.profileClients.add(client);
|
|
@@ -466,6 +487,13 @@ export class CodexRunner extends AbstractRunner {
|
|
|
466
487
|
this.client.clearEnvironment();
|
|
467
488
|
this.environmentFingerprint = undefined;
|
|
468
489
|
this.environmentRuns.clear();
|
|
490
|
+
for (const timer of this.profileCacheTimers.values())
|
|
491
|
+
clearTimeout(timer);
|
|
492
|
+
this.profileCacheTimers.clear();
|
|
493
|
+
this.profileCache.clear();
|
|
494
|
+
this.profileKeyByEnvironment.clear();
|
|
495
|
+
this.latestProfileCache = undefined;
|
|
496
|
+
this.selectedProfileKey = undefined;
|
|
469
497
|
}
|
|
470
498
|
request(method, params) {
|
|
471
499
|
return this.client.request(method, params);
|
|
@@ -128,7 +128,7 @@ function partItem(session, turnId, raw, planMode, startedAt, completedAt, sequen
|
|
|
128
128
|
}, startedAt, completedAt, sequence);
|
|
129
129
|
}
|
|
130
130
|
export function openCodeQuestions(value) {
|
|
131
|
-
if (!Array.isArray(value) || value.length === 0
|
|
131
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
132
132
|
return [];
|
|
133
133
|
return value.flatMap((value, index) => {
|
|
134
134
|
const question = record(value);
|
|
@@ -17,10 +17,13 @@ interface OpenCodeManagedRunHost {
|
|
|
17
17
|
cwd: string;
|
|
18
18
|
externalSessionId?: string;
|
|
19
19
|
}): void;
|
|
20
|
+
run(runId: string): {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
};
|
|
23
|
+
hasActiveSessionLease(sessionId: string): boolean;
|
|
20
24
|
promote(sessionId: string, nativeSessionId: string): void;
|
|
21
25
|
rememberNative(nativeSessionId: string, sessionId: string): void;
|
|
22
26
|
send(type: string, payload: unknown): void;
|
|
23
|
-
captureSnapshot(runId: string, cwd: string): Promise<void>;
|
|
24
27
|
createApproval(input: {
|
|
25
28
|
readonly runId: string;
|
|
26
29
|
readonly approvalId: string;
|
|
@@ -41,7 +44,7 @@ interface OpenCodeManagedRunHost {
|
|
|
41
44
|
}
|
|
42
45
|
type OpenCodePublicItem = {
|
|
43
46
|
readonly itemId: string;
|
|
44
|
-
readonly kind: 'assistant_message' | 'plan' | 'reasoning_summary' | 'tool_call' | 'user_input_request' | 'context_compaction' | 'unknown';
|
|
47
|
+
readonly kind: 'assistant_message' | 'plan' | 'reasoning_summary' | 'tool_call' | 'user_input_request' | 'context_compaction' | 'error' | 'unknown';
|
|
45
48
|
readonly status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';
|
|
46
49
|
readonly payload: Record<string, unknown>;
|
|
47
50
|
readonly merge?: boolean;
|
|
@@ -60,6 +63,8 @@ export declare class OpenCodeManagedRunController {
|
|
|
60
63
|
executeCommand(session: NodeAgentSession, command: RunnerCommandDescriptor, context: {
|
|
61
64
|
readonly secretEnvironment: Readonly<Record<string, string>>;
|
|
62
65
|
}): Promise<unknown>;
|
|
66
|
+
private compact;
|
|
67
|
+
private runCompact;
|
|
63
68
|
start(envelope: NodeEnvelope): void;
|
|
64
69
|
cancel(runId: string, intent: 'CANCEL' | 'INTERRUPT'): boolean;
|
|
65
70
|
decideApproval(input: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
2
|
-
import {
|
|
2
|
+
import { runnerErrorProjection } from '../../util/runner-error.js';
|
|
3
3
|
import { boundedConversationItemId, compactRunnerText, compactRunnerValue } from '../codex/conversation-parser.js';
|
|
4
4
|
import { openCodePatchToolPayload, openCodeQuestions } from './conversation-parser.js';
|
|
5
5
|
export class OpenCodeManagedRunController {
|
|
@@ -19,7 +19,7 @@ export class OpenCodeManagedRunController {
|
|
|
19
19
|
}
|
|
20
20
|
async executeCommand(session, command, context) {
|
|
21
21
|
if (command.id === 'compact')
|
|
22
|
-
return this.
|
|
22
|
+
return this.compact(session, context.secretEnvironment);
|
|
23
23
|
if (command.id !== 'plan')
|
|
24
24
|
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
25
25
|
const current = this.host.commandState(session.id, 'plan');
|
|
@@ -37,6 +37,53 @@ export class OpenCodeManagedRunController {
|
|
|
37
37
|
});
|
|
38
38
|
return { command: command.id, states: this.host.commandStates(session.id) };
|
|
39
39
|
}
|
|
40
|
+
compact(session, environment) {
|
|
41
|
+
if (session.externalSessionId === null ||
|
|
42
|
+
session.model === null ||
|
|
43
|
+
session.nativeControl !== 'MAR_MANAGED' ||
|
|
44
|
+
this.host.hasActiveSessionLease(session.id))
|
|
45
|
+
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
46
|
+
const runId = crypto.randomUUID();
|
|
47
|
+
const client = this.runner.managedClient(environment);
|
|
48
|
+
this.host.adopt({
|
|
49
|
+
runId,
|
|
50
|
+
sessionId: session.id,
|
|
51
|
+
runner: 'opencode',
|
|
52
|
+
cwd: session.cwd,
|
|
53
|
+
externalSessionId: session.externalSessionId
|
|
54
|
+
});
|
|
55
|
+
this.host.active.add(runId);
|
|
56
|
+
this.runner.execution.set(runId, {
|
|
57
|
+
runId,
|
|
58
|
+
sessionId: session.id,
|
|
59
|
+
nativeSessionId: session.externalSessionId,
|
|
60
|
+
cwd: session.cwd,
|
|
61
|
+
client,
|
|
62
|
+
abort: new AbortController(),
|
|
63
|
+
access: session.access ?? 'default',
|
|
64
|
+
agent: 'build'
|
|
65
|
+
});
|
|
66
|
+
this.host.emit(runId, 'run.started', { command: 'compact' }, 'STARTING');
|
|
67
|
+
void this.runCompact(runId, session.model);
|
|
68
|
+
return {
|
|
69
|
+
command: 'compact',
|
|
70
|
+
run: this.host.run(runId),
|
|
71
|
+
states: this.host.commandStates(session.id)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async runCompact(runId, model) {
|
|
75
|
+
const execution = this.runner.execution.get(runId);
|
|
76
|
+
if (execution === undefined)
|
|
77
|
+
return;
|
|
78
|
+
this.host.emit(runId, 'run.running', { command: 'compact' }, 'RUNNING');
|
|
79
|
+
try {
|
|
80
|
+
await execution.client.summarize(execution.cwd, execution.nativeSessionId, model);
|
|
81
|
+
this.finish(runId, 'SUCCEEDED', 'run.completed', { status: 'SUCCEEDED' });
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
this.finish(runId, 'FAILED', 'run.failed', error, 'OPENCODE_COMPACT_FAILED');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
40
87
|
start(envelope) {
|
|
41
88
|
const payload = envelope.payload;
|
|
42
89
|
if (payload.runner !== 'opencode' ||
|
|
@@ -57,7 +104,14 @@ export class OpenCodeManagedRunController {
|
|
|
57
104
|
attachments = this.host.parseAttachments(payload.attachments);
|
|
58
105
|
}
|
|
59
106
|
catch (error) {
|
|
60
|
-
|
|
107
|
+
const projected = runnerErrorProjection(error, 'RUNNER_INPUT_INVALID');
|
|
108
|
+
this.host.emitItem(payload.runId, {
|
|
109
|
+
itemId: boundedConversationItemId('runner-error', payload.runId),
|
|
110
|
+
kind: 'error',
|
|
111
|
+
status: 'FAILED',
|
|
112
|
+
payload: { ...projected, recoverable: true, retryAfterMs: null }
|
|
113
|
+
});
|
|
114
|
+
this.host.emit(payload.runId, 'run.rejected', projected, 'FAILED');
|
|
61
115
|
return;
|
|
62
116
|
}
|
|
63
117
|
void this.run({
|
|
@@ -181,9 +235,7 @@ export class OpenCodeManagedRunController {
|
|
|
181
235
|
rejectReady(error);
|
|
182
236
|
return;
|
|
183
237
|
}
|
|
184
|
-
this.finish(input.runId, 'FAILED', 'run.failed',
|
|
185
|
-
code: safeErrorCode(error, 'OPENCODE_EVENT_FAILED')
|
|
186
|
-
});
|
|
238
|
+
this.finish(input.runId, 'FAILED', 'run.failed', error, 'OPENCODE_EVENT_FAILED');
|
|
187
239
|
});
|
|
188
240
|
await subscriptionReady;
|
|
189
241
|
this.host.emit(input.runId, 'run.running', {}, 'RUNNING');
|
|
@@ -206,9 +258,7 @@ export class OpenCodeManagedRunController {
|
|
|
206
258
|
this.finish(input.runId, 'SUCCEEDED', 'run.completed', { status: 'SUCCEEDED' });
|
|
207
259
|
}
|
|
208
260
|
catch (error) {
|
|
209
|
-
this.finish(input.runId, 'FAILED', 'run.failed',
|
|
210
|
-
code: safeErrorCode(error, 'OPENCODE_RUN_START_FAILED')
|
|
211
|
-
});
|
|
261
|
+
this.finish(input.runId, 'FAILED', 'run.failed', error, 'OPENCODE_RUN_START_FAILED');
|
|
212
262
|
}
|
|
213
263
|
}
|
|
214
264
|
event(runId, nativeSessionId, value) {
|
|
@@ -231,7 +281,7 @@ export class OpenCodeManagedRunController {
|
|
|
231
281
|
return;
|
|
232
282
|
}
|
|
233
283
|
if (event?.type === 'session.error') {
|
|
234
|
-
this.finish(runId, 'FAILED', 'run.failed',
|
|
284
|
+
this.finish(runId, 'FAILED', 'run.failed', properties?.error ?? properties);
|
|
235
285
|
return;
|
|
236
286
|
}
|
|
237
287
|
if (event?.type === 'message.part.updated') {
|
|
@@ -309,7 +359,7 @@ export class OpenCodeManagedRunController {
|
|
|
309
359
|
if (execution === undefined || requestId === undefined)
|
|
310
360
|
return;
|
|
311
361
|
const questions = openCodeQuestions(rawQuestions);
|
|
312
|
-
if (questions.length === 0 ||
|
|
362
|
+
if (questions.length === 0 || this.questions.has(requestId))
|
|
313
363
|
return;
|
|
314
364
|
const timer = setTimeout(() => {
|
|
315
365
|
const pending = this.questions.get(requestId);
|
|
@@ -340,7 +390,7 @@ export class OpenCodeManagedRunController {
|
|
|
340
390
|
payload: { requestId, questions }
|
|
341
391
|
});
|
|
342
392
|
}
|
|
343
|
-
finish(runId, status, eventType, payload) {
|
|
393
|
+
finish(runId, status, eventType, payload, fallbackCode = 'OPENCODE_RUN_FAILED') {
|
|
344
394
|
const execution = this.runner.execution.get(runId);
|
|
345
395
|
const cwd = execution?.cwd ?? this.startingCwds.get(runId);
|
|
346
396
|
const client = execution?.client ?? this.startingClients.get(runId);
|
|
@@ -363,11 +413,19 @@ export class OpenCodeManagedRunController {
|
|
|
363
413
|
clearTimeout(pending.timer);
|
|
364
414
|
this.questions.delete(id);
|
|
365
415
|
}
|
|
416
|
+
if (status === 'FAILED') {
|
|
417
|
+
const projected = runnerErrorProjection(payload, fallbackCode);
|
|
418
|
+
this.host.emitItem(runId, {
|
|
419
|
+
itemId: boundedConversationItemId('runner-error', runId),
|
|
420
|
+
kind: 'error',
|
|
421
|
+
status: 'FAILED',
|
|
422
|
+
payload: { ...projected, recoverable: true, retryAfterMs: null }
|
|
423
|
+
});
|
|
424
|
+
payload = projected;
|
|
425
|
+
}
|
|
366
426
|
this.host.emit(runId, eventType, payload, status);
|
|
367
427
|
if (client !== undefined)
|
|
368
428
|
this.runner.releaseManagedClient(client);
|
|
369
|
-
if (cwd !== undefined)
|
|
370
|
-
void this.host.captureSnapshot(runId, cwd);
|
|
371
429
|
}
|
|
372
430
|
}
|
|
373
431
|
function openCodeImageInput(input, attachmentPaths) {
|
|
@@ -19,6 +19,7 @@ export declare class OpenCodeRunner extends AbstractRunner<'opencode'> {
|
|
|
19
19
|
readonly discoveryScope: "WORKSPACE_REQUIRED";
|
|
20
20
|
private readonly modelsByWorkspace;
|
|
21
21
|
private readonly contextLimitsByWorkspace;
|
|
22
|
+
private readonly profileTimersByWorkspace;
|
|
22
23
|
readonly execution: Map<string, OpenCodeManagedExecution>;
|
|
23
24
|
readonly managedTurns: Map<string, string>;
|
|
24
25
|
private readonly environmentClients;
|
|
@@ -56,6 +57,8 @@ export declare class OpenCodeRunner extends AbstractRunner<'opencode'> {
|
|
|
56
57
|
maxTokens: number;
|
|
57
58
|
} | undefined>;
|
|
58
59
|
stop(): Promise<void>;
|
|
60
|
+
private scheduleProfileExpiry;
|
|
61
|
+
private clearProfileTimer;
|
|
59
62
|
managedClient(environment: Readonly<Record<string, string>>, isolationKey?: string): OpenCodeServerClient;
|
|
60
63
|
releaseManagedClient(client: OpenCodeServerClient): void;
|
|
61
64
|
}
|
|
@@ -5,6 +5,7 @@ import { AbstractRunner } from './abstract-runner.js';
|
|
|
5
5
|
import { isWithinWorkspace } from './codex/conversation-parser.js';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
7
|
import { openCodeConversationPage } from './opencode/conversation-parser.js';
|
|
8
|
+
const PROFILE_ENTRY_TTL_MS = 30 * 60_000;
|
|
8
9
|
export class OpenCodeRunner extends AbstractRunner {
|
|
9
10
|
client;
|
|
10
11
|
clientFactory;
|
|
@@ -12,6 +13,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
12
13
|
discoveryScope = 'WORKSPACE_REQUIRED';
|
|
13
14
|
modelsByWorkspace = new Map();
|
|
14
15
|
contextLimitsByWorkspace = new Map();
|
|
16
|
+
profileTimersByWorkspace = new Map();
|
|
15
17
|
execution = new Map();
|
|
16
18
|
managedTurns = new Map();
|
|
17
19
|
environmentClients = new Map();
|
|
@@ -39,11 +41,13 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
39
41
|
const models = openCodeProfileModels(catalog);
|
|
40
42
|
this.modelsByWorkspace.set(workspace.path, models);
|
|
41
43
|
this.contextLimitsByWorkspace.set(workspace.path, openCodeContextLimits(catalog));
|
|
44
|
+
this.scheduleProfileExpiry(workspace.path);
|
|
42
45
|
return { ...this.profile(capabilities), models };
|
|
43
46
|
}
|
|
44
47
|
catch {
|
|
45
48
|
this.modelsByWorkspace.delete(workspace.path);
|
|
46
49
|
this.contextLimitsByWorkspace.delete(workspace.path);
|
|
50
|
+
this.clearProfileTimer(workspace.path);
|
|
47
51
|
}
|
|
48
52
|
finally {
|
|
49
53
|
this.releaseManagedClient(client);
|
|
@@ -279,6 +283,11 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
279
283
|
execution.abort.abort();
|
|
280
284
|
this.execution.clear();
|
|
281
285
|
this.managedTurns.clear();
|
|
286
|
+
for (const timer of this.profileTimersByWorkspace.values())
|
|
287
|
+
clearTimeout(timer);
|
|
288
|
+
this.profileTimersByWorkspace.clear();
|
|
289
|
+
this.modelsByWorkspace.clear();
|
|
290
|
+
this.contextLimitsByWorkspace.clear();
|
|
282
291
|
const stopping = [
|
|
283
292
|
this.client.stop(),
|
|
284
293
|
...[...this.environmentClients.values()].map(({ client }) => client.stop()),
|
|
@@ -288,6 +297,22 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
288
297
|
await Promise.allSettled(stopping);
|
|
289
298
|
this.stoppingClients.clear();
|
|
290
299
|
}
|
|
300
|
+
scheduleProfileExpiry(workspacePath) {
|
|
301
|
+
this.clearProfileTimer(workspacePath);
|
|
302
|
+
const timer = setTimeout(() => {
|
|
303
|
+
this.profileTimersByWorkspace.delete(workspacePath);
|
|
304
|
+
this.modelsByWorkspace.delete(workspacePath);
|
|
305
|
+
this.contextLimitsByWorkspace.delete(workspacePath);
|
|
306
|
+
}, PROFILE_ENTRY_TTL_MS);
|
|
307
|
+
timer.unref();
|
|
308
|
+
this.profileTimersByWorkspace.set(workspacePath, timer);
|
|
309
|
+
}
|
|
310
|
+
clearProfileTimer(workspacePath) {
|
|
311
|
+
const timer = this.profileTimersByWorkspace.get(workspacePath);
|
|
312
|
+
if (timer !== undefined)
|
|
313
|
+
clearTimeout(timer);
|
|
314
|
+
this.profileTimersByWorkspace.delete(workspacePath);
|
|
315
|
+
}
|
|
291
316
|
managedClient(environment, isolationKey) {
|
|
292
317
|
const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
|
|
293
318
|
if (entries.length === 0 && isolationKey === undefined)
|
package/dist/runner-profiles.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const COMMON_COMMAND_DESCRIPTIONS = {
|
|
2
|
+
compact: 'Compact the current session context.',
|
|
3
|
+
plan: 'Toggle collaborative Plan Mode.'
|
|
4
|
+
};
|
|
1
5
|
const CLAUDE_MODEL_ENVIRONMENT = {
|
|
2
6
|
opus: 'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
3
7
|
sonnet: 'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
@@ -35,7 +39,7 @@ const definitions = {
|
|
|
35
39
|
{
|
|
36
40
|
id: 'compact',
|
|
37
41
|
label: '/compact',
|
|
38
|
-
description:
|
|
42
|
+
description: COMMON_COMMAND_DESCRIPTIONS.compact,
|
|
39
43
|
inputHint: '/compact',
|
|
40
44
|
interaction: 'IMMEDIATE_ACTION'
|
|
41
45
|
},
|
|
@@ -43,7 +47,7 @@ const definitions = {
|
|
|
43
47
|
id: 'plan',
|
|
44
48
|
label: '/plan',
|
|
45
49
|
stateLabel: 'Plan Mode',
|
|
46
|
-
description:
|
|
50
|
+
description: COMMON_COMMAND_DESCRIPTIONS.plan,
|
|
47
51
|
inputHint: '/plan',
|
|
48
52
|
interaction: 'TOGGLE'
|
|
49
53
|
},
|
|
@@ -96,7 +100,7 @@ const definitions = {
|
|
|
96
100
|
id: 'compact',
|
|
97
101
|
runner: 'claude-code',
|
|
98
102
|
label: '/compact',
|
|
99
|
-
description:
|
|
103
|
+
description: COMMON_COMMAND_DESCRIPTIONS.compact,
|
|
100
104
|
inputHint: '/compact',
|
|
101
105
|
source: 'CLAUDE_AGENT_SDK',
|
|
102
106
|
interaction: 'IMMEDIATE_ACTION',
|
|
@@ -108,7 +112,7 @@ const definitions = {
|
|
|
108
112
|
runner: 'claude-code',
|
|
109
113
|
label: '/plan',
|
|
110
114
|
stateLabel: 'Plan Mode',
|
|
111
|
-
description:
|
|
115
|
+
description: COMMON_COMMAND_DESCRIPTIONS.plan,
|
|
112
116
|
inputHint: '/plan',
|
|
113
117
|
source: 'CLAUDE_AGENT_SDK',
|
|
114
118
|
interaction: 'TOGGLE',
|
|
@@ -137,7 +141,7 @@ const definitions = {
|
|
|
137
141
|
id: 'compact',
|
|
138
142
|
runner: 'opencode',
|
|
139
143
|
label: '/compact',
|
|
140
|
-
description:
|
|
144
|
+
description: COMMON_COMMAND_DESCRIPTIONS.compact,
|
|
141
145
|
inputHint: '/compact',
|
|
142
146
|
source: 'OPENCODE_SERVER',
|
|
143
147
|
interaction: 'IMMEDIATE_ACTION',
|
|
@@ -149,7 +153,7 @@ const definitions = {
|
|
|
149
153
|
runner: 'opencode',
|
|
150
154
|
label: '/plan',
|
|
151
155
|
stateLabel: 'Plan Mode',
|
|
152
|
-
description:
|
|
156
|
+
description: COMMON_COMMAND_DESCRIPTIONS.plan,
|
|
153
157
|
inputHint: '/plan',
|
|
154
158
|
source: 'OPENCODE_SERVER',
|
|
155
159
|
interaction: 'TOGGLE',
|
package/dist/runtime-state.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentRunStatus, RunnerName } from '@myagentroam/protocol';
|
|
2
|
-
import type { ConversationItemPage, ConversationPage, NativeSessionControl, NodeAgentRun, NodeAgentSession, NodeApproval, NodeConversationTurn, NodeMessageAttachmentInput, NodeRunEvent, NodeWorkspace
|
|
2
|
+
import type { ConversationItemPage, ConversationPage, NativeSessionControl, NodeAgentRun, NodeAgentSession, NodeApproval, NodeConversationTurn, NodeMessageAttachmentInput, NodeRunEvent, NodeWorkspace } from './database.js';
|
|
3
3
|
/**
|
|
4
4
|
* Deliberately process-local runtime domain. No method in this class writes
|
|
5
5
|
* into Node SQLite: a Node restart loses active runs, approvals, leases and
|
|
@@ -10,22 +10,22 @@ export declare class NodeRuntimeState {
|
|
|
10
10
|
private readonly nodeId;
|
|
11
11
|
private readonly getWorkspace;
|
|
12
12
|
private readonly now;
|
|
13
|
+
private static readonly MAX_COMPLETED_TURNS_PER_SESSION;
|
|
13
14
|
private readonly sessions;
|
|
14
15
|
private readonly runs;
|
|
15
16
|
private readonly messageRuns;
|
|
16
17
|
private readonly events;
|
|
17
18
|
private readonly turns;
|
|
18
19
|
private readonly approvals;
|
|
19
|
-
private readonly workspaceEvents;
|
|
20
20
|
/** A Session owns at most one running turn; Workspace sessions may run concurrently. */
|
|
21
21
|
private readonly leases;
|
|
22
22
|
/** Session FIFO metadata is deliberately process-local with the rest of run state. */
|
|
23
23
|
private readonly queuedRuns;
|
|
24
24
|
private readonly queueVersions;
|
|
25
25
|
private readonly pausedQueues;
|
|
26
|
-
|
|
26
|
+
/** Bounded tombstones reject stale internal start envelopes after terminal cleanup. */
|
|
27
|
+
private readonly terminalRunIds;
|
|
27
28
|
private nextEventId;
|
|
28
|
-
private nextWorkspaceEventId;
|
|
29
29
|
constructor(nodeId: () => string, getWorkspace: (id: string) => NodeWorkspace | undefined, now?: () => number);
|
|
30
30
|
activeWorkspaceLeaseCount(): number;
|
|
31
31
|
activeSessionRun(sessionId: string): NodeAgentRun | undefined;
|
|
@@ -138,11 +138,6 @@ export declare class NodeRuntimeState {
|
|
|
138
138
|
readonly outputPreview: string;
|
|
139
139
|
readonly failed?: boolean;
|
|
140
140
|
}): NodeConversationTurn;
|
|
141
|
-
listWorkspaceActivity(input: {
|
|
142
|
-
readonly workspaceId: string;
|
|
143
|
-
readonly cursor?: string;
|
|
144
|
-
readonly limit?: number;
|
|
145
|
-
}): WorkspaceActivityPage;
|
|
146
141
|
createApproval(input: {
|
|
147
142
|
readonly runId: string;
|
|
148
143
|
readonly approvalId: string;
|
|
@@ -156,8 +151,7 @@ export declare class NodeRuntimeState {
|
|
|
156
151
|
expireDueApprovals(): readonly NodeApproval[];
|
|
157
152
|
listNonterminalRuns(): readonly NodeAgentRun[];
|
|
158
153
|
lastRunSequence(id: string): number;
|
|
159
|
-
|
|
160
|
-
getSnapshot(id: string): unknown | undefined;
|
|
154
|
+
removeTerminalRun(runId: string): void;
|
|
161
155
|
hasActiveWorkspaceLease(workspaceId: string): boolean;
|
|
162
156
|
/** Removes a terminal Session's process-local projection after its native history is deleted. */
|
|
163
157
|
removeAgentSession(sessionId: string): void;
|
|
@@ -168,7 +162,6 @@ export declare class NodeRuntimeState {
|
|
|
168
162
|
private requireSession;
|
|
169
163
|
private requireRun;
|
|
170
164
|
private requireApproval;
|
|
171
|
-
private recordWorkspaceActivity;
|
|
172
165
|
private updateTurnForRun;
|
|
173
166
|
private projectEvent;
|
|
174
167
|
}
|