@openclaw/acpx 2026.5.2-beta.2 → 2026.5.3-beta.1
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/index.js +14 -0
- package/dist/register.runtime.js +108 -0
- package/dist/runtime-B3LWev_t.js +357 -0
- package/dist/runtime-api.js +4 -0
- package/dist/service-YhcC786_.js +665 -0
- package/dist/setup-api.js +16 -0
- package/package.json +33 -3
- package/AGENTS.md +0 -54
- package/index.test.ts +0 -119
- package/index.ts +0 -19
- package/register.runtime.ts +0 -154
- package/runtime-api.ts +0 -46
- package/setup-api.ts +0 -18
- package/src/acpx-runtime-compat.d.ts +0 -62
- package/src/claude-agent-acp-completion.test.ts +0 -129
- package/src/codex-auth-bridge.test.ts +0 -448
- package/src/codex-auth-bridge.ts +0 -385
- package/src/config-schema.ts +0 -117
- package/src/config.test.ts +0 -144
- package/src/config.ts +0 -273
- package/src/manifest.test.ts +0 -20
- package/src/runtime-internals/mcp-command-line.test.ts +0 -59
- package/src/runtime-internals/mcp-proxy.test.ts +0 -114
- package/src/runtime.test.ts +0 -816
- package/src/runtime.ts +0 -613
- package/src/service.test.ts +0 -401
- package/src/service.ts +0 -278
- package/tsconfig.json +0 -16
- /package/{src/runtime-internals → dist}/error-format.mjs +0 -0
- /package/{src/runtime-internals → dist}/mcp-command-line.mjs +0 -0
- /package/{src/runtime-internals → dist}/mcp-proxy.mjs +0 -0
package/src/runtime.ts
DELETED
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
3
|
-
ACPX_BACKEND_ID,
|
|
4
|
-
AcpxRuntime as BaseAcpxRuntime,
|
|
5
|
-
createAcpRuntime,
|
|
6
|
-
createAgentRegistry,
|
|
7
|
-
createFileSessionStore,
|
|
8
|
-
decodeAcpxRuntimeHandleState,
|
|
9
|
-
encodeAcpxRuntimeHandleState,
|
|
10
|
-
type AcpAgentRegistry,
|
|
11
|
-
type AcpRuntimeDoctorReport,
|
|
12
|
-
type AcpRuntimeEvent,
|
|
13
|
-
type AcpRuntimeHandle,
|
|
14
|
-
type AcpRuntimeOptions,
|
|
15
|
-
type AcpRuntimeStatus,
|
|
16
|
-
} from "acpx/runtime";
|
|
17
|
-
import { AcpRuntimeError, type AcpRuntime } from "../runtime-api.js";
|
|
18
|
-
|
|
19
|
-
type AcpSessionStore = AcpRuntimeOptions["sessionStore"];
|
|
20
|
-
type AcpSessionRecord = Parameters<AcpSessionStore["save"]>[0];
|
|
21
|
-
type AcpLoadedSessionRecord = Awaited<ReturnType<AcpSessionStore["load"]>>;
|
|
22
|
-
|
|
23
|
-
type ResetAwareSessionStore = AcpSessionStore & {
|
|
24
|
-
markFresh: (sessionKey: string) => void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function readSessionRecordName(record: AcpSessionRecord): string {
|
|
28
|
-
if (typeof record !== "object" || record === null) {
|
|
29
|
-
return "";
|
|
30
|
-
}
|
|
31
|
-
const { name } = record as { name?: unknown };
|
|
32
|
-
return typeof name === "string" ? name.trim() : "";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function createResetAwareSessionStore(baseStore: AcpSessionStore): ResetAwareSessionStore {
|
|
36
|
-
const freshSessionKeys = new Set<string>();
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
async load(sessionId: string): Promise<AcpLoadedSessionRecord> {
|
|
40
|
-
const normalized = sessionId.trim();
|
|
41
|
-
if (normalized && freshSessionKeys.has(normalized)) {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
return await baseStore.load(sessionId);
|
|
45
|
-
},
|
|
46
|
-
async save(record: AcpSessionRecord): Promise<void> {
|
|
47
|
-
await baseStore.save(record);
|
|
48
|
-
const sessionName = readSessionRecordName(record);
|
|
49
|
-
if (sessionName) {
|
|
50
|
-
freshSessionKeys.delete(sessionName);
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
markFresh(sessionKey: string): void {
|
|
54
|
-
const normalized = sessionKey.trim();
|
|
55
|
-
if (normalized) {
|
|
56
|
-
freshSessionKeys.add(normalized);
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
|
63
|
-
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
|
64
|
-
const CODEX_ACP_AGENT_ID = "codex";
|
|
65
|
-
const CODEX_ACP_OPENCLAW_PREFIX = "openai-codex/";
|
|
66
|
-
const CODEX_ACP_REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh"]);
|
|
67
|
-
const CODEX_ACP_THINKING_ALIASES = new Map<string, string | undefined>([
|
|
68
|
-
["off", undefined],
|
|
69
|
-
["minimal", "low"],
|
|
70
|
-
["low", "low"],
|
|
71
|
-
["medium", "medium"],
|
|
72
|
-
["high", "high"],
|
|
73
|
-
["x-high", "xhigh"],
|
|
74
|
-
["x_high", "xhigh"],
|
|
75
|
-
["extra-high", "xhigh"],
|
|
76
|
-
["extra_high", "xhigh"],
|
|
77
|
-
["extra high", "xhigh"],
|
|
78
|
-
["xhigh", "xhigh"],
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
type CodexAcpModelOverride = {
|
|
82
|
-
model?: string;
|
|
83
|
-
reasoningEffort?: string;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
function normalizeAgentName(value: string | undefined): string | undefined {
|
|
87
|
-
const normalized = value?.trim().toLowerCase();
|
|
88
|
-
return normalized ? normalized : undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function readAgentFromSessionKey(sessionKey: string | undefined): string | undefined {
|
|
92
|
-
const normalized = sessionKey?.trim();
|
|
93
|
-
if (!normalized) {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
const match = /^agent:(?<agent>[^:]+):/i.exec(normalized);
|
|
97
|
-
return normalizeAgentName(match?.groups?.agent);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function readAgentFromHandle(handle: AcpRuntimeHandle): string | undefined {
|
|
101
|
-
const decoded = decodeAcpxRuntimeHandleState(handle.runtimeSessionName);
|
|
102
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
103
|
-
const { agent } = decoded as { agent?: unknown };
|
|
104
|
-
if (typeof agent === "string") {
|
|
105
|
-
return normalizeAgentName(agent) ?? readAgentFromSessionKey(handle.sessionKey);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return readAgentFromSessionKey(handle.sessionKey);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function readAgentCommandFromRecord(record: AcpLoadedSessionRecord): string | undefined {
|
|
112
|
-
if (typeof record !== "object" || record === null) {
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
const { agentCommand } = record as { agentCommand?: unknown };
|
|
116
|
-
return typeof agentCommand === "string" ? agentCommand.trim() || undefined : undefined;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function splitCommandParts(value: string): string[] {
|
|
120
|
-
const parts: string[] = [];
|
|
121
|
-
let current = "";
|
|
122
|
-
let quote: "'" | '"' | null = null;
|
|
123
|
-
let escaping = false;
|
|
124
|
-
|
|
125
|
-
for (const ch of value) {
|
|
126
|
-
if (escaping) {
|
|
127
|
-
current += ch;
|
|
128
|
-
escaping = false;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (ch === "\\" && quote !== "'") {
|
|
132
|
-
escaping = true;
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (quote) {
|
|
136
|
-
if (ch === quote) {
|
|
137
|
-
quote = null;
|
|
138
|
-
} else {
|
|
139
|
-
current += ch;
|
|
140
|
-
}
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (ch === "'" || ch === '"') {
|
|
144
|
-
quote = ch;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (/\s/.test(ch)) {
|
|
148
|
-
if (current) {
|
|
149
|
-
parts.push(current);
|
|
150
|
-
current = "";
|
|
151
|
-
}
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
current += ch;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (escaping) {
|
|
158
|
-
current += "\\";
|
|
159
|
-
}
|
|
160
|
-
if (current) {
|
|
161
|
-
parts.push(current);
|
|
162
|
-
}
|
|
163
|
-
return parts;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function basename(value: string): string {
|
|
167
|
-
return value.split(/[\\/]/).pop() ?? value;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function isEnvAssignment(value: string): boolean {
|
|
171
|
-
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function unwrapEnvCommand(parts: string[]): string[] {
|
|
175
|
-
if (!parts.length || basename(parts[0]) !== "env") {
|
|
176
|
-
return parts;
|
|
177
|
-
}
|
|
178
|
-
let index = 1;
|
|
179
|
-
while (index < parts.length && isEnvAssignment(parts[index])) {
|
|
180
|
-
index += 1;
|
|
181
|
-
}
|
|
182
|
-
return parts.slice(index);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function isOpenClawBridgeCommand(command: string | undefined): boolean {
|
|
186
|
-
if (!command) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
const parts = unwrapEnvCommand(splitCommandParts(command.trim()));
|
|
190
|
-
if (basename(parts[0] ?? "") === OPENCLAW_BRIDGE_EXECUTABLE) {
|
|
191
|
-
return parts[1] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
192
|
-
}
|
|
193
|
-
if (basename(parts[0] ?? "") !== "node") {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
const scriptName = basename(parts[1] ?? "");
|
|
197
|
-
return /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) && parts[2] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function isCodexAcpPackageSpec(value: string): boolean {
|
|
201
|
-
return /^@zed-industries\/codex-acp(?:@.+)?$/i.test(value.trim());
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function isCodexAcpCommand(command: string | undefined): boolean {
|
|
205
|
-
if (!command) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
const parts = unwrapEnvCommand(splitCommandParts(command.trim()));
|
|
209
|
-
if (!parts.length) {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
if (parts.some(isCodexAcpPackageSpec)) {
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
const commandName = basename(parts[0] ?? "");
|
|
216
|
-
if (/^codex-acp(?:\.exe)?$/i.test(commandName)) {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
if (commandName !== "node") {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
const scriptName = basename(parts[1] ?? "");
|
|
223
|
-
return /^codex-acp(?:-wrapper)?(?:\.[cm]?js)?$/i.test(scriptName);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function failUnsupportedCodexAcpModel(rawModel: string, detail?: string): never {
|
|
227
|
-
throw new AcpRuntimeError(
|
|
228
|
-
"ACP_INVALID_RUNTIME_OPTION",
|
|
229
|
-
detail ??
|
|
230
|
-
`Codex ACP model "${rawModel}" is not supported. Use openai-codex/<model> or <model>/<reasoning-effort>.`,
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// acpx's `decodeAcpxRuntimeHandleState` only accepts `persistent` and `oneshot`; any other
|
|
235
|
-
// value silently round-trips through the encoded handle as `persistent` and later throws
|
|
236
|
-
// `SessionResumeRequiredError` on agent restart. Fail fast at this boundary instead.
|
|
237
|
-
// See openclaw/openclaw#73071.
|
|
238
|
-
const SUPPORTED_RUNTIME_SESSION_MODES = new Set(["persistent", "oneshot"] as const);
|
|
239
|
-
|
|
240
|
-
function assertSupportedRuntimeSessionMode(
|
|
241
|
-
mode: unknown,
|
|
242
|
-
): asserts mode is "persistent" | "oneshot" {
|
|
243
|
-
if (typeof mode === "string" && SUPPORTED_RUNTIME_SESSION_MODES.has(mode as never)) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const supported = Array.from(SUPPORTED_RUNTIME_SESSION_MODES).join(", ");
|
|
247
|
-
throw new AcpRuntimeError(
|
|
248
|
-
"ACP_INVALID_RUNTIME_OPTION",
|
|
249
|
-
`Unsupported ACP runtime session mode ${JSON.stringify(mode)}. Expected one of: ${supported}.`,
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function failUnsupportedCodexAcpThinking(rawThinking: string): never {
|
|
254
|
-
throw new AcpRuntimeError(
|
|
255
|
-
"ACP_INVALID_RUNTIME_OPTION",
|
|
256
|
-
`Codex ACP thinking level "${rawThinking}" is not supported. Use off, minimal, low, medium, high, or xhigh.`,
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function normalizeCodexAcpReasoningEffort(rawThinking: string | undefined): string | undefined {
|
|
261
|
-
const normalized = rawThinking?.trim().toLowerCase();
|
|
262
|
-
if (!normalized) {
|
|
263
|
-
return undefined;
|
|
264
|
-
}
|
|
265
|
-
if (!CODEX_ACP_THINKING_ALIASES.has(normalized)) {
|
|
266
|
-
failUnsupportedCodexAcpThinking(rawThinking ?? "");
|
|
267
|
-
}
|
|
268
|
-
return CODEX_ACP_THINKING_ALIASES.get(normalized);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function normalizeCodexAcpModelOverride(
|
|
272
|
-
rawModel: string | undefined,
|
|
273
|
-
rawThinking?: string,
|
|
274
|
-
): CodexAcpModelOverride | undefined {
|
|
275
|
-
const raw = rawModel?.trim();
|
|
276
|
-
const thinkingReasoningEffort = normalizeCodexAcpReasoningEffort(rawThinking);
|
|
277
|
-
|
|
278
|
-
if (!raw) {
|
|
279
|
-
return thinkingReasoningEffort ? { reasoningEffort: thinkingReasoningEffort } : undefined;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let value = raw;
|
|
283
|
-
if (value.toLowerCase().startsWith(CODEX_ACP_OPENCLAW_PREFIX)) {
|
|
284
|
-
value = value.slice(CODEX_ACP_OPENCLAW_PREFIX.length);
|
|
285
|
-
}
|
|
286
|
-
const parts = value.split("/");
|
|
287
|
-
if (parts.length > 2) {
|
|
288
|
-
failUnsupportedCodexAcpModel(
|
|
289
|
-
raw,
|
|
290
|
-
`Codex ACP model "${raw}" is not supported. Use openai-codex/<model> or <model>/<reasoning-effort>.`,
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
const model = (parts[0] ?? "").trim();
|
|
294
|
-
const modelReasoningEffort = normalizeCodexAcpReasoningEffort(parts[1]);
|
|
295
|
-
if (!model) {
|
|
296
|
-
failUnsupportedCodexAcpModel(
|
|
297
|
-
raw,
|
|
298
|
-
`Codex ACP model "${raw}" is not supported. Use openai-codex/<model> or <model>/<reasoning-effort>.`,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
const reasoningEffort = thinkingReasoningEffort ?? modelReasoningEffort;
|
|
302
|
-
if (reasoningEffort && !CODEX_ACP_REASONING_EFFORTS.has(reasoningEffort)) {
|
|
303
|
-
failUnsupportedCodexAcpThinking(reasoningEffort);
|
|
304
|
-
}
|
|
305
|
-
return {
|
|
306
|
-
model,
|
|
307
|
-
...(reasoningEffort ? { reasoningEffort } : {}),
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function codexAcpSessionModelId(override: CodexAcpModelOverride): string {
|
|
312
|
-
if (!override.model) {
|
|
313
|
-
return "";
|
|
314
|
-
}
|
|
315
|
-
return override.reasoningEffort
|
|
316
|
-
? `${override.model}/${override.reasoningEffort}`
|
|
317
|
-
: override.model;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function quoteShellArg(value: string): string {
|
|
321
|
-
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
|
|
322
|
-
return value;
|
|
323
|
-
}
|
|
324
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function appendCodexAcpConfigOverrides(command: string, override: CodexAcpModelOverride): string {
|
|
328
|
-
const configArgs = override.model ? [`model=${override.model}`] : [];
|
|
329
|
-
if (override.reasoningEffort) {
|
|
330
|
-
configArgs.push(`model_reasoning_effort=${override.reasoningEffort}`);
|
|
331
|
-
}
|
|
332
|
-
if (configArgs.length === 0) {
|
|
333
|
-
return command;
|
|
334
|
-
}
|
|
335
|
-
return `${command} ${configArgs.map((arg) => `-c ${quoteShellArg(arg)}`).join(" ")}`;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function createModelScopedAgentRegistry(params: {
|
|
339
|
-
agentRegistry: AcpAgentRegistry;
|
|
340
|
-
scope: AsyncLocalStorage<CodexAcpModelOverride | undefined>;
|
|
341
|
-
}): AcpAgentRegistry {
|
|
342
|
-
return {
|
|
343
|
-
resolve(agentName: string): string | undefined {
|
|
344
|
-
const command = params.agentRegistry.resolve(agentName);
|
|
345
|
-
const override = params.scope.getStore();
|
|
346
|
-
if (
|
|
347
|
-
!override ||
|
|
348
|
-
normalizeAgentName(agentName) !== CODEX_ACP_AGENT_ID ||
|
|
349
|
-
typeof command !== "string" ||
|
|
350
|
-
!isCodexAcpCommand(command)
|
|
351
|
-
) {
|
|
352
|
-
return command;
|
|
353
|
-
}
|
|
354
|
-
return appendCodexAcpConfigOverrides(command, override);
|
|
355
|
-
},
|
|
356
|
-
list(): string[] {
|
|
357
|
-
return params.agentRegistry.list();
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function resolveAgentCommand(params: {
|
|
363
|
-
agentName: string | undefined;
|
|
364
|
-
agentRegistry: AcpAgentRegistry;
|
|
365
|
-
}): string | undefined {
|
|
366
|
-
const normalizedAgentName = normalizeAgentName(params.agentName);
|
|
367
|
-
if (!normalizedAgentName) {
|
|
368
|
-
return undefined;
|
|
369
|
-
}
|
|
370
|
-
const resolvedCommand = params.agentRegistry.resolve(normalizedAgentName);
|
|
371
|
-
return typeof resolvedCommand === "string" ? resolvedCommand.trim() || undefined : undefined;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function resolveProbeAgentName(options: AcpRuntimeOptions): string {
|
|
375
|
-
const { probeAgent } = options as { probeAgent?: unknown };
|
|
376
|
-
return normalizeAgentName(typeof probeAgent === "string" ? probeAgent : undefined) ?? "codex";
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function resolveAgentCommandForName(params: {
|
|
380
|
-
agentName: string | undefined;
|
|
381
|
-
agentRegistry: AcpAgentRegistry;
|
|
382
|
-
}): string | undefined {
|
|
383
|
-
return resolveAgentCommand(params);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function shouldUseBridgeSafeDelegateForCommand(command: string | undefined): boolean {
|
|
387
|
-
return isOpenClawBridgeCommand(command);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function shouldUseDistinctBridgeDelegate(options: AcpRuntimeOptions): boolean {
|
|
391
|
-
const { mcpServers } = options as { mcpServers?: unknown };
|
|
392
|
-
return Array.isArray(mcpServers) && mcpServers.length > 0;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export class AcpxRuntime implements AcpRuntime {
|
|
396
|
-
private readonly sessionStore: ResetAwareSessionStore;
|
|
397
|
-
private readonly agentRegistry: AcpAgentRegistry;
|
|
398
|
-
private readonly scopedAgentRegistry: AcpAgentRegistry;
|
|
399
|
-
private readonly codexAcpModelOverrideScope = new AsyncLocalStorage<
|
|
400
|
-
CodexAcpModelOverride | undefined
|
|
401
|
-
>();
|
|
402
|
-
private readonly delegate: BaseAcpxRuntime;
|
|
403
|
-
private readonly bridgeSafeDelegate: BaseAcpxRuntime;
|
|
404
|
-
private readonly probeDelegate: BaseAcpxRuntime;
|
|
405
|
-
|
|
406
|
-
constructor(
|
|
407
|
-
options: AcpRuntimeOptions,
|
|
408
|
-
testOptions?: ConstructorParameters<typeof BaseAcpxRuntime>[1],
|
|
409
|
-
) {
|
|
410
|
-
this.sessionStore = createResetAwareSessionStore(options.sessionStore);
|
|
411
|
-
this.agentRegistry = options.agentRegistry;
|
|
412
|
-
this.scopedAgentRegistry = createModelScopedAgentRegistry({
|
|
413
|
-
agentRegistry: this.agentRegistry,
|
|
414
|
-
scope: this.codexAcpModelOverrideScope,
|
|
415
|
-
});
|
|
416
|
-
const sharedOptions = {
|
|
417
|
-
...options,
|
|
418
|
-
sessionStore: this.sessionStore,
|
|
419
|
-
agentRegistry: this.scopedAgentRegistry,
|
|
420
|
-
};
|
|
421
|
-
this.delegate = new BaseAcpxRuntime(sharedOptions, testOptions);
|
|
422
|
-
this.bridgeSafeDelegate = shouldUseDistinctBridgeDelegate(options)
|
|
423
|
-
? new BaseAcpxRuntime(
|
|
424
|
-
{
|
|
425
|
-
...sharedOptions,
|
|
426
|
-
mcpServers: [],
|
|
427
|
-
},
|
|
428
|
-
testOptions,
|
|
429
|
-
)
|
|
430
|
-
: this.delegate;
|
|
431
|
-
this.probeDelegate = this.resolveDelegateForAgent(resolveProbeAgentName(options));
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
private resolveDelegateForAgent(agentName: string | undefined): BaseAcpxRuntime {
|
|
435
|
-
const command = resolveAgentCommandForName({
|
|
436
|
-
agentName,
|
|
437
|
-
agentRegistry: this.agentRegistry,
|
|
438
|
-
});
|
|
439
|
-
return this.resolveDelegateForCommand(command);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
private resolveDelegateForCommand(command: string | undefined): BaseAcpxRuntime {
|
|
443
|
-
return shouldUseBridgeSafeDelegateForCommand(command) ? this.bridgeSafeDelegate : this.delegate;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private async resolveDelegateForHandle(handle: AcpRuntimeHandle): Promise<BaseAcpxRuntime> {
|
|
447
|
-
const record = await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey);
|
|
448
|
-
const recordCommand = readAgentCommandFromRecord(record);
|
|
449
|
-
if (recordCommand) {
|
|
450
|
-
return this.resolveDelegateForCommand(recordCommand);
|
|
451
|
-
}
|
|
452
|
-
return this.resolveDelegateForAgent(readAgentFromHandle(handle));
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
private async resolveCommandForHandle(handle: AcpRuntimeHandle): Promise<string | undefined> {
|
|
456
|
-
const record = await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey);
|
|
457
|
-
const recordCommand = readAgentCommandFromRecord(record);
|
|
458
|
-
if (recordCommand) {
|
|
459
|
-
return recordCommand;
|
|
460
|
-
}
|
|
461
|
-
return resolveAgentCommandForName({
|
|
462
|
-
agentName: readAgentFromHandle(handle),
|
|
463
|
-
agentRegistry: this.agentRegistry,
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
isHealthy(): boolean {
|
|
468
|
-
return this.probeDelegate.isHealthy();
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
probeAvailability(): Promise<void> {
|
|
472
|
-
return this.probeDelegate.probeAvailability();
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
doctor(): Promise<AcpRuntimeDoctorReport> {
|
|
476
|
-
return this.probeDelegate.doctor();
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async ensureSession(
|
|
480
|
-
input: Parameters<AcpRuntime["ensureSession"]>[0],
|
|
481
|
-
): Promise<AcpRuntimeHandle> {
|
|
482
|
-
assertSupportedRuntimeSessionMode(input.mode);
|
|
483
|
-
const command = resolveAgentCommandForName({
|
|
484
|
-
agentName: input.agent,
|
|
485
|
-
agentRegistry: this.agentRegistry,
|
|
486
|
-
});
|
|
487
|
-
const delegate = this.resolveDelegateForCommand(command);
|
|
488
|
-
const codexModelOverride =
|
|
489
|
-
normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command)
|
|
490
|
-
? normalizeCodexAcpModelOverride(input.model, input.thinking)
|
|
491
|
-
: undefined;
|
|
492
|
-
|
|
493
|
-
if (!codexModelOverride) {
|
|
494
|
-
return delegate.ensureSession(input);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const normalizedInput = {
|
|
498
|
-
...input,
|
|
499
|
-
...(codexAcpSessionModelId(codexModelOverride)
|
|
500
|
-
? { model: codexAcpSessionModelId(codexModelOverride) }
|
|
501
|
-
: {}),
|
|
502
|
-
};
|
|
503
|
-
return this.codexAcpModelOverrideScope.run(codexModelOverride, () =>
|
|
504
|
-
delegate.ensureSession(normalizedInput),
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async *runTurn(input: Parameters<AcpRuntime["runTurn"]>[0]): AsyncIterable<AcpRuntimeEvent> {
|
|
509
|
-
yield* (await this.resolveDelegateForHandle(input.handle)).runTurn(input);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
getCapabilities(): ReturnType<BaseAcpxRuntime["getCapabilities"]> {
|
|
513
|
-
return this.delegate.getCapabilities();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
async getStatus(
|
|
517
|
-
input: Parameters<NonNullable<AcpRuntime["getStatus"]>>[0],
|
|
518
|
-
): Promise<AcpRuntimeStatus> {
|
|
519
|
-
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
520
|
-
return delegate.getStatus(input);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
async setMode(input: Parameters<NonNullable<AcpRuntime["setMode"]>>[0]): Promise<void> {
|
|
524
|
-
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
525
|
-
await delegate.setMode(input);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async setConfigOption(
|
|
529
|
-
input: Parameters<NonNullable<AcpRuntime["setConfigOption"]>>[0],
|
|
530
|
-
): Promise<void> {
|
|
531
|
-
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
532
|
-
const command = await this.resolveCommandForHandle(input.handle);
|
|
533
|
-
const key = input.key.trim().toLowerCase();
|
|
534
|
-
if (isCodexAcpCommand(command)) {
|
|
535
|
-
if (key === "timeout" || key === "timeout_seconds") {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
if (
|
|
539
|
-
key === "model" ||
|
|
540
|
-
key === "thinking" ||
|
|
541
|
-
key === "thought_level" ||
|
|
542
|
-
key === "reasoning_effort"
|
|
543
|
-
) {
|
|
544
|
-
const override =
|
|
545
|
-
key === "model"
|
|
546
|
-
? normalizeCodexAcpModelOverride(input.value)
|
|
547
|
-
: normalizeCodexAcpModelOverride(undefined, input.value);
|
|
548
|
-
if (!override && key !== "model") {
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
if (override) {
|
|
552
|
-
if (override.model) {
|
|
553
|
-
await delegate.setConfigOption({
|
|
554
|
-
...input,
|
|
555
|
-
key: "model",
|
|
556
|
-
value: override.model,
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
if (override.reasoningEffort) {
|
|
560
|
-
await delegate.setConfigOption({
|
|
561
|
-
...input,
|
|
562
|
-
key: "reasoning_effort",
|
|
563
|
-
value: override.reasoningEffort,
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
await delegate.setConfigOption(input);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
async cancel(input: Parameters<AcpRuntime["cancel"]>[0]): Promise<void> {
|
|
574
|
-
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
575
|
-
await delegate.cancel(input);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async prepareFreshSession(input: { sessionKey: string }): Promise<void> {
|
|
579
|
-
this.sessionStore.markFresh(input.sessionKey);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async close(input: Parameters<AcpRuntime["close"]>[0]): Promise<void> {
|
|
583
|
-
await (
|
|
584
|
-
await this.resolveDelegateForHandle(input.handle)
|
|
585
|
-
).close({
|
|
586
|
-
handle: input.handle,
|
|
587
|
-
reason: input.reason,
|
|
588
|
-
discardPersistentState: input.discardPersistentState,
|
|
589
|
-
});
|
|
590
|
-
if (input.discardPersistentState) {
|
|
591
|
-
this.sessionStore.markFresh(input.handle.sessionKey);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
export {
|
|
597
|
-
ACPX_BACKEND_ID,
|
|
598
|
-
createAcpRuntime,
|
|
599
|
-
createAgentRegistry,
|
|
600
|
-
createFileSessionStore,
|
|
601
|
-
decodeAcpxRuntimeHandleState,
|
|
602
|
-
encodeAcpxRuntimeHandleState,
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
export const __testing = {
|
|
606
|
-
appendCodexAcpConfigOverrides,
|
|
607
|
-
assertSupportedRuntimeSessionMode,
|
|
608
|
-
codexAcpSessionModelId,
|
|
609
|
-
isCodexAcpCommand,
|
|
610
|
-
normalizeCodexAcpModelOverride,
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
export type { AcpAgentRegistry, AcpRuntimeOptions, AcpSessionRecord, AcpSessionStore };
|