@myagentroam/node 0.1.3 → 0.1.6
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 +4 -16
- package/dist/claude-agent-sdk.js +18 -79
- package/dist/claude-channel.js +0 -1
- package/dist/codex-app-server.d.ts +5 -0
- package/dist/codex-app-server.js +26 -5
- package/dist/config.d.ts +3 -0
- package/dist/config.js +18 -2
- package/dist/connector/node-connector-options.d.ts +26 -0
- package/dist/connector/node-connector-options.js +1 -0
- package/dist/connector/node-operation-router.d.ts +9 -0
- package/dist/connector/node-operation-router.js +22 -0
- package/dist/connector.d.ts +62 -356
- package/dist/connector.js +714 -6176
- package/dist/control-outbound-scheduler.d.ts +26 -0
- package/dist/control-outbound-scheduler.js +121 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.js +15 -10
- 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.d.ts +14 -0
- package/dist/native-jsonl-reader.js +97 -0
- package/dist/native-session-history.d.ts +41 -4
- package/dist/native-session-history.js +340 -159
- 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 +153 -0
- package/dist/runner/abstract-runner.js +154 -0
- package/dist/runner/claude/managed-run-controller.d.ts +52 -0
- package/dist/runner/claude/managed-run-controller.js +326 -0
- package/dist/runner/claude-code-runner.d.ts +70 -0
- package/dist/runner/claude-code-runner.js +286 -0
- package/dist/runner/codex/conversation-parser.d.ts +59 -0
- package/dist/runner/codex/conversation-parser.js +816 -0
- package/dist/runner/codex/managed-run-controller.d.ts +80 -0
- package/dist/runner/codex/managed-run-controller.js +388 -0
- package/dist/runner/codex-runner.d.ts +85 -0
- package/dist/runner/codex-runner.js +365 -0
- package/dist/runner/fake-test-runner.d.ts +26 -0
- package/dist/runner/fake-test-runner.js +47 -0
- 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 +19 -0
- package/dist/runner/runner-registry.js +75 -0
- 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.d.ts +2 -2
- package/dist/runner-usage.js +3 -6
- package/dist/runtime-state.js +26 -14
- package/dist/service/attachment-domain-state.d.ts +5 -0
- package/dist/service/attachment-domain-state.js +5 -0
- package/dist/service/conversation-history-service.d.ts +34 -0
- package/dist/service/conversation-history-service.js +158 -0
- package/dist/service/external-session-resume-service.d.ts +32 -0
- package/dist/service/external-session-resume-service.js +98 -0
- package/dist/service/fake-managed-run-service.d.ts +29 -0
- package/dist/service/fake-managed-run-service.js +62 -0
- package/dist/service/managed-runner-session-service.d.ts +12 -0
- package/dist/service/managed-runner-session-service.js +35 -0
- package/dist/service/native-session-projection-service.d.ts +51 -0
- package/dist/service/native-session-projection-service.js +271 -0
- package/dist/service/native-session-watch-service.d.ts +37 -0
- package/dist/service/native-session-watch-service.js +150 -0
- package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
- package/dist/service/node-connection-lifecycle-service.js +85 -0
- package/dist/service/node-control-channel.d.ts +32 -0
- package/dist/service/node-control-channel.js +85 -0
- package/dist/service/node-control-message-service.d.ts +28 -0
- package/dist/service/node-control-message-service.js +104 -0
- package/dist/service/node-request-service.d.ts +14 -0
- package/dist/service/node-request-service.js +50 -0
- package/dist/service/node-run-event-bridge.d.ts +40 -0
- package/dist/service/node-run-event-bridge.js +83 -0
- package/dist/service/node-terminal-channel.d.ts +29 -0
- package/dist/service/node-terminal-channel.js +182 -0
- 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.d.ts +32 -0
- package/dist/service/node-workspace-coordinator.js +80 -0
- package/dist/service/run-attachment-service.d.ts +37 -0
- package/dist/service/run-attachment-service.js +160 -0
- package/dist/service/run-domain-state.d.ts +4 -0
- package/dist/service/run-domain-state.js +4 -0
- package/dist/service/run-event-service.d.ts +31 -0
- package/dist/service/run-event-service.js +102 -0
- package/dist/service/run-workbench-service.d.ts +27 -0
- package/dist/service/run-workbench-service.js +113 -0
- package/dist/service/runner-channel-message-service.d.ts +13 -0
- package/dist/service/runner-channel-message-service.js +20 -0
- package/dist/service/runner-interaction-service.d.ts +40 -0
- package/dist/service/runner-interaction-service.js +246 -0
- package/dist/service/runner-service.d.ts +44 -0
- package/dist/service/runner-service.js +68 -0
- package/dist/service/session-catalog-service.d.ts +45 -0
- package/dist/service/session-catalog-service.js +224 -0
- package/dist/service/session-command-service.d.ts +23 -0
- package/dist/service/session-command-service.js +69 -0
- package/dist/service/session-context-service.d.ts +34 -0
- package/dist/service/session-context-service.js +87 -0
- package/dist/service/session-domain-state.d.ts +8 -0
- package/dist/service/session-domain-state.js +8 -0
- package/dist/service/session-identity-service.d.ts +24 -0
- package/dist/service/session-identity-service.js +86 -0
- package/dist/service/session-lifecycle-service.d.ts +31 -0
- package/dist/service/session-lifecycle-service.js +242 -0
- package/dist/service/session-message-service.d.ts +33 -0
- package/dist/service/session-message-service.js +157 -0
- package/dist/service/session-presentation-service.d.ts +33 -0
- package/dist/service/session-presentation-service.js +36 -0
- package/dist/service/session-query-service.d.ts +31 -0
- package/dist/service/session-query-service.js +77 -0
- package/dist/service/terminal-relay-state.d.ts +6 -0
- package/dist/service/terminal-relay-state.js +3 -0
- package/dist/service/terminal-service.d.ts +12 -0
- package/dist/service/terminal-service.js +52 -0
- package/dist/service/workbench-event-service.d.ts +21 -0
- package/dist/service/workbench-event-service.js +53 -0
- package/dist/service/workbench-manifest-service.d.ts +89 -0
- package/dist/service/workbench-manifest-service.js +70 -0
- package/dist/service/workspace-change-service.d.ts +9 -0
- package/dist/service/workspace-change-service.js +86 -0
- 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.d.ts +75 -0
- package/dist/service/workspace-domain-state.js +40 -0
- package/dist/service/workspace-file-service.d.ts +17 -0
- package/dist/service/workspace-file-service.js +109 -0
- package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
- package/dist/service/workspace-queue-workbench-service.js +226 -0
- package/dist/service/workspace-service.d.ts +15 -0
- package/dist/service/workspace-service.js +67 -0
- package/dist/service/workspace-session-service.d.ts +23 -0
- package/dist/service/workspace-session-service.js +139 -0
- package/dist/service/workspace-upload-service.d.ts +37 -0
- package/dist/service/workspace-upload-service.js +240 -0
- package/dist/service/workspace-watch-service.d.ts +18 -0
- package/dist/service/workspace-watch-service.js +120 -0
- package/dist/service/workspace-workbench-service.d.ts +47 -0
- package/dist/service/workspace-workbench-service.js +223 -0
- 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.d.ts +1 -0
- package/dist/terminal.js +1 -2
- package/dist/util/node-operation-parsers.d.ts +92 -0
- package/dist/util/node-operation-parsers.js +382 -0
- package/dist/util/runner-native-session-parsers.d.ts +72 -0
- package/dist/util/runner-native-session-parsers.js +381 -0
- package/dist/util/secret-environment.d.ts +1 -0
- package/dist/util/secret-environment.js +25 -0
- 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.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-session-history.js.map +0 -1
- package/dist/operational.js.map +0 -1
- package/dist/process-tree.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.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/terminal.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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AbstractRunner, type RegisteredRunnerName, type RunnerApprovalDecision, type RunnerCancellationIntent } from './abstract-runner.js';
|
|
2
|
+
import type { NodeCapabilities, RunnerDefaultConfiguration, NodeEnvelope, RunnerName, RunnerProfile } from '@myagentroam/protocol';
|
|
3
|
+
export declare class RunnerRegistry {
|
|
4
|
+
private readonly runners;
|
|
5
|
+
constructor(runners?: readonly AbstractRunner[]);
|
|
6
|
+
register<T extends AbstractRunner>(runner: T): T;
|
|
7
|
+
optional<T extends AbstractRunner>(name: RegisteredRunnerName): T | undefined;
|
|
8
|
+
require<T extends AbstractRunner>(name: RegisteredRunnerName): T;
|
|
9
|
+
advertised(): readonly AbstractRunner[];
|
|
10
|
+
product(name: unknown): AbstractRunner<RunnerName> | undefined;
|
|
11
|
+
profiles(capabilities: NodeCapabilities): readonly RunnerProfile[];
|
|
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;
|
|
14
|
+
startManagedRun(envelope: NodeEnvelope): boolean;
|
|
15
|
+
cancelManagedRun(runId: string, intent: RunnerCancellationIntent): boolean;
|
|
16
|
+
decideApproval(input: RunnerApprovalDecision): boolean;
|
|
17
|
+
respondUserInput(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export class RunnerRegistry {
|
|
2
|
+
runners = new Map();
|
|
3
|
+
constructor(runners = []) {
|
|
4
|
+
for (const runner of runners)
|
|
5
|
+
this.register(runner);
|
|
6
|
+
}
|
|
7
|
+
register(runner) {
|
|
8
|
+
if (this.runners.has(runner.name))
|
|
9
|
+
throw new Error(`RUNNER_ALREADY_REGISTERED:${runner.name}`);
|
|
10
|
+
this.runners.set(runner.name, runner);
|
|
11
|
+
return runner;
|
|
12
|
+
}
|
|
13
|
+
optional(name) {
|
|
14
|
+
return this.runners.get(name);
|
|
15
|
+
}
|
|
16
|
+
require(name) {
|
|
17
|
+
const runner = this.optional(name);
|
|
18
|
+
if (runner === undefined)
|
|
19
|
+
throw new Error(`RUNNER_NOT_REGISTERED:${name}`);
|
|
20
|
+
return runner;
|
|
21
|
+
}
|
|
22
|
+
advertised() {
|
|
23
|
+
return [...this.runners.values()].filter((runner) => runner.advertised);
|
|
24
|
+
}
|
|
25
|
+
product(name) {
|
|
26
|
+
if (typeof name !== 'string')
|
|
27
|
+
return undefined;
|
|
28
|
+
const runner = this.runners.get(name);
|
|
29
|
+
return runner?.advertised === true ? runner : undefined;
|
|
30
|
+
}
|
|
31
|
+
profiles(capabilities) {
|
|
32
|
+
return this.advertised().flatMap((runner) => {
|
|
33
|
+
const profile = runner.profile(capabilities);
|
|
34
|
+
return profile === undefined ? [] : [profile];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
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);
|
|
43
|
+
}
|
|
44
|
+
startManagedRun(envelope) {
|
|
45
|
+
const runnerName = envelope.payload.runner;
|
|
46
|
+
const values = [...this.runners.values()];
|
|
47
|
+
const runner = values.find((candidate) => candidate.interceptsRuns && candidate.acceptsRun(runnerName)) ??
|
|
48
|
+
values.find((candidate) => candidate.acceptsRun(runnerName));
|
|
49
|
+
if (runner === undefined)
|
|
50
|
+
return false;
|
|
51
|
+
runner.startManagedRun(envelope);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
cancelManagedRun(runId, intent) {
|
|
55
|
+
for (const runner of this.runners.values())
|
|
56
|
+
if (runner.cancelManagedRun(runId, intent))
|
|
57
|
+
return true;
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
decideApproval(input) {
|
|
61
|
+
let handled = false;
|
|
62
|
+
for (const runner of this.runners.values())
|
|
63
|
+
handled = runner.decideApproval(input) || handled;
|
|
64
|
+
return handled;
|
|
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
|
+
}
|
|
72
|
+
async stop() {
|
|
73
|
+
await Promise.allSettled([...this.runners.values()].map((runner) => runner.stop()));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -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.d.ts
CHANGED
|
@@ -24,8 +24,8 @@ export declare class RunnerUsageReader {
|
|
|
24
24
|
private readonly now;
|
|
25
25
|
private readonly cache;
|
|
26
26
|
constructor(fetcher?: FetchLike, homePath?: string, now?: () => number);
|
|
27
|
-
status(runner: RunnerName
|
|
28
|
-
read(runner: RunnerName
|
|
27
|
+
status(runner: RunnerName): Promise<RunnerUsageStatus>;
|
|
28
|
+
read(runner: RunnerName): Promise<RunnerUsage>;
|
|
29
29
|
private credentials;
|
|
30
30
|
private readCodex;
|
|
31
31
|
private readClaude;
|
package/dist/runner-usage.js
CHANGED
|
@@ -18,15 +18,13 @@ export class RunnerUsageReader {
|
|
|
18
18
|
this.homePath = homePath;
|
|
19
19
|
this.now = now;
|
|
20
20
|
}
|
|
21
|
-
async status(runner
|
|
22
|
-
if (!runnerAvailable)
|
|
23
|
-
return unavailable(runner, 'RUNNER_UNAVAILABLE');
|
|
21
|
+
async status(runner) {
|
|
24
22
|
return (await this.credentials(runner)) === undefined
|
|
25
23
|
? unavailable(runner, USAGE_OAUTH_REQUIRED)
|
|
26
24
|
: { runner, available: true, reasonCode: null };
|
|
27
25
|
}
|
|
28
|
-
async read(runner
|
|
29
|
-
const status = await this.status(runner
|
|
26
|
+
async read(runner) {
|
|
27
|
+
const status = await this.status(runner);
|
|
30
28
|
if (!status.available)
|
|
31
29
|
throw new Error(status.reasonCode ?? 'USAGE_UNAVAILABLE');
|
|
32
30
|
const cached = this.cache.get(runner);
|
|
@@ -190,4 +188,3 @@ function timestamp(value) {
|
|
|
190
188
|
const parsed = Date.parse(value);
|
|
191
189
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
192
190
|
}
|
|
193
|
-
//# sourceMappingURL=runner-usage.js.map
|