@myagentroam/node 0.1.4 → 0.1.7
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 +9 -1
- package/dist/capabilities.d.ts +2 -0
- package/dist/capabilities.js +15 -4
- package/dist/claude-agent-sdk.d.ts +3 -1
- package/dist/claude-agent-sdk.js +9 -2
- package/dist/claude-channel.js +0 -1
- package/dist/codex-app-server.d.ts +1 -0
- package/dist/codex-app-server.js +8 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +18 -2
- package/dist/connector/node-connector-options.d.ts +3 -0
- package/dist/connector/node-connector-options.js +0 -1
- package/dist/connector/node-operation-router.js +0 -1
- package/dist/connector.d.ts +3 -0
- package/dist/connector.js +109 -7
- package/dist/control-outbound-scheduler.js +0 -1
- package/dist/database.d.ts +4 -0
- package/dist/database.js +14 -9
- package/dist/event-buffer.js +0 -1
- package/dist/fake-runner.js +0 -1
- package/dist/health.js +0 -1
- package/dist/main.js +6 -1
- package/dist/migrations/v001.js +0 -1
- package/dist/migrations/v002.js +0 -1
- package/dist/migrations/v003.d.ts +4 -0
- package/dist/migrations/v003.js +44 -0
- package/dist/native-jsonl-reader.js +0 -1
- package/dist/native-session-history.d.ts +19 -1
- package/dist/native-session-history.js +65 -3
- package/dist/opencode-runtime.d.ts +33 -0
- package/dist/opencode-runtime.js +80 -0
- package/dist/opencode-server.d.ts +47 -0
- package/dist/opencode-server.js +250 -0
- package/dist/operational.js +10 -3
- package/dist/process-tree.js +0 -1
- package/dist/runner/abstract-runner.d.ts +7 -2
- package/dist/runner/abstract-runner.js +17 -2
- package/dist/runner/claude/managed-run-controller.js +20 -1
- package/dist/runner/claude-code-runner.d.ts +3 -1
- package/dist/runner/claude-code-runner.js +26 -8
- package/dist/runner/codex/conversation-parser.d.ts +1 -3
- package/dist/runner/codex/conversation-parser.js +174 -34
- package/dist/runner/codex/managed-run-controller.d.ts +2 -1
- package/dist/runner/codex/managed-run-controller.js +13 -4
- package/dist/runner/codex-runner.d.ts +4 -0
- package/dist/runner/codex-runner.js +27 -4
- package/dist/runner/fake-test-runner.js +0 -1
- package/dist/runner/opencode/conversation-parser.d.ts +27 -0
- package/dist/runner/opencode/conversation-parser.js +217 -0
- package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
- package/dist/runner/opencode/managed-run-controller.js +451 -0
- package/dist/runner/opencode-runner.d.ts +60 -0
- package/dist/runner/opencode-runner.js +330 -0
- package/dist/runner/runner-registry.d.ts +3 -1
- package/dist/runner/runner-registry.js +12 -3
- package/dist/runner-command-engine.js +0 -1
- package/dist/runner-profiles.d.ts +4 -0
- package/dist/runner-profiles.js +76 -3
- package/dist/runner-usage.js +0 -1
- package/dist/runtime-state.js +20 -9
- package/dist/service/attachment-domain-state.js +0 -1
- package/dist/service/conversation-history-service.js +0 -1
- package/dist/service/external-session-resume-service.js +0 -1
- package/dist/service/fake-managed-run-service.js +0 -1
- package/dist/service/managed-runner-session-service.d.ts +3 -1
- package/dist/service/managed-runner-session-service.js +8 -4
- package/dist/service/native-session-projection-service.d.ts +2 -1
- package/dist/service/native-session-projection-service.js +8 -5
- package/dist/service/native-session-watch-service.js +0 -1
- package/dist/service/node-connection-lifecycle-service.js +3 -2
- package/dist/service/node-control-channel.d.ts +1 -1
- package/dist/service/node-control-channel.js +5 -4
- package/dist/service/node-control-message-service.js +0 -1
- package/dist/service/node-request-service.js +0 -1
- package/dist/service/node-run-event-bridge.d.ts +2 -0
- package/dist/service/node-run-event-bridge.js +4 -1
- package/dist/service/node-terminal-channel.js +16 -10
- package/dist/service/node-upgrade-coordinator.d.ts +29 -0
- package/dist/service/node-upgrade-coordinator.js +137 -0
- package/dist/service/node-workspace-coordinator.js +0 -1
- package/dist/service/run-attachment-service.js +0 -1
- package/dist/service/run-domain-state.js +0 -1
- package/dist/service/run-event-service.js +0 -1
- package/dist/service/run-workbench-service.js +0 -1
- package/dist/service/runner-channel-message-service.js +0 -1
- package/dist/service/runner-interaction-service.js +2 -1
- package/dist/service/runner-service.d.ts +5 -4
- package/dist/service/runner-service.js +19 -6
- package/dist/service/session-catalog-service.d.ts +16 -1
- package/dist/service/session-catalog-service.js +82 -3
- package/dist/service/session-command-service.js +0 -1
- package/dist/service/session-context-service.js +0 -1
- package/dist/service/session-domain-state.js +0 -1
- package/dist/service/session-identity-service.js +0 -1
- package/dist/service/session-lifecycle-service.js +13 -7
- package/dist/service/session-message-service.js +0 -1
- package/dist/service/session-presentation-service.js +0 -1
- package/dist/service/session-query-service.js +0 -1
- package/dist/service/terminal-relay-state.js +0 -1
- package/dist/service/terminal-service.js +0 -1
- package/dist/service/workbench-event-service.js +0 -1
- package/dist/service/workbench-manifest-service.d.ts +5 -4
- package/dist/service/workbench-manifest-service.js +3 -2
- package/dist/service/workspace-change-service.js +2 -2
- package/dist/service/workspace-content-search-service.d.ts +14 -0
- package/dist/service/workspace-content-search-service.js +477 -0
- package/dist/service/workspace-domain-state.js +0 -1
- package/dist/service/workspace-file-service.d.ts +3 -0
- package/dist/service/workspace-file-service.js +35 -3
- package/dist/service/workspace-queue-workbench-service.js +0 -1
- package/dist/service/workspace-service.js +23 -10
- package/dist/service/workspace-session-service.js +0 -1
- package/dist/service/workspace-upload-service.js +0 -1
- package/dist/service/workspace-watch-service.js +0 -1
- package/dist/service/workspace-workbench-service.d.ts +9 -0
- package/dist/service/workspace-workbench-service.js +69 -2
- package/dist/service.js +2 -3
- package/dist/storage.js +0 -1
- package/dist/supervisor.d.ts +1 -0
- package/dist/supervisor.js +154 -0
- package/dist/terminal.js +0 -1
- package/dist/util/node-operation-parsers.d.ts +1 -0
- package/dist/util/node-operation-parsers.js +3 -1
- package/dist/util/runner-native-session-parsers.js +117 -73
- package/dist/util/secret-environment.js +0 -1
- package/dist/workspace.d.ts +62 -2
- package/dist/workspace.js +539 -57
- package/package.json +3 -2
- package/dist/capabilities.js.map +0 -1
- package/dist/claude-agent-sdk.js.map +0 -1
- package/dist/claude-channel.js.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/connector/node-connector-options.js.map +0 -1
- package/dist/connector/node-operation-router.js.map +0 -1
- package/dist/connector.js.map +0 -1
- package/dist/control-outbound-scheduler.js.map +0 -1
- package/dist/database.js.map +0 -1
- package/dist/event-buffer.js.map +0 -1
- package/dist/fake-runner.js.map +0 -1
- package/dist/health.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/migrations/v001.js.map +0 -1
- package/dist/migrations/v002.js.map +0 -1
- package/dist/native-jsonl-reader.js.map +0 -1
- package/dist/native-session-history.js.map +0 -1
- package/dist/operational.js.map +0 -1
- package/dist/process-tree.js.map +0 -1
- package/dist/runner/abstract-runner.js.map +0 -1
- package/dist/runner/claude/managed-run-controller.js.map +0 -1
- package/dist/runner/claude-code-runner.js.map +0 -1
- package/dist/runner/codex/conversation-parser.js.map +0 -1
- package/dist/runner/codex/managed-run-controller.js.map +0 -1
- package/dist/runner/codex-runner.js.map +0 -1
- package/dist/runner/fake-test-runner.js.map +0 -1
- package/dist/runner/runner-registry.js.map +0 -1
- package/dist/runner-command-engine.js.map +0 -1
- package/dist/runner-profiles.js.map +0 -1
- package/dist/runner-usage.js.map +0 -1
- package/dist/runtime-state.js.map +0 -1
- package/dist/service/attachment-domain-state.js.map +0 -1
- package/dist/service/conversation-history-service.js.map +0 -1
- package/dist/service/external-session-resume-service.js.map +0 -1
- package/dist/service/fake-managed-run-service.js.map +0 -1
- package/dist/service/managed-runner-session-service.js.map +0 -1
- package/dist/service/native-session-projection-service.js.map +0 -1
- package/dist/service/native-session-watch-service.js.map +0 -1
- package/dist/service/node-connection-lifecycle-service.js.map +0 -1
- package/dist/service/node-control-channel.js.map +0 -1
- package/dist/service/node-control-message-service.js.map +0 -1
- package/dist/service/node-request-service.js.map +0 -1
- package/dist/service/node-run-event-bridge.js.map +0 -1
- package/dist/service/node-terminal-channel.js.map +0 -1
- package/dist/service/node-workspace-coordinator.js.map +0 -1
- package/dist/service/run-attachment-service.js.map +0 -1
- package/dist/service/run-domain-state.js.map +0 -1
- package/dist/service/run-event-service.js.map +0 -1
- package/dist/service/run-workbench-service.js.map +0 -1
- package/dist/service/runner-channel-message-service.js.map +0 -1
- package/dist/service/runner-interaction-service.js.map +0 -1
- package/dist/service/runner-service.js.map +0 -1
- package/dist/service/session-catalog-service.js.map +0 -1
- package/dist/service/session-command-service.js.map +0 -1
- package/dist/service/session-context-service.js.map +0 -1
- package/dist/service/session-domain-state.js.map +0 -1
- package/dist/service/session-identity-service.js.map +0 -1
- package/dist/service/session-lifecycle-service.js.map +0 -1
- package/dist/service/session-message-service.js.map +0 -1
- package/dist/service/session-presentation-service.js.map +0 -1
- package/dist/service/session-query-service.js.map +0 -1
- package/dist/service/terminal-relay-state.js.map +0 -1
- package/dist/service/terminal-service.js.map +0 -1
- package/dist/service/workbench-event-service.js.map +0 -1
- package/dist/service/workbench-manifest-service.js.map +0 -1
- package/dist/service/workspace-change-service.js.map +0 -1
- package/dist/service/workspace-domain-state.js.map +0 -1
- package/dist/service/workspace-file-service.js.map +0 -1
- package/dist/service/workspace-queue-workbench-service.js.map +0 -1
- package/dist/service/workspace-service.js.map +0 -1
- package/dist/service/workspace-session-service.js.map +0 -1
- package/dist/service/workspace-upload-service.js.map +0 -1
- package/dist/service/workspace-watch-service.js.map +0 -1
- package/dist/service/workspace-workbench-service.js.map +0 -1
- package/dist/service.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/terminal.js.map +0 -1
- package/dist/util/node-operation-parsers.js.map +0 -1
- package/dist/util/runner-native-session-parsers.js.map +0 -1
- package/dist/util/secret-environment.js.map +0 -1
- package/dist/workspace.js.map +0 -1
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { OpenCodeServerClient } from '../opencode-server.js';
|
|
2
|
+
import { openCodeContextLimits, openCodeProfileModels } from '../opencode-runtime.js';
|
|
3
|
+
import { declaredRunnerProfiles } from '../runner-profiles.js';
|
|
4
|
+
import { AbstractRunner } from './abstract-runner.js';
|
|
5
|
+
import { isWithinWorkspace } from './codex/conversation-parser.js';
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { openCodeConversationPage } from './opencode/conversation-parser.js';
|
|
8
|
+
export class OpenCodeRunner extends AbstractRunner {
|
|
9
|
+
client;
|
|
10
|
+
clientFactory;
|
|
11
|
+
name = 'opencode';
|
|
12
|
+
discoveryScope = 'WORKSPACE_REQUIRED';
|
|
13
|
+
modelsByWorkspace = new Map();
|
|
14
|
+
contextLimitsByWorkspace = new Map();
|
|
15
|
+
execution = new Map();
|
|
16
|
+
managedTurns = new Map();
|
|
17
|
+
environmentClients = new Map();
|
|
18
|
+
stoppingClients = new Set();
|
|
19
|
+
constructor(client = new OpenCodeServerClient(), clientFactory = () => new OpenCodeServerClient()) {
|
|
20
|
+
super();
|
|
21
|
+
this.client = client;
|
|
22
|
+
this.clientFactory = clientFactory;
|
|
23
|
+
}
|
|
24
|
+
profile(capabilities) {
|
|
25
|
+
const base = declaredRunnerProfiles({
|
|
26
|
+
codexAvailable: capabilities.codex.available,
|
|
27
|
+
claudeCodeAvailable: capabilities.claudeCode.available,
|
|
28
|
+
openCodeAvailable: capabilities.openCode?.available === true
|
|
29
|
+
}).find((profile) => profile.runner === this.name);
|
|
30
|
+
return { ...base, models: [] };
|
|
31
|
+
}
|
|
32
|
+
async refreshProfile(capabilities, workspace, environment = {}) {
|
|
33
|
+
if (capabilities.openCode?.available !== true || workspace === undefined) {
|
|
34
|
+
return this.profile(capabilities);
|
|
35
|
+
}
|
|
36
|
+
const client = this.managedClient(environment);
|
|
37
|
+
try {
|
|
38
|
+
const catalog = await client.providers(workspace.path);
|
|
39
|
+
const models = openCodeProfileModels(catalog);
|
|
40
|
+
this.modelsByWorkspace.set(workspace.path, models);
|
|
41
|
+
this.contextLimitsByWorkspace.set(workspace.path, openCodeContextLimits(catalog));
|
|
42
|
+
return { ...this.profile(capabilities), models };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
this.modelsByWorkspace.delete(workspace.path);
|
|
46
|
+
this.contextLimitsByWorkspace.delete(workspace.path);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.releaseManagedClient(client);
|
|
50
|
+
}
|
|
51
|
+
return this.profile(capabilities);
|
|
52
|
+
}
|
|
53
|
+
available(capabilities) {
|
|
54
|
+
return capabilities.openCode?.available === true;
|
|
55
|
+
}
|
|
56
|
+
profileForValidation() {
|
|
57
|
+
return this.profile({
|
|
58
|
+
platform: 'linux',
|
|
59
|
+
architecture: 'validation',
|
|
60
|
+
nodeAppVersion: 'validation',
|
|
61
|
+
nodeVersion: 'validation',
|
|
62
|
+
codex: { available: false },
|
|
63
|
+
claudeCode: { available: false },
|
|
64
|
+
openCode: { available: true }
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
supportsConfiguration(configuration, workspace) {
|
|
68
|
+
if (configuration.runner !== this.name)
|
|
69
|
+
return false;
|
|
70
|
+
const models = workspace === undefined
|
|
71
|
+
? [...this.modelsByWorkspace.values()].flat()
|
|
72
|
+
: (this.modelsByWorkspace.get(workspace.path) ?? []);
|
|
73
|
+
const model = models.find((candidate) => candidate.id === configuration.model);
|
|
74
|
+
return (model !== undefined &&
|
|
75
|
+
(model.supportedEfforts.length === 0 ||
|
|
76
|
+
model.supportedEfforts.includes(configuration.effort)) &&
|
|
77
|
+
this.profileForValidation().accessOptions.some((candidate) => candidate.id === configuration.access));
|
|
78
|
+
}
|
|
79
|
+
defaultConfiguration(workspace) {
|
|
80
|
+
const model = workspace === undefined
|
|
81
|
+
? this.modelsByWorkspace.values().next().value?.[0]
|
|
82
|
+
: this.modelsByWorkspace.get(workspace.path)?.[0];
|
|
83
|
+
return {
|
|
84
|
+
runner: this.name,
|
|
85
|
+
model: model?.id ?? '',
|
|
86
|
+
effort: model?.supportedEfforts[0] ?? '',
|
|
87
|
+
access: 'default'
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async discoverSessions(context, workspace) {
|
|
91
|
+
const result = [];
|
|
92
|
+
for (const target of workspace === undefined ? context.workspaces : [workspace]) {
|
|
93
|
+
let sessions;
|
|
94
|
+
try {
|
|
95
|
+
sessions = await this.client.sessions(target.path);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const value of sessions) {
|
|
101
|
+
const session = openCodeSession(value);
|
|
102
|
+
if (session === undefined || !isWithinWorkspace(session.directory, target.path))
|
|
103
|
+
continue;
|
|
104
|
+
result.push(context.createProjection({
|
|
105
|
+
workspace: target,
|
|
106
|
+
runner: this.name,
|
|
107
|
+
externalSessionId: session.id,
|
|
108
|
+
cwd: session.directory,
|
|
109
|
+
title: session.title,
|
|
110
|
+
titleOrigin: 'OFFICIAL',
|
|
111
|
+
lastActivityAt: session.updatedAt
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
async resumeExternal(external, context) {
|
|
118
|
+
if (external.runner !== this.name || external.externalSessionId === null || !context.available)
|
|
119
|
+
return { failureCode: 'RUNNER_UNAVAILABLE' };
|
|
120
|
+
const activity = await this.readExternalActivity(external);
|
|
121
|
+
if (activity !== 'IDLE')
|
|
122
|
+
return {
|
|
123
|
+
activity: activity ?? 'UNAVAILABLE',
|
|
124
|
+
failureCode: activity === 'EXTERNAL_ACTIVE' ? 'EXTERNAL_SESSION_ACTIVE' : 'RUNNER_RESUME_FAILED'
|
|
125
|
+
};
|
|
126
|
+
const projection = context.projectionFor(external);
|
|
127
|
+
const session = projection === undefined ? context.create(external) : context.promote(projection.id);
|
|
128
|
+
return session === undefined
|
|
129
|
+
? { activity: 'UNAVAILABLE', failureCode: 'RUNNER_RESUME_FAILED' }
|
|
130
|
+
: { activity: 'IDLE', session: context.ensureConfiguration(session) };
|
|
131
|
+
}
|
|
132
|
+
async readExternalActivity(session) {
|
|
133
|
+
if (session.externalSessionId === null)
|
|
134
|
+
return 'UNAVAILABLE';
|
|
135
|
+
try {
|
|
136
|
+
const status = await this.client.sessionStatus(session.cwd);
|
|
137
|
+
const value = status[session.externalSessionId];
|
|
138
|
+
return value?.type === 'busy' || value?.type === 'retry'
|
|
139
|
+
? 'EXTERNAL_ACTIVE'
|
|
140
|
+
: value?.type === 'idle' || value === undefined
|
|
141
|
+
? 'IDLE'
|
|
142
|
+
: 'UNAVAILABLE';
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return 'UNAVAILABLE';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async readOfficialConversation(session, input, managedTurn) {
|
|
149
|
+
if (session.externalSessionId === null)
|
|
150
|
+
return undefined;
|
|
151
|
+
try {
|
|
152
|
+
return openCodeConversationPage(session, await this.client.messages(session.cwd, session.externalSessionId, 500), input, managedTurn);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
managedRunForNativeTurn(nativeTurnId) {
|
|
159
|
+
return this.managedTurns.get(nativeTurnId);
|
|
160
|
+
}
|
|
161
|
+
presentSession(session, capabilities, context) {
|
|
162
|
+
const available = capabilities.openCode?.available === true;
|
|
163
|
+
const managed = session.nativeControl === 'MAR_MANAGED';
|
|
164
|
+
const activity = context.managedActive
|
|
165
|
+
? 'MANAGED_ACTIVE'
|
|
166
|
+
: (context.externalActivity ?? (available ? 'IDLE' : 'UNAVAILABLE'));
|
|
167
|
+
if (!available)
|
|
168
|
+
return {
|
|
169
|
+
activity: 'UNAVAILABLE',
|
|
170
|
+
control: { level: 'UNAVAILABLE', capabilities: [] },
|
|
171
|
+
runtime: { level: 'UNAVAILABLE', capabilities: [], reasonCode: 'RUNNER_UNAVAILABLE' },
|
|
172
|
+
commandStates: context.commandStates
|
|
173
|
+
};
|
|
174
|
+
const runtimeCapabilities = [
|
|
175
|
+
'history.read',
|
|
176
|
+
'events.live',
|
|
177
|
+
'turn.start',
|
|
178
|
+
'turn.queue',
|
|
179
|
+
'turn.interrupt',
|
|
180
|
+
'run.cancel',
|
|
181
|
+
'approval.respond',
|
|
182
|
+
'user-input.respond',
|
|
183
|
+
'session.rename',
|
|
184
|
+
'session.remove',
|
|
185
|
+
'session.pin',
|
|
186
|
+
...(session.externalSessionId === null ? [] : ['session.rewind'])
|
|
187
|
+
];
|
|
188
|
+
return {
|
|
189
|
+
activity,
|
|
190
|
+
control: {
|
|
191
|
+
level: managed ? 'MANAGED' : activity === 'IDLE' ? 'RESUMABLE' : 'OBSERVE_ONLY',
|
|
192
|
+
capabilities: managed || activity === 'IDLE'
|
|
193
|
+
? ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
194
|
+
: ['history.read', 'events.live']
|
|
195
|
+
},
|
|
196
|
+
runtime: {
|
|
197
|
+
level: managed ? 'MANAGED' : activity === 'IDLE' ? 'RESUMABLE' : 'OBSERVE_ONLY',
|
|
198
|
+
capabilities: runtimeCapabilities,
|
|
199
|
+
reasonCode: activity === 'EXTERNAL_ACTIVE' ? 'EXTERNAL_SESSION_OBSERVE_ONLY' : null
|
|
200
|
+
},
|
|
201
|
+
commandStates: context.commandStates
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async renameNative(session, input, database) {
|
|
205
|
+
if (session.externalSessionId !== null && input.title !== undefined)
|
|
206
|
+
await this.client.updateSession(session.cwd, session.externalSessionId, input.title ?? '');
|
|
207
|
+
await super.renameNative(session, input, database);
|
|
208
|
+
}
|
|
209
|
+
async removeNative(session, nativeSessionId) {
|
|
210
|
+
await this.client.deleteSession(session.cwd, nativeSessionId);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
async forkNative(session, boundary, title) {
|
|
214
|
+
if (session.externalSessionId === null)
|
|
215
|
+
throw new Error('SESSION_REWIND_UNAVAILABLE');
|
|
216
|
+
const result = boundary === null
|
|
217
|
+
? await this.client.createSession(session.cwd, title)
|
|
218
|
+
: await this.client.forkSession(session.cwd, session.externalSessionId, boundary);
|
|
219
|
+
const value = object(result);
|
|
220
|
+
if (typeof value?.id !== 'string')
|
|
221
|
+
throw new Error('SESSION_REWIND_FAILED');
|
|
222
|
+
if (boundary !== null)
|
|
223
|
+
await this.client.updateSession(session.cwd, value.id, title);
|
|
224
|
+
return value.id;
|
|
225
|
+
}
|
|
226
|
+
async executeCompact(session, environment) {
|
|
227
|
+
if (session.externalSessionId === null || session.model === null)
|
|
228
|
+
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
229
|
+
const client = this.managedClient(environment);
|
|
230
|
+
try {
|
|
231
|
+
return await client.summarize(session.cwd, session.externalSessionId, session.model);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
this.releaseManagedClient(client);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async readContextUsage(session) {
|
|
238
|
+
if (session.externalSessionId === null)
|
|
239
|
+
return undefined;
|
|
240
|
+
const messages = await this.client.messages(session.cwd, session.externalSessionId, 100);
|
|
241
|
+
const assistants = messages.flatMap((value) => {
|
|
242
|
+
const entry = object(value);
|
|
243
|
+
const info = object(entry?.info);
|
|
244
|
+
return info?.role === 'assistant' ? [info] : [];
|
|
245
|
+
});
|
|
246
|
+
const latest = assistants.at(-1);
|
|
247
|
+
const provider = typeof latest?.providerID === 'string' ? latest.providerID : undefined;
|
|
248
|
+
const model = typeof latest?.modelID === 'string' ? latest.modelID : undefined;
|
|
249
|
+
const tokens = object(latest?.tokens);
|
|
250
|
+
const cache = object(tokens?.cache);
|
|
251
|
+
const usedTokens = positiveNumber(tokens?.input) + positiveNumber(cache?.read);
|
|
252
|
+
const modelId = provider !== undefined && model !== undefined ? `${provider}/${model}` : session.model;
|
|
253
|
+
const limits = [...this.contextLimitsByWorkspace].find(([workspacePath]) => isWithinWorkspace(session.cwd, workspacePath))?.[1];
|
|
254
|
+
const maxTokens = modelId === null ? undefined : limits?.get(modelId);
|
|
255
|
+
return maxTokens === undefined || usedTokens <= 0
|
|
256
|
+
? undefined
|
|
257
|
+
: { model: modelId, usedTokens, maxTokens };
|
|
258
|
+
}
|
|
259
|
+
async stop() {
|
|
260
|
+
for (const execution of this.execution.values())
|
|
261
|
+
execution.abort.abort();
|
|
262
|
+
this.execution.clear();
|
|
263
|
+
this.managedTurns.clear();
|
|
264
|
+
const stopping = [
|
|
265
|
+
this.client.stop(),
|
|
266
|
+
...[...this.environmentClients.values()].map(({ client }) => client.stop()),
|
|
267
|
+
...this.stoppingClients
|
|
268
|
+
];
|
|
269
|
+
this.environmentClients.clear();
|
|
270
|
+
await Promise.allSettled(stopping);
|
|
271
|
+
this.stoppingClients.clear();
|
|
272
|
+
}
|
|
273
|
+
managedClient(environment) {
|
|
274
|
+
const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
|
|
275
|
+
if (entries.length === 0)
|
|
276
|
+
return this.client;
|
|
277
|
+
const fingerprint = createHash('sha256').update(JSON.stringify(entries)).digest('hex');
|
|
278
|
+
const existing = this.environmentClients.get(fingerprint);
|
|
279
|
+
if (existing !== undefined) {
|
|
280
|
+
existing.leases += 1;
|
|
281
|
+
return existing.client;
|
|
282
|
+
}
|
|
283
|
+
const client = this.clientFactory();
|
|
284
|
+
client.configureEnvironment(environment);
|
|
285
|
+
this.environmentClients.set(fingerprint, { client, leases: 1 });
|
|
286
|
+
return client;
|
|
287
|
+
}
|
|
288
|
+
releaseManagedClient(client) {
|
|
289
|
+
if (client === this.client)
|
|
290
|
+
return;
|
|
291
|
+
for (const [fingerprint, entry] of this.environmentClients) {
|
|
292
|
+
if (entry.client !== client)
|
|
293
|
+
continue;
|
|
294
|
+
entry.leases -= 1;
|
|
295
|
+
if (entry.leases <= 0) {
|
|
296
|
+
const stopping = entry.client.stop();
|
|
297
|
+
this.stoppingClients.add(stopping);
|
|
298
|
+
void stopping.finally(() => this.stoppingClients.delete(stopping));
|
|
299
|
+
this.environmentClients.delete(fingerprint);
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function object(value) {
|
|
306
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
307
|
+
? value
|
|
308
|
+
: undefined;
|
|
309
|
+
}
|
|
310
|
+
function positiveNumber(value) {
|
|
311
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
312
|
+
}
|
|
313
|
+
function openCodeSession(value) {
|
|
314
|
+
if (typeof value !== 'object' || value === null)
|
|
315
|
+
return undefined;
|
|
316
|
+
const record = value;
|
|
317
|
+
const time = typeof record.time === 'object' && record.time !== null
|
|
318
|
+
? record.time
|
|
319
|
+
: undefined;
|
|
320
|
+
if (typeof record.id !== 'string' ||
|
|
321
|
+
typeof record.directory !== 'string' ||
|
|
322
|
+
typeof record.title !== 'string')
|
|
323
|
+
return undefined;
|
|
324
|
+
return {
|
|
325
|
+
id: record.id,
|
|
326
|
+
directory: record.directory,
|
|
327
|
+
title: record.title,
|
|
328
|
+
updatedAt: typeof time?.updated === 'number' ? time.updated : 0
|
|
329
|
+
};
|
|
330
|
+
}
|
|
@@ -9,9 +9,11 @@ export declare class RunnerRegistry {
|
|
|
9
9
|
advertised(): readonly AbstractRunner[];
|
|
10
10
|
product(name: unknown): AbstractRunner<RunnerName> | undefined;
|
|
11
11
|
profiles(capabilities: NodeCapabilities): readonly RunnerProfile[];
|
|
12
|
-
|
|
12
|
+
refreshProfiles(capabilities: NodeCapabilities, workspace?: import('../database.js').NodeWorkspace, environment?: Readonly<Record<string, string>>): Promise<readonly RunnerProfile[]>;
|
|
13
|
+
supportsConfiguration(configuration: RunnerDefaultConfiguration, workspace?: import('../database.js').NodeWorkspace): boolean;
|
|
13
14
|
startManagedRun(envelope: NodeEnvelope): boolean;
|
|
14
15
|
cancelManagedRun(runId: string, intent: RunnerCancellationIntent): boolean;
|
|
15
16
|
decideApproval(input: RunnerApprovalDecision): boolean;
|
|
17
|
+
respondUserInput(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
|
|
16
18
|
stop(): Promise<void>;
|
|
17
19
|
}
|
|
@@ -34,8 +34,12 @@ export class RunnerRegistry {
|
|
|
34
34
|
return profile === undefined ? [] : [profile];
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
async refreshProfiles(capabilities, workspace, environment = {}) {
|
|
38
|
+
const profiles = await Promise.all(this.advertised().map((runner) => runner.refreshProfile(capabilities, workspace, environment)));
|
|
39
|
+
return profiles.flatMap((profile) => (profile === undefined ? [] : [profile]));
|
|
40
|
+
}
|
|
41
|
+
supportsConfiguration(configuration, workspace) {
|
|
42
|
+
return (this.product(configuration.runner)?.supportsConfiguration(configuration, workspace) === true);
|
|
39
43
|
}
|
|
40
44
|
startManagedRun(envelope) {
|
|
41
45
|
const runnerName = envelope.payload.runner;
|
|
@@ -59,8 +63,13 @@ export class RunnerRegistry {
|
|
|
59
63
|
handled = runner.decideApproval(input) || handled;
|
|
60
64
|
return handled;
|
|
61
65
|
}
|
|
66
|
+
respondUserInput(runId, requestId, answers) {
|
|
67
|
+
for (const runner of this.runners.values())
|
|
68
|
+
if (runner.respondUserInput(runId, requestId, answers))
|
|
69
|
+
return true;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
62
72
|
async stop() {
|
|
63
73
|
await Promise.allSettled([...this.runners.values()].map((runner) => runner.stop()));
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
|
-
//# sourceMappingURL=runner-registry.js.map
|
|
@@ -2,10 +2,14 @@ import type { RunnerName, RunnerProfile } from '@myagentroam/protocol';
|
|
|
2
2
|
export declare function declaredRunnerProfiles(input: {
|
|
3
3
|
readonly codexAvailable: boolean;
|
|
4
4
|
readonly claudeCodeAvailable: boolean;
|
|
5
|
+
readonly environment?: NodeJS.ProcessEnv;
|
|
6
|
+
readonly openCodeAvailable?: boolean;
|
|
5
7
|
}): readonly RunnerProfile[];
|
|
6
8
|
export declare function supportsRunnerConfiguration(input: {
|
|
7
9
|
readonly runner: RunnerName;
|
|
8
10
|
readonly model: string;
|
|
9
11
|
readonly effort: string;
|
|
10
12
|
readonly access: string;
|
|
13
|
+
readonly environment?: NodeJS.ProcessEnv;
|
|
11
14
|
}): boolean;
|
|
15
|
+
export declare function claudeDefaultModel(environment?: NodeJS.ProcessEnv): string;
|
package/dist/runner-profiles.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// Codex reasoning efforts follow the official Codex model surface. Keep this
|
|
2
2
|
// shared so an alias such as GPT-5.6 Luna cannot accidentally lose `high`.
|
|
3
3
|
const CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
|
|
4
|
+
const CLAUDE_MODEL_ENVIRONMENT = {
|
|
5
|
+
opus: 'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
6
|
+
sonnet: 'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
7
|
+
haiku: 'ANTHROPIC_DEFAULT_HAIKU_MODEL'
|
|
8
|
+
};
|
|
4
9
|
/**
|
|
5
10
|
* The Runner APIs used by MAR do not expose a portable model-list endpoint.
|
|
6
11
|
* Keep the supported, product-visible choices in one Node-owned definition so
|
|
@@ -123,21 +128,90 @@ const definitions = {
|
|
|
123
128
|
reasonCode: null
|
|
124
129
|
}
|
|
125
130
|
]
|
|
131
|
+
},
|
|
132
|
+
opencode: {
|
|
133
|
+
runner: 'opencode',
|
|
134
|
+
models: [],
|
|
135
|
+
accessOptions: [
|
|
136
|
+
{
|
|
137
|
+
id: 'default',
|
|
138
|
+
label: 'Default',
|
|
139
|
+
description: '按 OpenCode 和当前 Workspace 配置确认权限。'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'auto',
|
|
143
|
+
label: 'Auto approve',
|
|
144
|
+
description: '自动允许 OpenCode 请求的工具权限。'
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
commands: [
|
|
148
|
+
{
|
|
149
|
+
id: 'compact',
|
|
150
|
+
runner: 'opencode',
|
|
151
|
+
label: '/compact',
|
|
152
|
+
description: '通过 OpenCode 压缩当前会话上下文。',
|
|
153
|
+
inputHint: '/compact',
|
|
154
|
+
source: 'OPENCODE_SERVER',
|
|
155
|
+
interaction: 'IMMEDIATE_ACTION',
|
|
156
|
+
available: true,
|
|
157
|
+
reasonCode: null
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'plan',
|
|
161
|
+
runner: 'opencode',
|
|
162
|
+
label: '/plan',
|
|
163
|
+
stateLabel: 'Plan Mode',
|
|
164
|
+
description: '开启或关闭 OpenCode 原生 Plan Agent。',
|
|
165
|
+
inputHint: '/plan',
|
|
166
|
+
source: 'OPENCODE_SERVER',
|
|
167
|
+
interaction: 'TOGGLE',
|
|
168
|
+
available: true,
|
|
169
|
+
reasonCode: null
|
|
170
|
+
}
|
|
171
|
+
]
|
|
126
172
|
}
|
|
127
173
|
};
|
|
128
174
|
export function declaredRunnerProfiles(input) {
|
|
129
175
|
return [
|
|
130
176
|
withCapabilities(definitions.codex, input.codexAvailable),
|
|
131
|
-
withCapabilities(
|
|
177
|
+
withCapabilities(claudeProfile(input.environment ?? process.env), input.claudeCodeAvailable),
|
|
178
|
+
withCapabilities(definitions.opencode, input.openCodeAvailable === true)
|
|
132
179
|
];
|
|
133
180
|
}
|
|
134
181
|
export function supportsRunnerConfiguration(input) {
|
|
135
|
-
const profile =
|
|
182
|
+
const profile = input.runner === 'claude-code'
|
|
183
|
+
? claudeProfile(input.environment ?? process.env)
|
|
184
|
+
: definitions[input.runner];
|
|
136
185
|
const model = profile.models.find((candidate) => candidate.id === input.model);
|
|
137
186
|
return (model !== undefined &&
|
|
138
187
|
model.supportedEfforts.includes(input.effort) &&
|
|
139
188
|
profile.accessOptions.some((candidate) => candidate.id === input.access));
|
|
140
189
|
}
|
|
190
|
+
export function claudeDefaultModel(environment = process.env) {
|
|
191
|
+
return configuredClaudeModel('sonnet', environment) ?? 'sonnet';
|
|
192
|
+
}
|
|
193
|
+
function claudeProfile(environment) {
|
|
194
|
+
const profile = definitions['claude-code'];
|
|
195
|
+
const models = profile.models.map((model) => {
|
|
196
|
+
const configured = configuredClaudeModel(model.id, environment);
|
|
197
|
+
return configured === undefined ? model : { ...model, id: configured, label: configured };
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
...profile,
|
|
201
|
+
models: models.filter((model, index) => models.findIndex((candidate) => candidate.id === model.id) === index)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function configuredClaudeModel(alias, environment) {
|
|
205
|
+
const environmentName = CLAUDE_MODEL_ENVIRONMENT[alias];
|
|
206
|
+
return (validModelName(environmentName === undefined ? undefined : environment[environmentName]) ??
|
|
207
|
+
(alias === 'sonnet' ? validModelName(environment['ANTHROPIC_MODEL']) : undefined));
|
|
208
|
+
}
|
|
209
|
+
function validModelName(value) {
|
|
210
|
+
const normalized = value?.trim();
|
|
211
|
+
return normalized !== undefined && normalized.length > 0 && normalized.length <= 128
|
|
212
|
+
? normalized
|
|
213
|
+
: undefined;
|
|
214
|
+
}
|
|
141
215
|
function withCapabilities(profile, available) {
|
|
142
216
|
return {
|
|
143
217
|
...profile,
|
|
@@ -147,4 +221,3 @@ function withCapabilities(profile, available) {
|
|
|
147
221
|
: []
|
|
148
222
|
};
|
|
149
223
|
}
|
|
150
|
-
//# sourceMappingURL=runner-profiles.js.map
|
package/dist/runner-usage.js
CHANGED
package/dist/runtime-state.js
CHANGED
|
@@ -754,15 +754,18 @@ export class NodeRuntimeState {
|
|
|
754
754
|
if (event.eventType === 'text.delta' &&
|
|
755
755
|
isRecord(event.payload) &&
|
|
756
756
|
typeof event.payload.text === 'string') {
|
|
757
|
-
const
|
|
757
|
+
const lastItem = turn.items.at(-1);
|
|
758
|
+
const existing = lastItem?.kind === 'assistant_message' && activeConversationItem(lastItem.status)
|
|
759
|
+
? lastItem
|
|
760
|
+
: undefined;
|
|
758
761
|
const text = `${existing === undefined ? '' : readableText(existing.payload)}${event.payload.text}`;
|
|
759
762
|
const item = {
|
|
760
|
-
id: `${event.runId}:assistant`,
|
|
763
|
+
id: existing?.id ?? `${event.runId}:assistant:${event.sequence}`,
|
|
761
764
|
sessionId: run.sessionId,
|
|
762
765
|
turnId: turn.id,
|
|
763
766
|
runId: event.runId,
|
|
764
767
|
parentItemId: null,
|
|
765
|
-
sourceSequence: event.sequence,
|
|
768
|
+
sourceSequence: existing?.sourceSequence ?? event.sequence,
|
|
766
769
|
revision: event.sequence,
|
|
767
770
|
kind: 'assistant_message',
|
|
768
771
|
status: 'IN_PROGRESS',
|
|
@@ -774,12 +777,20 @@ export class NodeRuntimeState {
|
|
|
774
777
|
...turn,
|
|
775
778
|
status: 'RUNNING',
|
|
776
779
|
startedAt: turn.startedAt ?? event.createdAt,
|
|
777
|
-
items:
|
|
780
|
+
items: existing === undefined
|
|
781
|
+
? [...turn.items, item]
|
|
782
|
+
: turn.items.map((value) => (value.id === item.id ? item : value))
|
|
778
783
|
};
|
|
779
784
|
}
|
|
780
785
|
const projected = conversationItemFromEvent(event);
|
|
781
786
|
if (projected !== undefined) {
|
|
782
|
-
const
|
|
787
|
+
const trailingItem = turn.items.at(-1);
|
|
788
|
+
const orderedItems = trailingItem?.kind === 'assistant_message' && activeConversationItem(trailingItem.status)
|
|
789
|
+
? turn.items.map((item) => item.id === trailingItem.id
|
|
790
|
+
? { ...item, status: 'COMPLETED', completedAt: event.createdAt }
|
|
791
|
+
: item)
|
|
792
|
+
: turn.items;
|
|
793
|
+
const existing = orderedItems.find((item) => item.id === `${event.runId}:${projected.itemId}`);
|
|
783
794
|
const status = projected.status;
|
|
784
795
|
const item = {
|
|
785
796
|
id: `${event.runId}:${projected.itemId}`,
|
|
@@ -787,7 +798,7 @@ export class NodeRuntimeState {
|
|
|
787
798
|
turnId: turn.id,
|
|
788
799
|
runId: event.runId,
|
|
789
800
|
parentItemId: null,
|
|
790
|
-
sourceSequence: event.sequence,
|
|
801
|
+
sourceSequence: existing?.sourceSequence ?? event.sequence,
|
|
791
802
|
revision: (existing?.revision ?? -1) + 1,
|
|
792
803
|
kind: projected.kind,
|
|
793
804
|
status,
|
|
@@ -800,8 +811,8 @@ export class NodeRuntimeState {
|
|
|
800
811
|
status: terminal(run.status) ? run.status : 'RUNNING',
|
|
801
812
|
startedAt: turn.startedAt ?? event.createdAt,
|
|
802
813
|
items: existing === undefined
|
|
803
|
-
? [...
|
|
804
|
-
:
|
|
814
|
+
? [...orderedItems, item]
|
|
815
|
+
: orderedItems.map((value) => (value.id === item.id ? item : value))
|
|
805
816
|
};
|
|
806
817
|
}
|
|
807
818
|
if (event.eventType === 'run.started' || event.eventType === 'run.running')
|
|
@@ -874,6 +885,7 @@ function projectableConversationKind(value) {
|
|
|
874
885
|
value === 'plan' ||
|
|
875
886
|
value === 'file_change' ||
|
|
876
887
|
value === 'context_compaction' ||
|
|
888
|
+
value === 'unknown' ||
|
|
877
889
|
value === 'warning' ||
|
|
878
890
|
value === 'error');
|
|
879
891
|
}
|
|
@@ -955,4 +967,3 @@ function decodeCursor(cursor, scope) {
|
|
|
955
967
|
return 0;
|
|
956
968
|
}
|
|
957
969
|
}
|
|
958
|
-
//# sourceMappingURL=runtime-state.js.map
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { NodeRuntimeState } from '../runtime-state.js';
|
|
2
2
|
import type { RunnerRegistry } from '../runner/runner-registry.js';
|
|
3
|
+
import type { NodeAgentSession } from '../database.js';
|
|
3
4
|
/** Owns managed native-session identity and best-effort native title refresh. */
|
|
4
5
|
export declare class ManagedRunnerSessionService {
|
|
5
6
|
private readonly runtime;
|
|
6
7
|
private readonly runners;
|
|
7
|
-
|
|
8
|
+
private readonly emitSession;
|
|
9
|
+
constructor(runtime: NodeRuntimeState, runners: RunnerRegistry, emitSession: (session: NodeAgentSession) => void);
|
|
8
10
|
remember(nativeSessionId: string, sessionId: string): void;
|
|
9
11
|
refreshTitle(sessionId: string, externalSessionId: string, cwd: string): Promise<void>;
|
|
10
12
|
}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
export class ManagedRunnerSessionService {
|
|
3
3
|
runtime;
|
|
4
4
|
runners;
|
|
5
|
-
|
|
5
|
+
emitSession;
|
|
6
|
+
constructor(runtime, runners, emitSession) {
|
|
6
7
|
this.runtime = runtime;
|
|
7
8
|
this.runners = runners;
|
|
9
|
+
this.emitSession = emitSession;
|
|
8
10
|
}
|
|
9
11
|
remember(nativeSessionId, sessionId) {
|
|
10
12
|
const session = this.runtime.getAgentSession(sessionId);
|
|
@@ -20,12 +22,14 @@ export class ManagedRunnerSessionService {
|
|
|
20
22
|
const title = await this.runners
|
|
21
23
|
.require(session.runner)
|
|
22
24
|
.readManagedRunnerTitle({ ...session, cwd }, externalSessionId);
|
|
23
|
-
if (title
|
|
24
|
-
|
|
25
|
+
if (title === undefined)
|
|
26
|
+
return;
|
|
27
|
+
const updated = this.runtime.setRunnerTitleFromNative(session.id, title);
|
|
28
|
+
if (updated.runnerTitle !== session.runnerTitle)
|
|
29
|
+
this.emitSession(updated);
|
|
25
30
|
}
|
|
26
31
|
catch {
|
|
27
32
|
// Native history is a best-effort source and cannot alter the completed Run result.
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
|
-
//# sourceMappingURL=managed-runner-session-service.js.map
|