remote-codex 0.11.49 → 0.11.51
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 +38 -5
- package/apps/supervisor-api/dist/index.js +2137 -422
- package/apps/supervisor-web/dist/assets/index-7LiZQ3aJ.js +22 -0
- package/apps/supervisor-web/dist/assets/{index-Dy8PgXgw.css → index-Bg8FdhS1.css} +1 -1
- package/apps/supervisor-web/dist/assets/{thread-ui-gcslNXur.js → thread-ui-BbtcUwps.js} +25 -19
- package/apps/supervisor-web/dist/index.html +3 -3
- package/bin/remote-codex.mjs +4 -2
- package/package.json +7 -1
- package/packages/acp/src/agent-catalog.test.ts +25 -0
- package/packages/acp/src/agent-catalog.ts +3 -1
- package/packages/acp/src/capabilities.ts +139 -0
- package/packages/acp/src/capability-parity.test.ts +99 -0
- package/packages/acp/src/catalog-runtime.test.ts +200 -6
- package/packages/acp/src/catalog-runtime.ts +239 -31
- package/packages/acp/src/extension-registry.test.ts +198 -0
- package/packages/acp/src/extension-registry.ts +285 -0
- package/packages/acp/src/extensions.test.ts +45 -0
- package/packages/acp/src/extensions.ts +103 -0
- package/packages/acp/src/harness-contract.test.ts +81 -0
- package/packages/acp/src/harness-contract.ts +62 -0
- package/packages/acp/src/index.ts +7 -0
- package/packages/acp/src/item-mapper.ts +28 -2
- package/packages/acp/src/prompt-content.test.ts +90 -0
- package/packages/acp/src/prompt-content.ts +99 -0
- package/packages/acp/src/runtimeAdapter.test.ts +458 -5
- package/packages/acp/src/runtimeAdapter.ts +791 -60
- package/packages/acp/src/session-hydrator.test.ts +135 -0
- package/packages/acp/src/session-hydrator.ts +147 -0
- package/packages/acp/src/terminal-service.test.ts +21 -2
- package/packages/acp/src/terminal-service.ts +23 -3
- package/packages/acp/src/test/fixtures/fake-acp-agent.mjs +514 -0
- package/packages/acp/src/workspace-boundary.test.ts +32 -0
- package/packages/acp/src/workspace-boundary.ts +47 -0
- package/packages/agent-runtime/src/types.ts +61 -1
- package/packages/db/src/repositories.ts +3 -2
- package/packages/shared/src/index.ts +32 -1
- package/scripts/service-manager.mjs +13 -0
- package/apps/supervisor-web/dist/assets/index-DZI1aSXo.js +0 -22
|
@@ -5,15 +5,15 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
import
|
|
8
|
+
import fs33 from "fs";
|
|
9
9
|
|
|
10
10
|
// src/app.ts
|
|
11
11
|
import Fastify from "fastify";
|
|
12
12
|
import multipart from "@fastify/multipart";
|
|
13
13
|
import websocket from "@fastify/websocket";
|
|
14
14
|
import { spawn } from "child_process";
|
|
15
|
-
import
|
|
16
|
-
import
|
|
15
|
+
import fs31 from "fs";
|
|
16
|
+
import path33 from "path";
|
|
17
17
|
import { ZodError } from "zod";
|
|
18
18
|
|
|
19
19
|
// ../../packages/config/src/index.ts
|
|
@@ -176,7 +176,7 @@ function loadRuntimeConfig(env = process.env, platform = process.platform) {
|
|
|
176
176
|
nodeEnv === "production"
|
|
177
177
|
);
|
|
178
178
|
const enabledProviders = new Set(
|
|
179
|
-
(parsed.REMOTE_CODEX_ENABLED_AGENT_PROVIDERS ?? (platform === "win32" ? "codex" : "codex,claude,opencode")).split(",").map((provider2) => provider2.trim().toLowerCase()).filter(Boolean)
|
|
179
|
+
(parsed.REMOTE_CODEX_ENABLED_AGENT_PROVIDERS ?? (platform === "win32" ? "codex,acp" : "codex,claude,opencode,acp")).split(",").map((provider2) => provider2.trim().toLowerCase()).filter(Boolean)
|
|
180
180
|
);
|
|
181
181
|
const defaultAgentHomeRoot = os.homedir();
|
|
182
182
|
const codexHome = parsed.CODEX_HOME?.trim() ? path.resolve(parsed.CODEX_HOME) : path.join(defaultAgentHomeRoot, agentBackendMetadata.codex.defaultHomeDir);
|
|
@@ -2336,7 +2336,7 @@ Subquery.prototype.getSQL = function() {
|
|
|
2336
2336
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
2337
2337
|
const nullifyMap = {};
|
|
2338
2338
|
const result = columns.reduce(
|
|
2339
|
-
(result2, { path:
|
|
2339
|
+
(result2, { path: path35, field }, columnIndex) => {
|
|
2340
2340
|
let decoder;
|
|
2341
2341
|
if (is(field, Column)) {
|
|
2342
2342
|
decoder = field;
|
|
@@ -2346,8 +2346,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
2346
2346
|
decoder = field.sql.decoder;
|
|
2347
2347
|
}
|
|
2348
2348
|
let node = result2;
|
|
2349
|
-
for (const [pathChunkIndex, pathChunk] of
|
|
2350
|
-
if (pathChunkIndex <
|
|
2349
|
+
for (const [pathChunkIndex, pathChunk] of path35.entries()) {
|
|
2350
|
+
if (pathChunkIndex < path35.length - 1) {
|
|
2351
2351
|
if (!(pathChunk in node)) {
|
|
2352
2352
|
node[pathChunk] = {};
|
|
2353
2353
|
}
|
|
@@ -2355,8 +2355,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
2355
2355
|
} else {
|
|
2356
2356
|
const rawValue = row[columnIndex];
|
|
2357
2357
|
const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
|
|
2358
|
-
if (joinsNotNullableMap && is(field, Column) &&
|
|
2359
|
-
const objectName =
|
|
2358
|
+
if (joinsNotNullableMap && is(field, Column) && path35.length === 2) {
|
|
2359
|
+
const objectName = path35[0];
|
|
2360
2360
|
if (!(objectName in nullifyMap)) {
|
|
2361
2361
|
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
2362
2362
|
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
@@ -6846,7 +6846,7 @@ function upsertThreadTurnMetadata(db, input) {
|
|
|
6846
6846
|
pricingTierKey: input.pricingTierKey ?? null,
|
|
6847
6847
|
tokenUsageJson: input.tokenUsageJson ?? null,
|
|
6848
6848
|
displayPrompt: input.displayPrompt ?? null,
|
|
6849
|
-
createdAt: now,
|
|
6849
|
+
createdAt: input.createdAt ?? now,
|
|
6850
6850
|
updatedAt: now
|
|
6851
6851
|
}).run();
|
|
6852
6852
|
}
|
|
@@ -9139,7 +9139,7 @@ function extractFileChangeEntries(item) {
|
|
|
9139
9139
|
isRecord4(entry.summary) ? entry.summary : null,
|
|
9140
9140
|
isRecord4(entry.diff) ? entry.diff : null
|
|
9141
9141
|
].filter((candidate) => Boolean(candidate));
|
|
9142
|
-
const
|
|
9142
|
+
const path35 = uniqueStrings([
|
|
9143
9143
|
stringOrNull(valueFromRecords(nestedRecords, ["path", "filePath", "targetPath"])),
|
|
9144
9144
|
stringOrNull(
|
|
9145
9145
|
valueFromRecords(nestedRecords, [
|
|
@@ -9186,7 +9186,7 @@ function extractFileChangeEntries(item) {
|
|
|
9186
9186
|
const diffStats = explicitAdditions === 0 && explicitDeletions === 0 && diffText ? countUnifiedDiffStats(diffText) : null;
|
|
9187
9187
|
const additions = explicitAdditions || diffStats?.additions || 0;
|
|
9188
9188
|
const deletions = explicitDeletions || diffStats?.deletions || 0;
|
|
9189
|
-
const normalizedPath =
|
|
9189
|
+
const normalizedPath = path35 ?? (diffText ? projectRelativePathLabel(extractPathFromDiffText(diffText)) : null);
|
|
9190
9190
|
if (!normalizedPath && additions === 0 && deletions === 0) {
|
|
9191
9191
|
return null;
|
|
9192
9192
|
}
|
|
@@ -14187,14 +14187,14 @@ function displayPath(pathValue, options) {
|
|
|
14187
14187
|
}
|
|
14188
14188
|
return relativePath.split(path12.sep).join("/");
|
|
14189
14189
|
}
|
|
14190
|
-
function toolIsLowInformationPatch(normalized, state, input, patchText,
|
|
14190
|
+
function toolIsLowInformationPatch(normalized, state, input, patchText, path35, metadataStats) {
|
|
14191
14191
|
if (normalized !== "applypatch" && normalized !== "patch") {
|
|
14192
14192
|
return false;
|
|
14193
14193
|
}
|
|
14194
14194
|
if (toolStateStatus(state) !== "running") {
|
|
14195
14195
|
return false;
|
|
14196
14196
|
}
|
|
14197
|
-
if (
|
|
14197
|
+
if (path35 || patchText || metadataStats || stringValue2(state.output)) {
|
|
14198
14198
|
return false;
|
|
14199
14199
|
}
|
|
14200
14200
|
return !isRecord9(input) || Object.keys(input).length === 0;
|
|
@@ -14237,7 +14237,7 @@ function fileChangeStatsFromMetadata(metadata) {
|
|
|
14237
14237
|
if (files.length === 0) {
|
|
14238
14238
|
return null;
|
|
14239
14239
|
}
|
|
14240
|
-
const paths = files.map((file) => stringValue2(file.filePath) ?? stringValue2(file.path) ?? stringValue2(file.relativePath)).filter((
|
|
14240
|
+
const paths = files.map((file) => stringValue2(file.filePath) ?? stringValue2(file.path) ?? stringValue2(file.relativePath)).filter((path35) => Boolean(path35));
|
|
14241
14241
|
const addedLines = files.reduce((total, file) => total + (numberValue(file.additions) ?? numberValue(file.addedLines) ?? numberValue(file.added) ?? 0), 0);
|
|
14242
14242
|
const removedLines = files.reduce((total, file) => total + (numberValue(file.deletions) ?? numberValue(file.removedLines) ?? numberValue(file.removed) ?? 0), 0);
|
|
14243
14243
|
return {
|
|
@@ -14356,8 +14356,8 @@ function normalizeLegacyMessage(message) {
|
|
|
14356
14356
|
finish: stringValue2(info.finish) ?? void 0
|
|
14357
14357
|
};
|
|
14358
14358
|
}
|
|
14359
|
-
function mapAssistantTool(
|
|
14360
|
-
const id = stringValue2(tool.id) ?? `${
|
|
14359
|
+
function mapAssistantTool(messageId3, tool, options) {
|
|
14360
|
+
const id = stringValue2(tool.id) ?? `${messageId3}:tool`;
|
|
14361
14361
|
const name = stringValue2(tool.name) ?? stringValue2(tool.tool) ?? "Tool";
|
|
14362
14362
|
const state = isRecord9(tool.state) ? tool.state : {};
|
|
14363
14363
|
const input = state.input;
|
|
@@ -14385,28 +14385,28 @@ function mapAssistantTool(messageId2, tool, options) {
|
|
|
14385
14385
|
].includes(normalized)) {
|
|
14386
14386
|
const metadataStats = fileChangeStatsFromMetadata(state.metadata);
|
|
14387
14387
|
const patchText = isRecord9(input) ? stringValue2(input.patchText) ?? stringValue2(input.patch) ?? stringValue2(input.diff) : null;
|
|
14388
|
-
const
|
|
14389
|
-
if (toolIsLowInformationPatch(normalized, state, input, patchText,
|
|
14388
|
+
const path35 = metadataStats?.path ?? filePathFromInput(input) ?? extractPathFromPatchText(patchText);
|
|
14389
|
+
if (toolIsLowInformationPatch(normalized, state, input, patchText, path35, metadataStats)) {
|
|
14390
14390
|
return null;
|
|
14391
14391
|
}
|
|
14392
14392
|
const output = stringValue2(state.output);
|
|
14393
14393
|
const diffStats = countUnifiedDiffStats2(patchText);
|
|
14394
|
-
const displayFilePath = displayPath(
|
|
14394
|
+
const displayFilePath = displayPath(path35, options);
|
|
14395
14395
|
return {
|
|
14396
14396
|
id,
|
|
14397
14397
|
kind: "fileChange",
|
|
14398
14398
|
text: metadataStats ? metadataStats.changedFiles > 1 ? `${metadataStats.changedFiles} changed files` : displayFilePath ?? metadataStats.previewText : displayFilePath ?? output ?? summary ?? name,
|
|
14399
14399
|
previewText: metadataStats ? metadataStats.changedFiles > 1 ? `${metadataStats.changedFiles} changed files` : displayFilePath ?? metadataStats.previewText : displayFilePath ? `${name}: ${displayFilePath}` : output ?? summary ?? name,
|
|
14400
14400
|
detailText,
|
|
14401
|
-
changedFiles: metadataStats?.changedFiles ?? (
|
|
14401
|
+
changedFiles: metadataStats?.changedFiles ?? (path35 ? 1 : null),
|
|
14402
14402
|
addedLines: metadataStats?.addedLines ?? diffStats?.addedLines ?? null,
|
|
14403
14403
|
removedLines: metadataStats?.removedLines ?? diffStats?.removedLines ?? null,
|
|
14404
14404
|
status: toolStateStatus(state)
|
|
14405
14405
|
};
|
|
14406
14406
|
}
|
|
14407
14407
|
if (["read", "grep", "glob", "list", "ls", "bashoutput"].includes(normalized)) {
|
|
14408
|
-
const
|
|
14409
|
-
const text2 = displayPath(
|
|
14408
|
+
const path35 = filePathFromInput(input);
|
|
14409
|
+
const text2 = displayPath(path35, options) ?? summary ?? name;
|
|
14410
14410
|
return {
|
|
14411
14411
|
id,
|
|
14412
14412
|
kind: "fileRead",
|
|
@@ -14485,13 +14485,13 @@ function openCodeMessagesToPlanUpdate(messages) {
|
|
|
14485
14485
|
}
|
|
14486
14486
|
return null;
|
|
14487
14487
|
}
|
|
14488
|
-
function mapAssistantPart(
|
|
14489
|
-
const partId = stringValue2(part.id) ?? `${
|
|
14488
|
+
function mapAssistantPart(messageId3, part, index, options) {
|
|
14489
|
+
const partId = stringValue2(part.id) ?? `${messageId3}:${stringValue2(part.type) ?? "part"}:${index}`;
|
|
14490
14490
|
const partType = stringValue2(part.type);
|
|
14491
14491
|
if (partType === "text") {
|
|
14492
14492
|
const text2 = stringValue2(part.text);
|
|
14493
14493
|
return text2 ? {
|
|
14494
|
-
id: `${
|
|
14494
|
+
id: `${messageId3}:text:${index}`,
|
|
14495
14495
|
kind: "agentMessage",
|
|
14496
14496
|
text: text2
|
|
14497
14497
|
} : null;
|
|
@@ -14505,7 +14505,7 @@ function mapAssistantPart(messageId2, part, index, options) {
|
|
|
14505
14505
|
} : null;
|
|
14506
14506
|
}
|
|
14507
14507
|
if (partType === "tool") {
|
|
14508
|
-
return mapAssistantTool(
|
|
14508
|
+
return mapAssistantTool(messageId3, part, options);
|
|
14509
14509
|
}
|
|
14510
14510
|
if (partType === "file") {
|
|
14511
14511
|
const sourcePath = isRecord9(part.source) ? stringValue2(part.source.path) : null;
|
|
@@ -15935,7 +15935,8 @@ var builtinAcpAgents = [
|
|
|
15935
15935
|
baseProbeCommand: "opencode --version",
|
|
15936
15936
|
serverCommand: "opencode acp",
|
|
15937
15937
|
serverProbeCommand: "opencode acp --help",
|
|
15938
|
-
installCommand: null
|
|
15938
|
+
installCommand: null,
|
|
15939
|
+
modelListCommand: "opencode models"
|
|
15939
15940
|
},
|
|
15940
15941
|
{
|
|
15941
15942
|
id: "deepseek",
|
|
@@ -16079,7 +16080,7 @@ var AcpAgentCatalog = class {
|
|
|
16079
16080
|
${result.stderr}`;
|
|
16080
16081
|
const defaultModel = output.match(/^Default model:\s*(\S+)/m)?.[1] ?? null;
|
|
16081
16082
|
const models = output.split(/\r?\n/).flatMap((line) => {
|
|
16082
|
-
const match = line.match(/^\s*[-*]\s+(\S+?)(?:\s+\(default\))?\s*$/);
|
|
16083
|
+
const match = line.match(/^\s*[-*]\s+(\S+?)(?:\s+\(default\))?\s*$/) ?? line.match(/^\s*([A-Za-z0-9._-]+\/[A-Za-z0-9._:/-]+)\s*$/);
|
|
16083
16084
|
if (!match?.[1]) {
|
|
16084
16085
|
return [];
|
|
16085
16086
|
}
|
|
@@ -16162,13 +16163,125 @@ function acpAgentMetadata(entry) {
|
|
|
16162
16163
|
};
|
|
16163
16164
|
}
|
|
16164
16165
|
|
|
16166
|
+
// ../../packages/acp/src/extensions.ts
|
|
16167
|
+
var REMOTE_CODEX_HARNESS_EXTENSION_META_KEY = "remoteCodex.harnessExtensions";
|
|
16168
|
+
var REMOTE_CODEX_HARNESS_EXTENSION_PROTOCOL = "remote-codex.harness-extension/v1";
|
|
16169
|
+
var REMOTE_CODEX_HARNESS_EXTENSION_EVENT_METHOD = "remoteCodex/harness-extension/event";
|
|
16170
|
+
var extensionSegmentPattern = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
|
|
16171
|
+
function assertExtensionSegment(value, label) {
|
|
16172
|
+
if (!extensionSegmentPattern.test(value)) {
|
|
16173
|
+
throw new Error(`${label} must be a lowercase extension identifier.`);
|
|
16174
|
+
}
|
|
16175
|
+
}
|
|
16176
|
+
function harnessExtensionMethodName(extensionId, version2, method) {
|
|
16177
|
+
assertExtensionSegment(extensionId, "Extension id");
|
|
16178
|
+
assertExtensionSegment(method, "Extension method");
|
|
16179
|
+
if (!Number.isInteger(version2) || version2 < 1) {
|
|
16180
|
+
throw new Error("Extension version must be a positive integer.");
|
|
16181
|
+
}
|
|
16182
|
+
return `remoteCodex/${extensionId}/v${version2}/${method}`;
|
|
16183
|
+
}
|
|
16184
|
+
function createHarnessExtensionCall(input) {
|
|
16185
|
+
harnessExtensionMethodName(
|
|
16186
|
+
input.extensionId,
|
|
16187
|
+
input.extensionVersion,
|
|
16188
|
+
input.method
|
|
16189
|
+
);
|
|
16190
|
+
if (!input.operationId.trim()) {
|
|
16191
|
+
throw new Error("Extension operation id is required.");
|
|
16192
|
+
}
|
|
16193
|
+
if (!input.idempotencyKey.trim()) {
|
|
16194
|
+
throw new Error("Extension idempotency key is required.");
|
|
16195
|
+
}
|
|
16196
|
+
return {
|
|
16197
|
+
protocol: REMOTE_CODEX_HARNESS_EXTENSION_PROTOCOL,
|
|
16198
|
+
extensionId: input.extensionId,
|
|
16199
|
+
extensionVersion: input.extensionVersion,
|
|
16200
|
+
method: input.method,
|
|
16201
|
+
operationId: input.operationId,
|
|
16202
|
+
idempotencyKey: input.idempotencyKey,
|
|
16203
|
+
params: input.params
|
|
16204
|
+
};
|
|
16205
|
+
}
|
|
16206
|
+
|
|
16207
|
+
// ../../packages/acp/src/capabilities.ts
|
|
16208
|
+
function isRecord11(value) {
|
|
16209
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16210
|
+
}
|
|
16211
|
+
function stringValue4(value) {
|
|
16212
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16213
|
+
}
|
|
16214
|
+
function harnessExtensions(value) {
|
|
16215
|
+
if (!Array.isArray(value)) {
|
|
16216
|
+
return [];
|
|
16217
|
+
}
|
|
16218
|
+
return value.flatMap((candidate) => {
|
|
16219
|
+
if (!isRecord11(candidate)) {
|
|
16220
|
+
return [];
|
|
16221
|
+
}
|
|
16222
|
+
const id = stringValue4(candidate.id);
|
|
16223
|
+
const version2 = candidate.version;
|
|
16224
|
+
const stability = candidate.stability;
|
|
16225
|
+
const methods2 = Array.isArray(candidate.methods) ? candidate.methods.filter((item) => typeof item === "string") : [];
|
|
16226
|
+
const events = Array.isArray(candidate.events) ? candidate.events.filter((item) => typeof item === "string") : [];
|
|
16227
|
+
if (!id || !Number.isInteger(version2) || Number(version2) < 1 || stability !== "experimental" && stability !== "stable") {
|
|
16228
|
+
return [];
|
|
16229
|
+
}
|
|
16230
|
+
return [{
|
|
16231
|
+
id,
|
|
16232
|
+
version: Number(version2),
|
|
16233
|
+
stability,
|
|
16234
|
+
methods: methods2,
|
|
16235
|
+
events
|
|
16236
|
+
}];
|
|
16237
|
+
});
|
|
16238
|
+
}
|
|
16239
|
+
function legacyExtensions(meta) {
|
|
16240
|
+
const steering = isRecord11(meta.steering) ? { supported: meta.steering.supported === true } : null;
|
|
16241
|
+
const goal = isRecord11(meta.goal) ? {
|
|
16242
|
+
version: typeof meta.goal.version === "string" || typeof meta.goal.version === "number" ? meta.goal.version : null,
|
|
16243
|
+
controlMethod: stringValue4(meta.goal.controlMethod),
|
|
16244
|
+
actions: Array.isArray(meta.goal.actions) ? meta.goal.actions.filter((item) => typeof item === "string") : []
|
|
16245
|
+
} : null;
|
|
16246
|
+
return { steering, goal };
|
|
16247
|
+
}
|
|
16248
|
+
function snapshotAcpInitializeResponse(response) {
|
|
16249
|
+
if (!response) {
|
|
16250
|
+
return null;
|
|
16251
|
+
}
|
|
16252
|
+
const meta = isRecord11(response._meta) ? response._meta : {};
|
|
16253
|
+
return {
|
|
16254
|
+
protocolVersion: response.protocolVersion,
|
|
16255
|
+
agentInfo: response.agentInfo ? {
|
|
16256
|
+
name: response.agentInfo.name,
|
|
16257
|
+
title: response.agentInfo.title ?? null,
|
|
16258
|
+
version: response.agentInfo.version ?? null
|
|
16259
|
+
} : null,
|
|
16260
|
+
agentCapabilities: structuredClone(response.agentCapabilities ?? {}),
|
|
16261
|
+
harnessExtensions: harnessExtensions(
|
|
16262
|
+
meta[REMOTE_CODEX_HARNESS_EXTENSION_META_KEY]
|
|
16263
|
+
),
|
|
16264
|
+
legacyExtensions: legacyExtensions(meta)
|
|
16265
|
+
};
|
|
16266
|
+
}
|
|
16267
|
+
function snapshotAcpAgentCapabilities(input) {
|
|
16268
|
+
const ready = input.availability === "ready";
|
|
16269
|
+
return {
|
|
16270
|
+
provider: "acp",
|
|
16271
|
+
agentId: input.agentId,
|
|
16272
|
+
availability: input.availability,
|
|
16273
|
+
negotiated: ready ? input.negotiated ?? null : null,
|
|
16274
|
+
effectiveCapabilities: ready ? structuredClone(input.effectiveCapabilities) : null
|
|
16275
|
+
};
|
|
16276
|
+
}
|
|
16277
|
+
|
|
16165
16278
|
// ../../packages/acp/src/catalog-runtime.ts
|
|
16166
|
-
import { EventEmitter as
|
|
16279
|
+
import { EventEmitter as EventEmitter9 } from "events";
|
|
16167
16280
|
|
|
16168
16281
|
// ../../packages/acp/src/runtimeAdapter.ts
|
|
16169
|
-
import { EventEmitter as
|
|
16170
|
-
import
|
|
16171
|
-
import
|
|
16282
|
+
import { EventEmitter as EventEmitter8 } from "events";
|
|
16283
|
+
import fs13 from "fs/promises";
|
|
16284
|
+
import path16 from "path";
|
|
16172
16285
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
16173
16286
|
import { Readable, Writable } from "stream";
|
|
16174
16287
|
|
|
@@ -17919,17 +18032,17 @@ function isResponseMessage(value) {
|
|
|
17919
18032
|
function isNotificationMessage(value) {
|
|
17920
18033
|
return isJsonRpcEnvelope(value) && !("id" in value) && typeof value["method"] === "string";
|
|
17921
18034
|
}
|
|
17922
|
-
function
|
|
18035
|
+
function isRecord12(value) {
|
|
17923
18036
|
return typeof value === "object" && value !== null;
|
|
17924
18037
|
}
|
|
17925
18038
|
function isJsonRpcEnvelope(value) {
|
|
17926
|
-
return
|
|
18039
|
+
return isRecord12(value) && value["jsonrpc"] === "2.0";
|
|
17927
18040
|
}
|
|
17928
18041
|
function isJsonRpcId(value) {
|
|
17929
18042
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
17930
18043
|
}
|
|
17931
18044
|
function isResponseShapedMessage(value) {
|
|
17932
|
-
return
|
|
18045
|
+
return isRecord12(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
|
|
17933
18046
|
}
|
|
17934
18047
|
function isResponseBatch(batch) {
|
|
17935
18048
|
let hasValidCall = false;
|
|
@@ -17939,7 +18052,7 @@ function isResponseBatch(batch) {
|
|
|
17939
18052
|
for (const entry of batch) {
|
|
17940
18053
|
hasValidCall ||= isRequestMessage(entry) || isNotificationMessage(entry);
|
|
17941
18054
|
hasValidResponse ||= isResponseMessage(entry);
|
|
17942
|
-
if (!
|
|
18055
|
+
if (!isRecord12(entry)) {
|
|
17943
18056
|
continue;
|
|
17944
18057
|
}
|
|
17945
18058
|
hasCallShape ||= "method" in entry;
|
|
@@ -17954,13 +18067,13 @@ function isResponseBatch(batch) {
|
|
|
17954
18067
|
return hasResponseShape && !hasCallShape;
|
|
17955
18068
|
}
|
|
17956
18069
|
function cancelRequestId(params) {
|
|
17957
|
-
if (!
|
|
18070
|
+
if (!isRecord12(params) || !isJsonRpcId(params["requestId"])) {
|
|
17958
18071
|
return void 0;
|
|
17959
18072
|
}
|
|
17960
18073
|
return params["requestId"];
|
|
17961
18074
|
}
|
|
17962
18075
|
function isErrorResponse(value) {
|
|
17963
|
-
return
|
|
18076
|
+
return isRecord12(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
|
|
17964
18077
|
}
|
|
17965
18078
|
var Handled = {
|
|
17966
18079
|
/**
|
|
@@ -18505,7 +18618,7 @@ var Connection = class {
|
|
|
18505
18618
|
if (this.abortController.signal.aborted) {
|
|
18506
18619
|
return Promise.resolve();
|
|
18507
18620
|
}
|
|
18508
|
-
if (!
|
|
18621
|
+
if (!isRecord12(message)) {
|
|
18509
18622
|
console.error("Invalid message", { message });
|
|
18510
18623
|
return Promise.resolve();
|
|
18511
18624
|
}
|
|
@@ -18908,7 +19021,7 @@ function ndJsonStream(output, input) {
|
|
|
18908
19021
|
await writeJson(protocolErrorResponse(RequestError.parseError()));
|
|
18909
19022
|
return;
|
|
18910
19023
|
}
|
|
18911
|
-
if (
|
|
19024
|
+
if (isRecord12(message) || Array.isArray(message)) {
|
|
18912
19025
|
controller.enqueue(message);
|
|
18913
19026
|
} else {
|
|
18914
19027
|
await writeJson(protocolErrorResponse(RequestError.invalidRequest(message)));
|
|
@@ -20063,13 +20176,15 @@ function mappedPlan(entries) {
|
|
|
20063
20176
|
};
|
|
20064
20177
|
}
|
|
20065
20178
|
var AcpTurnItemMapper = class {
|
|
20066
|
-
constructor(turnId, initialItems = []) {
|
|
20179
|
+
constructor(turnId, initialItems = [], mode = "live") {
|
|
20067
20180
|
this.turnId = turnId;
|
|
20181
|
+
this.mode = mode;
|
|
20068
20182
|
for (const item of initialItems) {
|
|
20069
20183
|
this.upsert(item);
|
|
20070
20184
|
}
|
|
20071
20185
|
}
|
|
20072
20186
|
turnId;
|
|
20187
|
+
mode;
|
|
20073
20188
|
items = /* @__PURE__ */ new Map();
|
|
20074
20189
|
order = [];
|
|
20075
20190
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -20077,6 +20192,16 @@ var AcpTurnItemMapper = class {
|
|
|
20077
20192
|
thoughtIndex = 0;
|
|
20078
20193
|
currentAgentMessageId = null;
|
|
20079
20194
|
currentThoughtId = null;
|
|
20195
|
+
appendUserMessage(content, itemId) {
|
|
20196
|
+
const current = this.items.get(itemId);
|
|
20197
|
+
const item = {
|
|
20198
|
+
...current ?? { id: itemId, kind: "userMessage" },
|
|
20199
|
+
text: `${current?.text ?? ""}${acpContentBlockText(content)}`,
|
|
20200
|
+
status: modeStatus(this.mode)
|
|
20201
|
+
};
|
|
20202
|
+
this.upsert(item);
|
|
20203
|
+
return item;
|
|
20204
|
+
}
|
|
20080
20205
|
turn(status = "inProgress", error = null) {
|
|
20081
20206
|
return {
|
|
20082
20207
|
providerTurnId: this.turnId,
|
|
@@ -20099,7 +20224,7 @@ var AcpTurnItemMapper = class {
|
|
|
20099
20224
|
case "agent_message_chunk": {
|
|
20100
20225
|
result.itemUpdates.push(...this.finishThought());
|
|
20101
20226
|
const delta = acpContentBlockText(update.content);
|
|
20102
|
-
const itemId = this.currentAgentMessageId ?? `${this.turnId}:agent:${++this.agentMessageIndex}`;
|
|
20227
|
+
const itemId = this.currentAgentMessageId ?? messageId2(update) ?? `${this.turnId}:agent:${++this.agentMessageIndex}`;
|
|
20103
20228
|
this.currentAgentMessageId = itemId;
|
|
20104
20229
|
const current = this.items.get(itemId);
|
|
20105
20230
|
const item = {
|
|
@@ -20114,7 +20239,7 @@ var AcpTurnItemMapper = class {
|
|
|
20114
20239
|
case "agent_thought_chunk": {
|
|
20115
20240
|
result.itemUpdates.push(...this.finishAgentMessage());
|
|
20116
20241
|
const delta = acpContentBlockText(update.content);
|
|
20117
|
-
const itemId = this.currentThoughtId ?? `${this.turnId}:thought:${++this.thoughtIndex}`;
|
|
20242
|
+
const itemId = this.currentThoughtId ?? messageId2(update) ?? `${this.turnId}:thought:${++this.thoughtIndex}`;
|
|
20118
20243
|
this.currentThoughtId = itemId;
|
|
20119
20244
|
const current = this.items.get(itemId);
|
|
20120
20245
|
const item = {
|
|
@@ -20298,6 +20423,449 @@ var AcpTurnItemMapper = class {
|
|
|
20298
20423
|
return [...this.finishAgentMessage(), ...this.finishThought()];
|
|
20299
20424
|
}
|
|
20300
20425
|
};
|
|
20426
|
+
function messageId2(update) {
|
|
20427
|
+
return typeof update.messageId === "string" && update.messageId.trim() ? update.messageId.trim() : null;
|
|
20428
|
+
}
|
|
20429
|
+
function modeStatus(mode) {
|
|
20430
|
+
return mode === "hydrate" ? "completed" : "running";
|
|
20431
|
+
}
|
|
20432
|
+
|
|
20433
|
+
// ../../packages/acp/src/prompt-content.ts
|
|
20434
|
+
import fs12 from "fs/promises";
|
|
20435
|
+
import path15 from "path";
|
|
20436
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
20437
|
+
|
|
20438
|
+
// ../../packages/acp/src/workspace-boundary.ts
|
|
20439
|
+
import fs11 from "fs/promises";
|
|
20440
|
+
import path14 from "path";
|
|
20441
|
+
function isInside(root, candidate) {
|
|
20442
|
+
const relative = path14.relative(root, candidate);
|
|
20443
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path14.sep}`) && !path14.isAbsolute(relative);
|
|
20444
|
+
}
|
|
20445
|
+
async function resolveAcpWorkspacePath(workspacePath, candidatePath) {
|
|
20446
|
+
if (!path14.isAbsolute(candidatePath)) {
|
|
20447
|
+
throw new Error("ACP workspace paths must be absolute.");
|
|
20448
|
+
}
|
|
20449
|
+
const rootPath = path14.resolve(workspacePath);
|
|
20450
|
+
const requestedPath = path14.resolve(candidatePath);
|
|
20451
|
+
if (!isInside(rootPath, requestedPath)) {
|
|
20452
|
+
throw new Error("ACP path must stay inside the session workspace.");
|
|
20453
|
+
}
|
|
20454
|
+
const rootRealPath = await fs11.realpath(rootPath);
|
|
20455
|
+
let existingPath = requestedPath;
|
|
20456
|
+
const missingSegments = [];
|
|
20457
|
+
while (true) {
|
|
20458
|
+
try {
|
|
20459
|
+
const existingRealPath = await fs11.realpath(existingPath);
|
|
20460
|
+
if (!isInside(rootRealPath, existingRealPath)) {
|
|
20461
|
+
throw new Error("ACP path resolves outside the session workspace.");
|
|
20462
|
+
}
|
|
20463
|
+
return path14.join(existingRealPath, ...missingSegments);
|
|
20464
|
+
} catch (error) {
|
|
20465
|
+
if (error.code !== "ENOENT") {
|
|
20466
|
+
throw error;
|
|
20467
|
+
}
|
|
20468
|
+
const parent = path14.dirname(existingPath);
|
|
20469
|
+
if (parent === existingPath) {
|
|
20470
|
+
throw new Error("ACP workspace path has no existing parent.");
|
|
20471
|
+
}
|
|
20472
|
+
missingSegments.unshift(path14.basename(existingPath));
|
|
20473
|
+
existingPath = parent;
|
|
20474
|
+
}
|
|
20475
|
+
}
|
|
20476
|
+
}
|
|
20477
|
+
|
|
20478
|
+
// ../../packages/acp/src/prompt-content.ts
|
|
20479
|
+
var attachmentTokenPattern = /\[(PHOTO|FILE)\s+([^\]]+)\]/g;
|
|
20480
|
+
var maxEmbeddedImageBytes = 20 * 1024 * 1024;
|
|
20481
|
+
function imageMimeType(filePath) {
|
|
20482
|
+
switch (path15.extname(filePath).toLowerCase()) {
|
|
20483
|
+
case ".jpg":
|
|
20484
|
+
case ".jpeg":
|
|
20485
|
+
return "image/jpeg";
|
|
20486
|
+
case ".gif":
|
|
20487
|
+
return "image/gif";
|
|
20488
|
+
case ".webp":
|
|
20489
|
+
return "image/webp";
|
|
20490
|
+
case ".png":
|
|
20491
|
+
default:
|
|
20492
|
+
return "image/png";
|
|
20493
|
+
}
|
|
20494
|
+
}
|
|
20495
|
+
async function buildAcpPromptContent(input) {
|
|
20496
|
+
if (input.content) {
|
|
20497
|
+
return input.content.map((block) => {
|
|
20498
|
+
if (block.type === "image" && input.promptCapabilities?.image !== true) {
|
|
20499
|
+
throw new Error("The selected ACP agent does not support image prompts.");
|
|
20500
|
+
}
|
|
20501
|
+
if (block.type === "audio" && input.promptCapabilities?.audio !== true) {
|
|
20502
|
+
throw new Error("The selected ACP agent does not support audio prompts.");
|
|
20503
|
+
}
|
|
20504
|
+
if (block.type === "resource" && input.promptCapabilities?.embeddedContext !== true) {
|
|
20505
|
+
throw new Error("The selected ACP agent does not support embedded context.");
|
|
20506
|
+
}
|
|
20507
|
+
return structuredClone(block);
|
|
20508
|
+
});
|
|
20509
|
+
}
|
|
20510
|
+
const matches = [...input.prompt.matchAll(attachmentTokenPattern)];
|
|
20511
|
+
if (matches.length === 0) {
|
|
20512
|
+
return [{ type: "text", text: input.prompt }];
|
|
20513
|
+
}
|
|
20514
|
+
const blocks = [];
|
|
20515
|
+
let cursor = 0;
|
|
20516
|
+
for (const match of matches) {
|
|
20517
|
+
const start = match.index ?? 0;
|
|
20518
|
+
const preceding = input.prompt.slice(cursor, start);
|
|
20519
|
+
if (preceding) {
|
|
20520
|
+
blocks.push({ type: "text", text: preceding });
|
|
20521
|
+
}
|
|
20522
|
+
const kind = match[1];
|
|
20523
|
+
const requestedPath = match[2]?.trim() ?? "";
|
|
20524
|
+
const requestedAssetPath = path15.isAbsolute(requestedPath) ? requestedPath : path15.resolve(input.workspacePath, requestedPath);
|
|
20525
|
+
const assetPath = await resolveAcpWorkspacePath(
|
|
20526
|
+
input.workspacePath,
|
|
20527
|
+
requestedAssetPath
|
|
20528
|
+
);
|
|
20529
|
+
const uri = pathToFileURL3(assetPath).toString();
|
|
20530
|
+
if (kind === "PHOTO") {
|
|
20531
|
+
if (input.promptCapabilities?.image !== true) {
|
|
20532
|
+
throw new Error("The selected ACP agent does not support image prompts.");
|
|
20533
|
+
}
|
|
20534
|
+
const stat = await fs12.stat(assetPath);
|
|
20535
|
+
if (!stat.isFile() || stat.size > maxEmbeddedImageBytes) {
|
|
20536
|
+
throw new Error("ACP image attachment is missing or exceeds 20 MiB.");
|
|
20537
|
+
}
|
|
20538
|
+
blocks.push({
|
|
20539
|
+
type: "image",
|
|
20540
|
+
data: (await fs12.readFile(assetPath)).toString("base64"),
|
|
20541
|
+
mimeType: imageMimeType(assetPath),
|
|
20542
|
+
uri
|
|
20543
|
+
});
|
|
20544
|
+
} else {
|
|
20545
|
+
await fs12.access(assetPath);
|
|
20546
|
+
blocks.push({
|
|
20547
|
+
type: "resource_link",
|
|
20548
|
+
name: path15.basename(assetPath),
|
|
20549
|
+
uri
|
|
20550
|
+
});
|
|
20551
|
+
}
|
|
20552
|
+
cursor = start + match[0].length;
|
|
20553
|
+
}
|
|
20554
|
+
const trailing = input.prompt.slice(cursor);
|
|
20555
|
+
if (trailing) {
|
|
20556
|
+
blocks.push({ type: "text", text: trailing });
|
|
20557
|
+
}
|
|
20558
|
+
return blocks;
|
|
20559
|
+
}
|
|
20560
|
+
|
|
20561
|
+
// ../../packages/acp/src/extension-registry.ts
|
|
20562
|
+
import { EventEmitter as EventEmitter7 } from "events";
|
|
20563
|
+
var HarnessExtensionInvocationError = class extends Error {
|
|
20564
|
+
constructor(payload) {
|
|
20565
|
+
super(payload.message);
|
|
20566
|
+
this.payload = payload;
|
|
20567
|
+
this.name = "HarnessExtensionInvocationError";
|
|
20568
|
+
}
|
|
20569
|
+
payload;
|
|
20570
|
+
};
|
|
20571
|
+
function extensionKey(extensionId, version2) {
|
|
20572
|
+
return `${extensionId}@${version2}`;
|
|
20573
|
+
}
|
|
20574
|
+
function operationFingerprint(input) {
|
|
20575
|
+
return JSON.stringify(input);
|
|
20576
|
+
}
|
|
20577
|
+
var HarnessExtensionRegistry = class extends EventEmitter7 {
|
|
20578
|
+
extensions = /* @__PURE__ */ new Map();
|
|
20579
|
+
operations = /* @__PURE__ */ new Map();
|
|
20580
|
+
eventSequences = /* @__PURE__ */ new Set();
|
|
20581
|
+
register(input) {
|
|
20582
|
+
const key = extensionKey(input.descriptor.id, input.descriptor.version);
|
|
20583
|
+
const existing = this.extensions.get(key);
|
|
20584
|
+
if (existing && existing.ownerId !== input.ownerId) {
|
|
20585
|
+
throw new Error(
|
|
20586
|
+
`Harness extension ${key} is already owned by ${existing.ownerId}.`
|
|
20587
|
+
);
|
|
20588
|
+
}
|
|
20589
|
+
if (new Set(input.descriptor.methods).size !== input.descriptor.methods.length) {
|
|
20590
|
+
throw new Error(`Harness extension ${key} declares duplicate methods.`);
|
|
20591
|
+
}
|
|
20592
|
+
if (new Set(input.descriptor.events).size !== input.descriptor.events.length) {
|
|
20593
|
+
throw new Error(`Harness extension ${key} declares duplicate events.`);
|
|
20594
|
+
}
|
|
20595
|
+
this.extensions.set(key, {
|
|
20596
|
+
ownerId: input.ownerId,
|
|
20597
|
+
descriptor: structuredClone(input.descriptor),
|
|
20598
|
+
transport: input.transport,
|
|
20599
|
+
wireMethods: { ...input.wireMethods ?? {} },
|
|
20600
|
+
capabilityPatch: input.capabilityPatch ? structuredClone(input.capabilityPatch) : null,
|
|
20601
|
+
paramMappers: { ...input.paramMappers ?? {} }
|
|
20602
|
+
});
|
|
20603
|
+
}
|
|
20604
|
+
unregisterOwner(ownerId) {
|
|
20605
|
+
for (const [key, extension] of this.extensions) {
|
|
20606
|
+
if (extension.ownerId === ownerId) {
|
|
20607
|
+
this.extensions.delete(key);
|
|
20608
|
+
}
|
|
20609
|
+
}
|
|
20610
|
+
}
|
|
20611
|
+
list() {
|
|
20612
|
+
return [...this.extensions.values()].map((extension) => ({
|
|
20613
|
+
ownerId: extension.ownerId,
|
|
20614
|
+
descriptor: structuredClone(extension.descriptor)
|
|
20615
|
+
}));
|
|
20616
|
+
}
|
|
20617
|
+
supports(extensionId, version2, method) {
|
|
20618
|
+
return this.extensions.get(extensionKey(extensionId, version2))?.descriptor.methods.includes(method) ?? false;
|
|
20619
|
+
}
|
|
20620
|
+
effectiveCapabilities(base) {
|
|
20621
|
+
const effective = structuredClone(base);
|
|
20622
|
+
for (const extension of this.extensions.values()) {
|
|
20623
|
+
if (!extension.capabilityPatch) continue;
|
|
20624
|
+
for (const [section, patch] of Object.entries(extension.capabilityPatch)) {
|
|
20625
|
+
Object.assign(
|
|
20626
|
+
effective[section],
|
|
20627
|
+
patch
|
|
20628
|
+
);
|
|
20629
|
+
}
|
|
20630
|
+
}
|
|
20631
|
+
return effective;
|
|
20632
|
+
}
|
|
20633
|
+
invoke(input) {
|
|
20634
|
+
const key = extensionKey(input.extensionId, input.extensionVersion);
|
|
20635
|
+
const extension = this.extensions.get(key);
|
|
20636
|
+
if (!extension || !extension.descriptor.methods.includes(input.method)) {
|
|
20637
|
+
return Promise.reject(this.error(input, {
|
|
20638
|
+
code: "extension_method_unavailable",
|
|
20639
|
+
message: `Harness extension method is unavailable: ${key}/${input.method}`,
|
|
20640
|
+
retryable: false
|
|
20641
|
+
}));
|
|
20642
|
+
}
|
|
20643
|
+
if (input.signal?.aborted) {
|
|
20644
|
+
return Promise.reject(this.error(input, {
|
|
20645
|
+
code: "extension_cancelled",
|
|
20646
|
+
message: "Harness extension request was cancelled before dispatch.",
|
|
20647
|
+
retryable: true
|
|
20648
|
+
}));
|
|
20649
|
+
}
|
|
20650
|
+
const fingerprint = operationFingerprint(input);
|
|
20651
|
+
const cached = this.operations.get(input.idempotencyKey);
|
|
20652
|
+
if (cached) {
|
|
20653
|
+
if (cached.fingerprint !== fingerprint) {
|
|
20654
|
+
return Promise.reject(this.error(input, {
|
|
20655
|
+
code: "idempotency_conflict",
|
|
20656
|
+
message: "Harness extension idempotency key was reused for another operation.",
|
|
20657
|
+
retryable: false
|
|
20658
|
+
}));
|
|
20659
|
+
}
|
|
20660
|
+
return cached.promise;
|
|
20661
|
+
}
|
|
20662
|
+
const envelope = createHarnessExtensionCall(input);
|
|
20663
|
+
const controller = new AbortController();
|
|
20664
|
+
const timeoutMs = input.timeoutMs ?? 3e4;
|
|
20665
|
+
let timer = null;
|
|
20666
|
+
let abort = null;
|
|
20667
|
+
const request = new Promise((resolve, reject) => {
|
|
20668
|
+
abort = () => {
|
|
20669
|
+
controller.abort(input.signal?.reason);
|
|
20670
|
+
reject(this.error(input, {
|
|
20671
|
+
code: "extension_cancelled",
|
|
20672
|
+
message: "Harness extension request was cancelled.",
|
|
20673
|
+
retryable: true
|
|
20674
|
+
}));
|
|
20675
|
+
};
|
|
20676
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
20677
|
+
timer = setTimeout(() => {
|
|
20678
|
+
controller.abort(new Error("Harness extension request timed out."));
|
|
20679
|
+
reject(this.error(input, {
|
|
20680
|
+
code: "extension_timeout",
|
|
20681
|
+
message: `Harness extension request timed out after ${timeoutMs}ms.`,
|
|
20682
|
+
retryable: true
|
|
20683
|
+
}));
|
|
20684
|
+
}, timeoutMs);
|
|
20685
|
+
void Promise.resolve().then(() => extension.transport.request(
|
|
20686
|
+
extension.wireMethods[input.method] ?? harnessExtensionMethodName(
|
|
20687
|
+
input.extensionId,
|
|
20688
|
+
input.extensionVersion,
|
|
20689
|
+
input.method
|
|
20690
|
+
),
|
|
20691
|
+
extension.paramMappers[input.method]?.(envelope) ?? envelope,
|
|
20692
|
+
controller.signal
|
|
20693
|
+
)).then((value) => resolve(value), (cause) => reject(
|
|
20694
|
+
cause instanceof HarnessExtensionInvocationError ? cause : this.error(input, {
|
|
20695
|
+
code: "extension_request_failed",
|
|
20696
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
20697
|
+
retryable: false
|
|
20698
|
+
})
|
|
20699
|
+
));
|
|
20700
|
+
}).finally(() => {
|
|
20701
|
+
if (timer) clearTimeout(timer);
|
|
20702
|
+
if (abort) input.signal?.removeEventListener("abort", abort);
|
|
20703
|
+
});
|
|
20704
|
+
this.operations.set(input.idempotencyKey, { fingerprint, promise: request });
|
|
20705
|
+
request.catch(() => {
|
|
20706
|
+
if (this.operations.get(input.idempotencyKey)?.promise === request) {
|
|
20707
|
+
this.operations.delete(input.idempotencyKey);
|
|
20708
|
+
}
|
|
20709
|
+
});
|
|
20710
|
+
while (this.operations.size > 256) {
|
|
20711
|
+
this.operations.delete(this.operations.keys().next().value);
|
|
20712
|
+
}
|
|
20713
|
+
return request;
|
|
20714
|
+
}
|
|
20715
|
+
handleEvent(ownerId, event) {
|
|
20716
|
+
if (event.protocol !== REMOTE_CODEX_HARNESS_EXTENSION_PROTOCOL) {
|
|
20717
|
+
throw new Error("Harness extension event protocol is unsupported.");
|
|
20718
|
+
}
|
|
20719
|
+
const extension = this.extensions.get(
|
|
20720
|
+
extensionKey(event.extensionId, event.extensionVersion)
|
|
20721
|
+
);
|
|
20722
|
+
if (!extension || extension.ownerId !== ownerId) {
|
|
20723
|
+
throw new Error("Harness extension event owner does not match registration.");
|
|
20724
|
+
}
|
|
20725
|
+
if (!extension.descriptor.events.includes(event.event)) {
|
|
20726
|
+
throw new Error(`Harness extension event is not declared: ${event.event}`);
|
|
20727
|
+
}
|
|
20728
|
+
if (event.sequence !== null) {
|
|
20729
|
+
const sequenceKey = [
|
|
20730
|
+
ownerId,
|
|
20731
|
+
event.extensionId,
|
|
20732
|
+
event.extensionVersion,
|
|
20733
|
+
event.providerSessionId,
|
|
20734
|
+
event.event,
|
|
20735
|
+
event.sequence
|
|
20736
|
+
].join("\0");
|
|
20737
|
+
if (this.eventSequences.has(sequenceKey)) {
|
|
20738
|
+
return false;
|
|
20739
|
+
}
|
|
20740
|
+
this.eventSequences.add(sequenceKey);
|
|
20741
|
+
}
|
|
20742
|
+
this.emit("event", structuredClone(event));
|
|
20743
|
+
return true;
|
|
20744
|
+
}
|
|
20745
|
+
error(input, error) {
|
|
20746
|
+
return new HarnessExtensionInvocationError({
|
|
20747
|
+
protocol: REMOTE_CODEX_HARNESS_EXTENSION_PROTOCOL,
|
|
20748
|
+
extensionId: input.extensionId,
|
|
20749
|
+
extensionVersion: input.extensionVersion,
|
|
20750
|
+
method: input.method,
|
|
20751
|
+
operationId: input.operationId,
|
|
20752
|
+
...error
|
|
20753
|
+
});
|
|
20754
|
+
}
|
|
20755
|
+
};
|
|
20756
|
+
|
|
20757
|
+
// ../../packages/acp/src/session-hydrator.ts
|
|
20758
|
+
function updateMessageId(update) {
|
|
20759
|
+
return typeof update.messageId === "string" && update.messageId.trim() ? update.messageId.trim() : null;
|
|
20760
|
+
}
|
|
20761
|
+
var AcpSessionHydrator = class {
|
|
20762
|
+
constructor(providerSessionId) {
|
|
20763
|
+
this.providerSessionId = providerSessionId;
|
|
20764
|
+
}
|
|
20765
|
+
providerSessionId;
|
|
20766
|
+
turns = [];
|
|
20767
|
+
leadingUpdates = [];
|
|
20768
|
+
current = null;
|
|
20769
|
+
turnIndex = 0;
|
|
20770
|
+
providerIdentifiedTurnCount = 0;
|
|
20771
|
+
receivedAt = Date.now();
|
|
20772
|
+
apply(update) {
|
|
20773
|
+
if (update.sessionUpdate === "user_message_chunk") {
|
|
20774
|
+
this.applyUserMessage(update);
|
|
20775
|
+
return;
|
|
20776
|
+
}
|
|
20777
|
+
if (update.sessionUpdate === "config_option_update" || update.sessionUpdate === "current_mode_update" || update.sessionUpdate === "available_commands_update" || update.sessionUpdate === "session_info_update" || update.sessionUpdate === "usage_update") {
|
|
20778
|
+
return;
|
|
20779
|
+
}
|
|
20780
|
+
if (!this.current) {
|
|
20781
|
+
this.leadingUpdates.push(update);
|
|
20782
|
+
return;
|
|
20783
|
+
}
|
|
20784
|
+
const current = this.current;
|
|
20785
|
+
this.flushLeadingUpdates(current);
|
|
20786
|
+
current.sawNonUserUpdate = true;
|
|
20787
|
+
current.mapper.apply(update);
|
|
20788
|
+
}
|
|
20789
|
+
complete() {
|
|
20790
|
+
this.finishCurrent();
|
|
20791
|
+
return [...this.turns];
|
|
20792
|
+
}
|
|
20793
|
+
coverage() {
|
|
20794
|
+
this.finishCurrent();
|
|
20795
|
+
return {
|
|
20796
|
+
source: "providerReplay",
|
|
20797
|
+
// ACP v1 has no authoritative history total; absence cannot prove completeness.
|
|
20798
|
+
completeness: "unknown",
|
|
20799
|
+
replayedTurnCount: this.turns.length,
|
|
20800
|
+
replayedItemCount: this.turns.reduce(
|
|
20801
|
+
(count, turn) => count + turn.items.length,
|
|
20802
|
+
0
|
|
20803
|
+
),
|
|
20804
|
+
providerIdentifiedTurnCount: this.providerIdentifiedTurnCount
|
|
20805
|
+
};
|
|
20806
|
+
}
|
|
20807
|
+
applyUserMessage(update) {
|
|
20808
|
+
const messageId3 = updateMessageId(update);
|
|
20809
|
+
const shouldStartTurn = !this.current || this.current.sawNonUserUpdate || Boolean(
|
|
20810
|
+
messageId3 && this.current.userMessageId && messageId3 !== this.current.userMessageId
|
|
20811
|
+
);
|
|
20812
|
+
if (shouldStartTurn) {
|
|
20813
|
+
if (this.current) {
|
|
20814
|
+
this.finishCurrent();
|
|
20815
|
+
}
|
|
20816
|
+
this.startTurn(messageId3);
|
|
20817
|
+
}
|
|
20818
|
+
const current = this.current;
|
|
20819
|
+
if (messageId3 && !current.userMessageId) {
|
|
20820
|
+
current.userMessageId = messageId3;
|
|
20821
|
+
}
|
|
20822
|
+
current.mapper.appendUserMessage(
|
|
20823
|
+
update.content,
|
|
20824
|
+
current.userMessageId ?? `${current.mapper.turnId}:user`
|
|
20825
|
+
);
|
|
20826
|
+
}
|
|
20827
|
+
startTurn(userMessageId) {
|
|
20828
|
+
const index = this.turnIndex++;
|
|
20829
|
+
const stableSource = userMessageId ?? `${this.providerSessionId}:${index}`;
|
|
20830
|
+
const builder = {
|
|
20831
|
+
userMessageId,
|
|
20832
|
+
mapper: new AcpTurnItemMapper(
|
|
20833
|
+
`acp-hydrated:${stableSource}`,
|
|
20834
|
+
[],
|
|
20835
|
+
"hydrate"
|
|
20836
|
+
),
|
|
20837
|
+
startedAt: new Date(this.receivedAt + index).toISOString(),
|
|
20838
|
+
sawNonUserUpdate: false
|
|
20839
|
+
};
|
|
20840
|
+
if (userMessageId) {
|
|
20841
|
+
this.providerIdentifiedTurnCount += 1;
|
|
20842
|
+
}
|
|
20843
|
+
this.current = builder;
|
|
20844
|
+
return builder;
|
|
20845
|
+
}
|
|
20846
|
+
finishCurrent() {
|
|
20847
|
+
if (!this.current && this.leadingUpdates.length > 0) {
|
|
20848
|
+
this.startTurn(null);
|
|
20849
|
+
}
|
|
20850
|
+
if (!this.current) {
|
|
20851
|
+
return;
|
|
20852
|
+
}
|
|
20853
|
+
this.flushLeadingUpdates(this.current);
|
|
20854
|
+
const completed = this.current.mapper.complete("completed").turn;
|
|
20855
|
+
if (completed.items.length > 0) {
|
|
20856
|
+
this.turns.push({
|
|
20857
|
+
...completed,
|
|
20858
|
+
startedAt: this.current.startedAt
|
|
20859
|
+
});
|
|
20860
|
+
}
|
|
20861
|
+
this.current = null;
|
|
20862
|
+
}
|
|
20863
|
+
flushLeadingUpdates(current) {
|
|
20864
|
+
for (const update of this.leadingUpdates.splice(0)) {
|
|
20865
|
+
current.mapper.apply(update);
|
|
20866
|
+
}
|
|
20867
|
+
}
|
|
20868
|
+
};
|
|
20301
20869
|
|
|
20302
20870
|
// ../../packages/acp/src/terminal-service.ts
|
|
20303
20871
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -20317,14 +20885,28 @@ function retainedOutput(state) {
|
|
|
20317
20885
|
};
|
|
20318
20886
|
}
|
|
20319
20887
|
var AcpTerminalService = class {
|
|
20320
|
-
constructor(sessionCwd) {
|
|
20888
|
+
constructor(sessionCwd, onOperation = () => void 0) {
|
|
20321
20889
|
this.sessionCwd = sessionCwd;
|
|
20890
|
+
this.onOperation = onOperation;
|
|
20322
20891
|
}
|
|
20323
20892
|
sessionCwd;
|
|
20893
|
+
onOperation;
|
|
20324
20894
|
terminals = /* @__PURE__ */ new Map();
|
|
20325
|
-
create(params) {
|
|
20895
|
+
async create(params) {
|
|
20326
20896
|
const terminalId = randomUUID3();
|
|
20327
|
-
const
|
|
20897
|
+
const sessionCwd = this.sessionCwd(params.sessionId);
|
|
20898
|
+
if (!sessionCwd) {
|
|
20899
|
+
throw new Error(`ACP session workspace not found: ${params.sessionId}`);
|
|
20900
|
+
}
|
|
20901
|
+
const cwd = await resolveAcpWorkspacePath(
|
|
20902
|
+
sessionCwd,
|
|
20903
|
+
params.cwd ?? sessionCwd
|
|
20904
|
+
);
|
|
20905
|
+
this.onOperation({
|
|
20906
|
+
operation: "terminal.create",
|
|
20907
|
+
sessionId: params.sessionId,
|
|
20908
|
+
path: cwd
|
|
20909
|
+
});
|
|
20328
20910
|
const parsed = params.args && params.args.length > 0 ? { command: params.command, args: params.args } : parseCommandLine(params.command);
|
|
20329
20911
|
const child = spawnProcess({
|
|
20330
20912
|
command: parsed.command,
|
|
@@ -20412,10 +20994,13 @@ var AcpTerminalService = class {
|
|
|
20412
20994
|
// ../../packages/acp/src/runtimeAdapter.ts
|
|
20413
20995
|
var acpCapabilities = {
|
|
20414
20996
|
sessions: {
|
|
20415
|
-
list:
|
|
20997
|
+
list: false,
|
|
20416
20998
|
read: true,
|
|
20417
|
-
resume:
|
|
20418
|
-
importLocal: false
|
|
20999
|
+
resume: false,
|
|
21000
|
+
importLocal: false,
|
|
21001
|
+
load: false,
|
|
21002
|
+
close: false,
|
|
21003
|
+
delete: false
|
|
20419
21004
|
},
|
|
20420
21005
|
turns: {
|
|
20421
21006
|
start: true,
|
|
@@ -20467,7 +21052,19 @@ function errorMessage3(error) {
|
|
|
20467
21052
|
function cloneCapabilities() {
|
|
20468
21053
|
return structuredClone(acpCapabilities);
|
|
20469
21054
|
}
|
|
20470
|
-
function
|
|
21055
|
+
function applyNegotiatedAcpCapabilities(target, capabilities) {
|
|
21056
|
+
target.sessions.list = Boolean(capabilities?.sessionCapabilities?.list);
|
|
21057
|
+
target.sessions.load = capabilities?.loadSession === true;
|
|
21058
|
+
target.sessions.resume = Boolean(
|
|
21059
|
+
capabilities?.loadSession || capabilities?.sessionCapabilities?.resume
|
|
21060
|
+
);
|
|
21061
|
+
target.sessions.close = Boolean(capabilities?.sessionCapabilities?.close);
|
|
21062
|
+
target.sessions.delete = Boolean(capabilities?.sessionCapabilities?.delete);
|
|
21063
|
+
target.sessions.importLocal = false;
|
|
21064
|
+
target.branching.fork = Boolean(capabilities?.sessionCapabilities?.fork);
|
|
21065
|
+
return target;
|
|
21066
|
+
}
|
|
21067
|
+
function isRecord13(value) {
|
|
20471
21068
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20472
21069
|
}
|
|
20473
21070
|
function allSelectOptions(option) {
|
|
@@ -20563,8 +21160,12 @@ function sessionDetail(state) {
|
|
|
20563
21160
|
createdAt: state.createdAt,
|
|
20564
21161
|
updatedAt: state.updatedAt,
|
|
20565
21162
|
status: state.status,
|
|
20566
|
-
turns: state.turns
|
|
20567
|
-
|
|
21163
|
+
turns: state.turns.map((turn) => ({
|
|
21164
|
+
...turn,
|
|
21165
|
+
items: turn.items.map((item) => ({ ...item }))
|
|
21166
|
+
})),
|
|
21167
|
+
totalTurnCount: state.turns.length,
|
|
21168
|
+
...state.hydrationCoverage ? { historyCoverage: { ...state.hydrationCoverage } } : {}
|
|
20568
21169
|
};
|
|
20569
21170
|
}
|
|
20570
21171
|
function sessionSummaryFromInfo2(info) {
|
|
@@ -20580,12 +21181,29 @@ function sessionSummaryFromInfo2(info) {
|
|
|
20580
21181
|
rawSession: info
|
|
20581
21182
|
};
|
|
20582
21183
|
}
|
|
20583
|
-
var AcpRuntimeAdapter = class extends
|
|
21184
|
+
var AcpRuntimeAdapter = class extends EventEmitter8 {
|
|
20584
21185
|
constructor(options) {
|
|
20585
21186
|
super();
|
|
20586
21187
|
this.options = options;
|
|
21188
|
+
this.extensionRegistry.on(
|
|
21189
|
+
"event",
|
|
21190
|
+
(event) => this.emitRuntimeEvent({
|
|
21191
|
+
type: "harness.extension",
|
|
21192
|
+
provider: "acp",
|
|
21193
|
+
providerSessionId: event.providerSessionId,
|
|
21194
|
+
providerTurnId: event.providerTurnId,
|
|
21195
|
+
providerItemId: event.providerItemId,
|
|
21196
|
+
extensionId: event.extensionId,
|
|
21197
|
+
extensionVersion: event.extensionVersion,
|
|
21198
|
+
event: event.event,
|
|
21199
|
+
operationId: event.operationId,
|
|
21200
|
+
sequence: event.sequence,
|
|
21201
|
+
payload: event.payload
|
|
21202
|
+
})
|
|
21203
|
+
);
|
|
20587
21204
|
this.terminalService = new AcpTerminalService(
|
|
20588
|
-
(sessionId) => this.sessions.get(sessionId)?.cwd ?? null
|
|
21205
|
+
(sessionId) => this.sessions.get(sessionId)?.cwd ?? null,
|
|
21206
|
+
(operation) => this.emit("fs-operation", operation)
|
|
20589
21207
|
);
|
|
20590
21208
|
}
|
|
20591
21209
|
options;
|
|
@@ -20607,6 +21225,8 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20607
21225
|
sessions = /* @__PURE__ */ new Map();
|
|
20608
21226
|
knownSessions = /* @__PURE__ */ new Map();
|
|
20609
21227
|
pendingPermissions = /* @__PURE__ */ new Map();
|
|
21228
|
+
hydrators = /* @__PURE__ */ new Map();
|
|
21229
|
+
extensionRegistry = new HarnessExtensionRegistry();
|
|
20610
21230
|
terminalService;
|
|
20611
21231
|
child = null;
|
|
20612
21232
|
connection = null;
|
|
@@ -20625,6 +21245,9 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20625
21245
|
getStatus() {
|
|
20626
21246
|
return { ...this.status };
|
|
20627
21247
|
}
|
|
21248
|
+
getProtocolSnapshot() {
|
|
21249
|
+
return snapshotAcpInitializeResponse(this.initializeResponse);
|
|
21250
|
+
}
|
|
20628
21251
|
async start() {
|
|
20629
21252
|
if (this.status.state === "ready") {
|
|
20630
21253
|
return;
|
|
@@ -20675,7 +21298,16 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20675
21298
|
Writable.toWeb(child.stdin),
|
|
20676
21299
|
Readable.toWeb(child.stdout)
|
|
20677
21300
|
);
|
|
20678
|
-
const app2 = client({ name: this.options.clientInfo?.name ?? "remote-codex-supervisor" }).onRequest(methods.client.session.requestPermission, (request) => this.requestPermission(request.params)).onNotification(methods.client.session.update, (notification) => this.handleSessionUpdate(notification.params)).
|
|
21301
|
+
const app2 = client({ name: this.options.clientInfo?.name ?? "remote-codex-supervisor" }).onRequest(methods.client.session.requestPermission, (request) => this.requestPermission(request.params)).onNotification(methods.client.session.update, (notification) => this.handleSessionUpdate(notification.params)).onNotification(
|
|
21302
|
+
REMOTE_CODEX_HARNESS_EXTENSION_EVENT_METHOD,
|
|
21303
|
+
(params) => params,
|
|
21304
|
+
(notification) => {
|
|
21305
|
+
this.extensionRegistry.handleEvent(
|
|
21306
|
+
"acp-agent",
|
|
21307
|
+
notification.params
|
|
21308
|
+
);
|
|
21309
|
+
}
|
|
21310
|
+
).onRequest(methods.client.fs.readTextFile, (request) => this.readTextFile(request.params)).onRequest(methods.client.fs.writeTextFile, (request) => this.writeTextFile(request.params)).onRequest(methods.client.terminal.create, (request) => this.terminalService.create(request.params)).onRequest(methods.client.terminal.output, (request) => this.terminalService.output(request.params)).onRequest(methods.client.terminal.waitForExit, (request) => this.terminalService.waitForExit(request.params)).onRequest(methods.client.terminal.kill, (request) => this.terminalService.kill(request.params)).onRequest(methods.client.terminal.release, (request) => this.terminalService.release(request.params));
|
|
20679
21311
|
this.connection = app2.connect(stream);
|
|
20680
21312
|
this.context = this.connection.agent;
|
|
20681
21313
|
const initialize = this.context.request(methods.agent.initialize, {
|
|
@@ -20697,7 +21329,9 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20697
21329
|
});
|
|
20698
21330
|
const initializeResponse = await this.withStartupTimeout(initialize);
|
|
20699
21331
|
this.initializeResponse = initializeResponse;
|
|
21332
|
+
this.resetCapabilities();
|
|
20700
21333
|
this.applyAgentCapabilities(initializeResponse.agentCapabilities);
|
|
21334
|
+
this.registerNegotiatedExtensions(initializeResponse);
|
|
20701
21335
|
this.status = {
|
|
20702
21336
|
...this.status,
|
|
20703
21337
|
state: "ready",
|
|
@@ -20727,11 +21361,11 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20727
21361
|
async stop() {
|
|
20728
21362
|
this.stopping = true;
|
|
20729
21363
|
this.terminalService.stop();
|
|
20730
|
-
|
|
20731
|
-
|
|
20732
|
-
|
|
20733
|
-
|
|
20734
|
-
|
|
21364
|
+
this.settleActiveTurns("interrupted", "ACP runtime stopped.");
|
|
21365
|
+
this.cancelPendingPermissions();
|
|
21366
|
+
this.hydrators.clear();
|
|
21367
|
+
await this.closeLoadedSessionsBeforeStop();
|
|
21368
|
+
this.extensionRegistry.unregisterOwner("acp-agent");
|
|
20735
21369
|
this.connection?.close();
|
|
20736
21370
|
this.child?.kill("SIGTERM");
|
|
20737
21371
|
this.connection = null;
|
|
@@ -20746,6 +21380,21 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20746
21380
|
};
|
|
20747
21381
|
this.emit("status", this.getStatus());
|
|
20748
21382
|
}
|
|
21383
|
+
async closeLoadedSessionsBeforeStop() {
|
|
21384
|
+
if (!this.context || !this.initializeResponse?.agentCapabilities?.sessionCapabilities?.close) {
|
|
21385
|
+
return;
|
|
21386
|
+
}
|
|
21387
|
+
const sessionIds = [...this.sessions.keys()];
|
|
21388
|
+
const results = await Promise.allSettled(sessionIds.map((sessionId) => this.context.request(methods.agent.session.close, { sessionId })));
|
|
21389
|
+
results.forEach((result, index) => {
|
|
21390
|
+
if (result.status === "rejected") {
|
|
21391
|
+
this.emit(
|
|
21392
|
+
"warning",
|
|
21393
|
+
`Unable to close ACP session ${sessionIds[index] ?? "unknown"} before stop: ${errorMessage3(result.reason)}`
|
|
21394
|
+
);
|
|
21395
|
+
}
|
|
21396
|
+
});
|
|
21397
|
+
}
|
|
20749
21398
|
async listModels() {
|
|
20750
21399
|
return [{
|
|
20751
21400
|
id: "default",
|
|
@@ -20759,13 +21408,21 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20759
21408
|
}];
|
|
20760
21409
|
}
|
|
20761
21410
|
async inspectModelOptions(cwd) {
|
|
21411
|
+
const sessionCapabilities = this.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
21412
|
+
if (!sessionCapabilities?.delete) {
|
|
21413
|
+
return this.listModels();
|
|
21414
|
+
}
|
|
20762
21415
|
const context = await this.requireContext();
|
|
20763
21416
|
const response = await context.request(methods.agent.session.new, {
|
|
20764
|
-
cwd:
|
|
21417
|
+
cwd: path16.resolve(cwd),
|
|
20765
21418
|
mcpServers: [],
|
|
20766
21419
|
_meta: { yoloMode: false, remoteCodexCapabilityProbe: true }
|
|
20767
21420
|
});
|
|
20768
21421
|
let configOptions = response.configOptions ?? [];
|
|
21422
|
+
this.updateCapabilitiesFromConfigOptions(configOptions);
|
|
21423
|
+
const supportsPerformanceMode = configOptions.some(
|
|
21424
|
+
(option) => option.id === "fast-mode" || option.category === "model_config"
|
|
21425
|
+
);
|
|
20769
21426
|
try {
|
|
20770
21427
|
const modelOption = configOptionByCategory(configOptions, "model");
|
|
20771
21428
|
if (!modelOption || modelOption.type !== "select") {
|
|
@@ -20777,6 +21434,7 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20777
21434
|
description: "Use the model configured by the ACP agent.",
|
|
20778
21435
|
isDefault: true,
|
|
20779
21436
|
hidden: false,
|
|
21437
|
+
supportsPerformanceMode,
|
|
20780
21438
|
supportedReasoningEfforts: reasoning.efforts,
|
|
20781
21439
|
defaultReasoningEffort: reasoning.defaultEffort,
|
|
20782
21440
|
selectionKind: "model"
|
|
@@ -20804,6 +21462,7 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20804
21462
|
description: option.description ?? "",
|
|
20805
21463
|
isDefault: option.value === modelOption.currentValue,
|
|
20806
21464
|
hidden: false,
|
|
21465
|
+
supportsPerformanceMode,
|
|
20807
21466
|
supportedReasoningEfforts: reasoning.efforts,
|
|
20808
21467
|
defaultReasoningEffort: reasoning.defaultEffort,
|
|
20809
21468
|
selectionKind: "model"
|
|
@@ -20811,11 +21470,9 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20811
21470
|
}
|
|
20812
21471
|
return models;
|
|
20813
21472
|
} finally {
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
}).catch(() => void 0);
|
|
20818
|
-
}
|
|
21473
|
+
await context.request(methods.agent.session.delete, {
|
|
21474
|
+
sessionId: response.sessionId
|
|
21475
|
+
}).catch(() => void 0);
|
|
20819
21476
|
}
|
|
20820
21477
|
}
|
|
20821
21478
|
async listSessions() {
|
|
@@ -20847,22 +21504,170 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20847
21504
|
async listLoadedSessions() {
|
|
20848
21505
|
return [...this.sessions.keys()];
|
|
20849
21506
|
}
|
|
20850
|
-
async
|
|
20851
|
-
|
|
20852
|
-
|
|
20853
|
-
|
|
21507
|
+
async closeSession(providerSessionId) {
|
|
21508
|
+
if (!this.initializeResponse?.agentCapabilities?.sessionCapabilities?.close) {
|
|
21509
|
+
throw new AgentRuntimeError(
|
|
21510
|
+
"ACP agent does not support session/close.",
|
|
21511
|
+
"acp",
|
|
21512
|
+
"request_failed"
|
|
21513
|
+
);
|
|
20854
21514
|
}
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
21515
|
+
await (await this.requireContext()).request(methods.agent.session.close, {
|
|
21516
|
+
sessionId: providerSessionId
|
|
21517
|
+
});
|
|
21518
|
+
this.sessions.delete(providerSessionId);
|
|
21519
|
+
}
|
|
21520
|
+
async deleteSession(providerSessionId) {
|
|
21521
|
+
if (!this.initializeResponse?.agentCapabilities?.sessionCapabilities?.delete) {
|
|
21522
|
+
throw new AgentRuntimeError(
|
|
21523
|
+
"ACP agent does not support session/delete.",
|
|
21524
|
+
"acp",
|
|
21525
|
+
"request_failed"
|
|
21526
|
+
);
|
|
21527
|
+
}
|
|
21528
|
+
await (await this.requireContext()).request(methods.agent.session.delete, {
|
|
21529
|
+
sessionId: providerSessionId
|
|
21530
|
+
});
|
|
21531
|
+
this.sessions.delete(providerSessionId);
|
|
21532
|
+
this.knownSessions.delete(providerSessionId);
|
|
21533
|
+
}
|
|
21534
|
+
async sendInput(input) {
|
|
21535
|
+
const state = this.sessions.get(input.providerSessionId);
|
|
21536
|
+
if (!state?.activeMapper || state.activeMapper.turnId !== input.providerTurnId) {
|
|
21537
|
+
return null;
|
|
21538
|
+
}
|
|
21539
|
+
if (!this.extensionRegistry.supports("acp.steering", 1, "steer")) {
|
|
21540
|
+
throw new AgentRuntimeError(
|
|
21541
|
+
"The selected ACP agent does not support running-turn steering.",
|
|
21542
|
+
"acp",
|
|
21543
|
+
"request_failed"
|
|
21544
|
+
);
|
|
21545
|
+
}
|
|
21546
|
+
const prompt = await buildAcpPromptContent({
|
|
21547
|
+
prompt: input.prompt,
|
|
21548
|
+
workspacePath: input.workspacePath ?? state.cwd,
|
|
21549
|
+
promptCapabilities: this.initializeResponse?.agentCapabilities?.promptCapabilities
|
|
21550
|
+
});
|
|
21551
|
+
const operationId = randomUUID4();
|
|
21552
|
+
await this.extensionRegistry.invoke({
|
|
21553
|
+
extensionId: "acp.steering",
|
|
21554
|
+
extensionVersion: 1,
|
|
21555
|
+
method: "steer",
|
|
21556
|
+
operationId,
|
|
21557
|
+
idempotencyKey: `${input.providerSessionId}:${input.providerTurnId}:steer:${operationId}`,
|
|
21558
|
+
params: {
|
|
21559
|
+
providerSessionId: input.providerSessionId,
|
|
21560
|
+
prompt
|
|
21561
|
+
}
|
|
21562
|
+
});
|
|
21563
|
+
return state.activeMapper.turn();
|
|
21564
|
+
}
|
|
21565
|
+
async compactSession(providerSessionId) {
|
|
21566
|
+
const operationId = randomUUID4();
|
|
21567
|
+
await this.extensionRegistry.invoke({
|
|
21568
|
+
extensionId: "codex.control",
|
|
21569
|
+
extensionVersion: 1,
|
|
21570
|
+
method: "compact",
|
|
21571
|
+
operationId,
|
|
21572
|
+
idempotencyKey: `${providerSessionId}:compact:${operationId}`,
|
|
21573
|
+
params: { providerSessionId },
|
|
21574
|
+
timeoutMs: 18e4
|
|
21575
|
+
});
|
|
21576
|
+
}
|
|
21577
|
+
async forkSession(input) {
|
|
21578
|
+
if (!this.initializeResponse?.agentCapabilities?.sessionCapabilities?.fork) {
|
|
21579
|
+
throw new AgentRuntimeError(
|
|
21580
|
+
"The selected ACP agent does not support session/fork.",
|
|
21581
|
+
"acp",
|
|
21582
|
+
"request_failed"
|
|
21583
|
+
);
|
|
21584
|
+
}
|
|
21585
|
+
const source = this.sessions.get(input.providerSessionId) ?? await this.restoreSession(input.providerSessionId);
|
|
21586
|
+
const response = await (await this.requireContext()).request(
|
|
21587
|
+
methods.agent.session.fork,
|
|
21588
|
+
{
|
|
21589
|
+
sessionId: source.providerSessionId,
|
|
21590
|
+
cwd: source.cwd,
|
|
21591
|
+
mcpServers: []
|
|
21592
|
+
}
|
|
20860
21593
|
);
|
|
21594
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21595
|
+
this.knownSessions.set(response.sessionId, {
|
|
21596
|
+
provider: "acp",
|
|
21597
|
+
providerSessionId: response.sessionId,
|
|
21598
|
+
cwd: source.cwd,
|
|
21599
|
+
title: source.title,
|
|
21600
|
+
preview: source.turns.at(-1)?.items.filter((item) => item.kind === "agentMessage").map((item) => item.text).join("\n") || null,
|
|
21601
|
+
createdAt: now,
|
|
21602
|
+
updatedAt: now,
|
|
21603
|
+
status: "not_loaded"
|
|
21604
|
+
});
|
|
21605
|
+
const forked = await this.restoreSession(response.sessionId);
|
|
21606
|
+
if (forked.turns.length === 0 && source.turns.length > 0) {
|
|
21607
|
+
forked.turns = structuredClone(source.turns);
|
|
21608
|
+
forked.hydrationCoverage = null;
|
|
21609
|
+
}
|
|
21610
|
+
forked.modes = response.modes ?? forked.modes;
|
|
21611
|
+
forked.configOptions = response.configOptions ?? forked.configOptions;
|
|
21612
|
+
this.syncStateFromConfigOptions(forked);
|
|
21613
|
+
return sessionDetail(forked);
|
|
21614
|
+
}
|
|
21615
|
+
async getGoal(providerSessionId) {
|
|
21616
|
+
return this.sessions.get(providerSessionId)?.goal ?? null;
|
|
21617
|
+
}
|
|
21618
|
+
async setGoal(input) {
|
|
21619
|
+
const state = this.sessions.get(input.providerSessionId);
|
|
21620
|
+
if (!state) {
|
|
21621
|
+
throw new AgentRuntimeError("ACP session is not loaded.", "acp", "request_failed");
|
|
21622
|
+
}
|
|
21623
|
+
if (input.tokenBudget !== void 0 && input.tokenBudget !== null) {
|
|
21624
|
+
throw new AgentRuntimeError(
|
|
21625
|
+
"Codex ACP goal control does not expose token budgets.",
|
|
21626
|
+
"acp",
|
|
21627
|
+
"request_failed"
|
|
21628
|
+
);
|
|
21629
|
+
}
|
|
21630
|
+
const action = input.objective?.trim() ? { action: "set", objective: input.objective.trim() } : input.status === "paused" ? { action: "pause" } : input.status === "active" ? { action: "resume" } : null;
|
|
21631
|
+
if (!action) {
|
|
21632
|
+
throw new AgentRuntimeError(
|
|
21633
|
+
"Codex ACP goal update requires an objective, pause, or resume action.",
|
|
21634
|
+
"acp",
|
|
21635
|
+
"request_failed"
|
|
21636
|
+
);
|
|
21637
|
+
}
|
|
21638
|
+
await this.invokeAcpGoal(input.providerSessionId, action);
|
|
21639
|
+
if (!state.goal) {
|
|
21640
|
+
throw new AgentRuntimeError(
|
|
21641
|
+
"Codex ACP completed goal control without publishing a goal snapshot.",
|
|
21642
|
+
"acp",
|
|
21643
|
+
"invalid_response"
|
|
21644
|
+
);
|
|
21645
|
+
}
|
|
21646
|
+
return state.goal;
|
|
21647
|
+
}
|
|
21648
|
+
async clearGoal(providerSessionId) {
|
|
21649
|
+
const state = this.sessions.get(providerSessionId);
|
|
21650
|
+
if (!state) {
|
|
21651
|
+
return false;
|
|
21652
|
+
}
|
|
21653
|
+
const existed = Boolean(state.goal);
|
|
21654
|
+
await this.invokeAcpGoal(providerSessionId, { action: "clear" });
|
|
21655
|
+
return existed;
|
|
21656
|
+
}
|
|
21657
|
+
listHarnessExtensions() {
|
|
21658
|
+
return this.extensionRegistry.list();
|
|
21659
|
+
}
|
|
21660
|
+
invokeHarnessExtension(input) {
|
|
21661
|
+
return this.extensionRegistry.invoke(input);
|
|
21662
|
+
}
|
|
21663
|
+
async readSession(providerSessionId) {
|
|
21664
|
+
const state = this.sessions.get(providerSessionId) ?? await this.restoreSession(providerSessionId);
|
|
21665
|
+
return sessionDetail(state);
|
|
20861
21666
|
}
|
|
20862
21667
|
async startSession(input) {
|
|
20863
21668
|
const context = await this.requireContext();
|
|
20864
21669
|
const response = await context.request(methods.agent.session.new, {
|
|
20865
|
-
cwd:
|
|
21670
|
+
cwd: path16.resolve(input.cwd),
|
|
20866
21671
|
mcpServers: [],
|
|
20867
21672
|
_meta: {
|
|
20868
21673
|
yoloMode: input.approvalMode === "yolo"
|
|
@@ -20871,23 +21676,33 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20871
21676
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20872
21677
|
const state = {
|
|
20873
21678
|
providerSessionId: response.sessionId,
|
|
20874
|
-
cwd:
|
|
21679
|
+
cwd: path16.resolve(input.cwd),
|
|
20875
21680
|
title: null,
|
|
20876
21681
|
createdAt: now,
|
|
20877
21682
|
updatedAt: now,
|
|
20878
|
-
model:
|
|
20879
|
-
reasoningEffort:
|
|
21683
|
+
model: null,
|
|
21684
|
+
reasoningEffort: null,
|
|
20880
21685
|
sandboxMode: input.sandboxMode ?? null,
|
|
20881
21686
|
status: "idle",
|
|
20882
21687
|
turns: [],
|
|
20883
21688
|
activeMapper: null,
|
|
20884
21689
|
modes: response.modes ?? null,
|
|
20885
21690
|
configOptions: response.configOptions ?? [],
|
|
20886
|
-
availableCommands: []
|
|
21691
|
+
availableCommands: [],
|
|
21692
|
+
hydrationCoverage: null,
|
|
21693
|
+
goal: null
|
|
20887
21694
|
};
|
|
20888
21695
|
this.sessions.set(state.providerSessionId, state);
|
|
21696
|
+
this.syncStateFromConfigOptions(state);
|
|
20889
21697
|
this.knownSessions.set(state.providerSessionId, sessionDetail(state));
|
|
20890
|
-
await this.applySessionSettings(
|
|
21698
|
+
await this.applySessionSettings(
|
|
21699
|
+
state,
|
|
21700
|
+
input.model,
|
|
21701
|
+
input.reasoningEffort,
|
|
21702
|
+
input.sandboxMode,
|
|
21703
|
+
void 0,
|
|
21704
|
+
input.performanceMode
|
|
21705
|
+
);
|
|
20891
21706
|
const session = sessionDetail(state);
|
|
20892
21707
|
return {
|
|
20893
21708
|
provider: "acp",
|
|
@@ -20903,7 +21718,14 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20903
21718
|
async resumeSession(input) {
|
|
20904
21719
|
const existing = this.sessions.get(input.providerSessionId);
|
|
20905
21720
|
const state = existing ?? await this.restoreSession(input.providerSessionId);
|
|
20906
|
-
await this.applySessionSettings(
|
|
21721
|
+
await this.applySessionSettings(
|
|
21722
|
+
state,
|
|
21723
|
+
input.model,
|
|
21724
|
+
void 0,
|
|
21725
|
+
input.sandboxMode,
|
|
21726
|
+
void 0,
|
|
21727
|
+
input.performanceMode
|
|
21728
|
+
);
|
|
20907
21729
|
state.status = "idle";
|
|
20908
21730
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20909
21731
|
const session = sessionDetail(state);
|
|
@@ -20933,7 +21755,8 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20933
21755
|
input.model,
|
|
20934
21756
|
input.reasoningEffort,
|
|
20935
21757
|
input.sandboxMode,
|
|
20936
|
-
input.collaborationMode
|
|
21758
|
+
input.collaborationMode,
|
|
21759
|
+
input.performanceMode
|
|
20937
21760
|
);
|
|
20938
21761
|
const turnId = input.displayTurnId ?? randomUUID4();
|
|
20939
21762
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -20955,13 +21778,21 @@ var AcpRuntimeAdapter = class extends EventEmitter7 {
|
|
|
20955
21778
|
providerSessionId: state.providerSessionId,
|
|
20956
21779
|
turn: startedTurn
|
|
20957
21780
|
});
|
|
20958
|
-
const prompt =
|
|
21781
|
+
const prompt = await buildAcpPromptContent({
|
|
21782
|
+
prompt: input.prompt,
|
|
21783
|
+
workspacePath: input.workspacePath ?? state.cwd,
|
|
21784
|
+
promptCapabilities: this.initializeResponse?.agentCapabilities?.promptCapabilities,
|
|
21785
|
+
...input.content ? { content: input.content } : {}
|
|
21786
|
+
});
|
|
21787
|
+
if (input.developerInstructions?.trim()) {
|
|
21788
|
+
prompt.unshift({ type: "text", text: `${input.developerInstructions.trim()}
|
|
20959
21789
|
|
|
20960
|
-
|
|
21790
|
+
` });
|
|
21791
|
+
}
|
|
20961
21792
|
const context = await this.requireContext();
|
|
20962
21793
|
void context.request(methods.agent.session.prompt, {
|
|
20963
21794
|
sessionId: state.providerSessionId,
|
|
20964
|
-
prompt
|
|
21795
|
+
prompt
|
|
20965
21796
|
}).then(
|
|
20966
21797
|
(response) => this.completePrompt(state, mapper, response),
|
|
20967
21798
|
(error) => this.failPrompt(state, mapper, error)
|
|
@@ -20980,7 +21811,7 @@ ${input.prompt}` : input.prompt;
|
|
|
20980
21811
|
return state.activeMapper.turn("interrupted");
|
|
20981
21812
|
}
|
|
20982
21813
|
mapProviderRequest(request, options) {
|
|
20983
|
-
if (request.method !== methods.client.session.requestPermission || !
|
|
21814
|
+
if (request.method !== methods.client.session.requestPermission || !isRecord13(request.params)) {
|
|
20984
21815
|
return null;
|
|
20985
21816
|
}
|
|
20986
21817
|
const params = request.params;
|
|
@@ -21027,7 +21858,7 @@ ${input.prompt}` : input.prompt;
|
|
|
21027
21858
|
};
|
|
21028
21859
|
}
|
|
21029
21860
|
buildProviderRequestResponse(pending, input) {
|
|
21030
|
-
const options = Array.isArray(pending.responsePayload?.options) ? pending.responsePayload.options.filter(
|
|
21861
|
+
const options = Array.isArray(pending.responsePayload?.options) ? pending.responsePayload.options.filter(isRecord13) : [];
|
|
21031
21862
|
const answer = Object.values(input.answers).flatMap((entry) => entry.answers)[0] ?? "";
|
|
21032
21863
|
const selected = options.find((option) => option.name === answer || option.id === answer);
|
|
21033
21864
|
return selected && typeof selected.id === "string" ? selectedPermission(selected.id) : cancelledPermission();
|
|
@@ -21072,19 +21903,30 @@ ${input.prompt}` : input.prompt;
|
|
|
21072
21903
|
activeMapper: null,
|
|
21073
21904
|
modes: null,
|
|
21074
21905
|
configOptions: [],
|
|
21075
|
-
availableCommands: []
|
|
21906
|
+
availableCommands: [],
|
|
21907
|
+
hydrationCoverage: null,
|
|
21908
|
+
goal: null
|
|
21076
21909
|
};
|
|
21077
21910
|
this.sessions.set(providerSessionId, state);
|
|
21078
21911
|
try {
|
|
21079
21912
|
const capabilities = this.initializeResponse?.agentCapabilities;
|
|
21080
21913
|
if (capabilities?.loadSession) {
|
|
21081
|
-
const
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21085
|
-
|
|
21086
|
-
|
|
21087
|
-
|
|
21914
|
+
const hydrator = new AcpSessionHydrator(providerSessionId);
|
|
21915
|
+
this.hydrators.set(providerSessionId, hydrator);
|
|
21916
|
+
try {
|
|
21917
|
+
const response = await context.request(methods.agent.session.load, {
|
|
21918
|
+
sessionId: providerSessionId,
|
|
21919
|
+
cwd: summary.cwd,
|
|
21920
|
+
mcpServers: []
|
|
21921
|
+
});
|
|
21922
|
+
state.modes = response.modes ?? null;
|
|
21923
|
+
state.configOptions = response.configOptions ?? [];
|
|
21924
|
+
state.turns = hydrator.complete();
|
|
21925
|
+
state.hydrationCoverage = hydrator.coverage();
|
|
21926
|
+
this.syncStateFromConfigOptions(state);
|
|
21927
|
+
} finally {
|
|
21928
|
+
this.hydrators.delete(providerSessionId);
|
|
21929
|
+
}
|
|
21088
21930
|
} else if (capabilities?.sessionCapabilities?.resume) {
|
|
21089
21931
|
const response = await context.request(methods.agent.session.resume, {
|
|
21090
21932
|
sessionId: providerSessionId,
|
|
@@ -21093,6 +21935,7 @@ ${input.prompt}` : input.prompt;
|
|
|
21093
21935
|
});
|
|
21094
21936
|
state.modes = response.modes ?? null;
|
|
21095
21937
|
state.configOptions = response.configOptions ?? [];
|
|
21938
|
+
this.syncStateFromConfigOptions(state);
|
|
21096
21939
|
} else {
|
|
21097
21940
|
throw new Error("ACP agent does not support session/load or session/resume.");
|
|
21098
21941
|
}
|
|
@@ -21109,18 +21952,24 @@ ${input.prompt}` : input.prompt;
|
|
|
21109
21952
|
);
|
|
21110
21953
|
}
|
|
21111
21954
|
}
|
|
21112
|
-
async applySessionSettings(state, model, reasoningEffort, sandboxMode, collaborationMode) {
|
|
21955
|
+
async applySessionSettings(state, model, reasoningEffort, sandboxMode, collaborationMode, performanceMode) {
|
|
21113
21956
|
if (model && model !== "default") {
|
|
21114
|
-
await this.setConfigOption(state, "model", model);
|
|
21115
|
-
state.model = model;
|
|
21957
|
+
state.model = await this.setConfigOption(state, "model", model);
|
|
21116
21958
|
}
|
|
21117
21959
|
if (reasoningEffort) {
|
|
21118
|
-
await this.setConfigOption(
|
|
21119
|
-
|
|
21960
|
+
const applied = await this.setConfigOption(
|
|
21961
|
+
state,
|
|
21962
|
+
"thought_level",
|
|
21963
|
+
reasoningEffort
|
|
21964
|
+
);
|
|
21965
|
+
state.reasoningEffort = normalizeAcpEffort(applied);
|
|
21120
21966
|
}
|
|
21121
21967
|
if (sandboxMode !== void 0) {
|
|
21122
21968
|
state.sandboxMode = sandboxMode;
|
|
21123
21969
|
}
|
|
21970
|
+
if (performanceMode) {
|
|
21971
|
+
await this.setFastMode(state, performanceMode === "fast");
|
|
21972
|
+
}
|
|
21124
21973
|
const modes = state.modes?.availableModes ?? [];
|
|
21125
21974
|
if (modes.length === 0) {
|
|
21126
21975
|
return;
|
|
@@ -21146,16 +21995,27 @@ ${input.prompt}` : input.prompt;
|
|
|
21146
21995
|
sessionId: state.providerSessionId,
|
|
21147
21996
|
modelId: value
|
|
21148
21997
|
});
|
|
21149
|
-
return;
|
|
21998
|
+
return value;
|
|
21150
21999
|
}
|
|
21151
22000
|
if (!option || option.type !== "select") {
|
|
21152
|
-
|
|
22001
|
+
throw new AgentRuntimeError(
|
|
22002
|
+
`ACP agent does not expose a ${category} config option.`,
|
|
22003
|
+
"acp",
|
|
22004
|
+
"request_failed"
|
|
22005
|
+
);
|
|
21153
22006
|
}
|
|
21154
22007
|
const selected = allSelectOptions(option).find(
|
|
21155
22008
|
(candidate) => candidate.value === value || candidate.name.toLowerCase() === value.toLowerCase()
|
|
21156
22009
|
);
|
|
21157
|
-
if (!selected
|
|
21158
|
-
|
|
22010
|
+
if (!selected) {
|
|
22011
|
+
throw new AgentRuntimeError(
|
|
22012
|
+
`ACP agent rejected unknown ${category} option: ${value}`,
|
|
22013
|
+
"acp",
|
|
22014
|
+
"request_failed"
|
|
22015
|
+
);
|
|
22016
|
+
}
|
|
22017
|
+
if (selected.value === option.currentValue) {
|
|
22018
|
+
return selected.value;
|
|
21159
22019
|
}
|
|
21160
22020
|
const context = await this.requireContext();
|
|
21161
22021
|
const response = await context.request(methods.agent.session.setConfigOption, {
|
|
@@ -21164,6 +22024,60 @@ ${input.prompt}` : input.prompt;
|
|
|
21164
22024
|
value: selected.value
|
|
21165
22025
|
});
|
|
21166
22026
|
state.configOptions = response.configOptions;
|
|
22027
|
+
return selected.value;
|
|
22028
|
+
}
|
|
22029
|
+
syncStateFromConfigOptions(state) {
|
|
22030
|
+
this.updateCapabilitiesFromConfigOptions(state.configOptions);
|
|
22031
|
+
const modelOption = configOptionByCategory(state.configOptions, "model");
|
|
22032
|
+
if (modelOption?.type === "select") {
|
|
22033
|
+
state.model = modelOption.currentValue;
|
|
22034
|
+
}
|
|
22035
|
+
const thoughtOption = configOptionByCategory(
|
|
22036
|
+
state.configOptions,
|
|
22037
|
+
"thought_level"
|
|
22038
|
+
);
|
|
22039
|
+
if (thoughtOption?.type === "select") {
|
|
22040
|
+
state.reasoningEffort = normalizeAcpEffort(thoughtOption.currentValue);
|
|
22041
|
+
}
|
|
22042
|
+
}
|
|
22043
|
+
updateCapabilitiesFromConfigOptions(configOptions) {
|
|
22044
|
+
this.capabilities.management.models ||= Boolean(
|
|
22045
|
+
configOptionByCategory(configOptions, "model")
|
|
22046
|
+
);
|
|
22047
|
+
this.capabilities.controls.performanceMode ||= configOptions.some(
|
|
22048
|
+
(option) => option.id === "fast-mode" || option.category === "model_config"
|
|
22049
|
+
);
|
|
22050
|
+
}
|
|
22051
|
+
async setFastMode(state, enabled) {
|
|
22052
|
+
const option = state.configOptions.find(
|
|
22053
|
+
(candidate) => candidate.id === "fast-mode" || candidate.category === "model_config"
|
|
22054
|
+
);
|
|
22055
|
+
if (!option) {
|
|
22056
|
+
throw new AgentRuntimeError(
|
|
22057
|
+
"The selected ACP agent does not expose fast mode.",
|
|
22058
|
+
"acp",
|
|
22059
|
+
"request_failed"
|
|
22060
|
+
);
|
|
22061
|
+
}
|
|
22062
|
+
const value = option.type === "boolean" ? enabled : allSelectOptions(option).find((candidate) => enabled ? ["on", "true", "fast"].includes(candidate.value.toLowerCase()) : ["off", "false", "standard"].includes(candidate.value.toLowerCase()))?.value;
|
|
22063
|
+
if (value === void 0 || value === option.currentValue) {
|
|
22064
|
+
return;
|
|
22065
|
+
}
|
|
22066
|
+
const response = await (await this.requireContext()).request(
|
|
22067
|
+
methods.agent.session.setConfigOption,
|
|
22068
|
+
option.type === "boolean" ? {
|
|
22069
|
+
sessionId: state.providerSessionId,
|
|
22070
|
+
configId: option.id,
|
|
22071
|
+
type: "boolean",
|
|
22072
|
+
value
|
|
22073
|
+
} : {
|
|
22074
|
+
sessionId: state.providerSessionId,
|
|
22075
|
+
configId: option.id,
|
|
22076
|
+
value
|
|
22077
|
+
}
|
|
22078
|
+
);
|
|
22079
|
+
state.configOptions = response.configOptions;
|
|
22080
|
+
this.updateCapabilitiesFromConfigOptions(state.configOptions);
|
|
21167
22081
|
}
|
|
21168
22082
|
handleSessionUpdate(notification) {
|
|
21169
22083
|
const state = this.sessions.get(notification.sessionId);
|
|
@@ -21172,8 +22086,10 @@ ${input.prompt}` : input.prompt;
|
|
|
21172
22086
|
}
|
|
21173
22087
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21174
22088
|
const update = notification.update;
|
|
22089
|
+
const hydrator = this.hydrators.get(notification.sessionId);
|
|
21175
22090
|
if (update.sessionUpdate === "config_option_update") {
|
|
21176
22091
|
state.configOptions = update.configOptions;
|
|
22092
|
+
this.syncStateFromConfigOptions(state);
|
|
21177
22093
|
} else if (update.sessionUpdate === "current_mode_update" && state.modes) {
|
|
21178
22094
|
state.modes = { ...state.modes, currentModeId: update.currentModeId };
|
|
21179
22095
|
} else if (update.sessionUpdate === "available_commands_update") {
|
|
@@ -21181,7 +22097,7 @@ ${input.prompt}` : input.prompt;
|
|
|
21181
22097
|
} else if (update.sessionUpdate === "session_info_update") {
|
|
21182
22098
|
if (update.title !== void 0) {
|
|
21183
22099
|
state.title = update.title;
|
|
21184
|
-
if (update.title) {
|
|
22100
|
+
if (update.title && !hydrator) {
|
|
21185
22101
|
this.emitRuntimeEvent({
|
|
21186
22102
|
type: "session.title.updated",
|
|
21187
22103
|
provider: "acp",
|
|
@@ -21190,6 +22106,30 @@ ${input.prompt}` : input.prompt;
|
|
|
21190
22106
|
});
|
|
21191
22107
|
}
|
|
21192
22108
|
}
|
|
22109
|
+
const goal = this.goalFromSessionInfoUpdate(
|
|
22110
|
+
state.providerSessionId,
|
|
22111
|
+
update
|
|
22112
|
+
);
|
|
22113
|
+
if (goal !== void 0) {
|
|
22114
|
+
state.goal = goal;
|
|
22115
|
+
if (!hydrator) {
|
|
22116
|
+
this.emitRuntimeEvent(goal ? {
|
|
22117
|
+
type: "goal.updated",
|
|
22118
|
+
provider: "acp",
|
|
22119
|
+
providerSessionId: state.providerSessionId,
|
|
22120
|
+
providerTurnId: state.activeMapper?.turnId ?? null,
|
|
22121
|
+
goal
|
|
22122
|
+
} : {
|
|
22123
|
+
type: "goal.cleared",
|
|
22124
|
+
provider: "acp",
|
|
22125
|
+
providerSessionId: state.providerSessionId
|
|
22126
|
+
});
|
|
22127
|
+
}
|
|
22128
|
+
}
|
|
22129
|
+
}
|
|
22130
|
+
if (hydrator) {
|
|
22131
|
+
hydrator.apply(update);
|
|
22132
|
+
return;
|
|
21193
22133
|
}
|
|
21194
22134
|
const mapper = state.activeMapper;
|
|
21195
22135
|
if (!mapper) {
|
|
@@ -21328,10 +22268,17 @@ ${input.prompt}` : input.prompt;
|
|
|
21328
22268
|
});
|
|
21329
22269
|
}
|
|
21330
22270
|
async readTextFile(params) {
|
|
21331
|
-
|
|
21332
|
-
|
|
22271
|
+
const session = this.sessions.get(params.sessionId);
|
|
22272
|
+
if (!session) {
|
|
22273
|
+
throw new Error(`ACP session workspace not found: ${params.sessionId}`);
|
|
21333
22274
|
}
|
|
21334
|
-
const
|
|
22275
|
+
const filePath = await resolveAcpWorkspacePath(session.cwd, params.path);
|
|
22276
|
+
this.emit("fs-operation", {
|
|
22277
|
+
operation: "fs.readTextFile",
|
|
22278
|
+
sessionId: params.sessionId,
|
|
22279
|
+
path: filePath
|
|
22280
|
+
});
|
|
22281
|
+
const content = await fs13.readFile(filePath, "utf8");
|
|
21335
22282
|
if (params.line === void 0 && params.limit === void 0) {
|
|
21336
22283
|
return { content };
|
|
21337
22284
|
}
|
|
@@ -21341,11 +22288,18 @@ ${input.prompt}` : input.prompt;
|
|
|
21341
22288
|
return { content: lines.slice(start, end).join("\n") };
|
|
21342
22289
|
}
|
|
21343
22290
|
async writeTextFile(params) {
|
|
21344
|
-
|
|
21345
|
-
|
|
22291
|
+
const session = this.sessions.get(params.sessionId);
|
|
22292
|
+
if (!session) {
|
|
22293
|
+
throw new Error(`ACP session workspace not found: ${params.sessionId}`);
|
|
21346
22294
|
}
|
|
21347
|
-
await
|
|
21348
|
-
|
|
22295
|
+
const filePath = await resolveAcpWorkspacePath(session.cwd, params.path);
|
|
22296
|
+
this.emit("fs-operation", {
|
|
22297
|
+
operation: "fs.writeTextFile",
|
|
22298
|
+
sessionId: params.sessionId,
|
|
22299
|
+
path: filePath
|
|
22300
|
+
});
|
|
22301
|
+
await fs13.mkdir(path16.dirname(filePath), { recursive: true });
|
|
22302
|
+
await fs13.writeFile(filePath, params.content, "utf8");
|
|
21349
22303
|
return {};
|
|
21350
22304
|
}
|
|
21351
22305
|
async requireContext() {
|
|
@@ -21356,9 +22310,207 @@ ${input.prompt}` : input.prompt;
|
|
|
21356
22310
|
return this.context;
|
|
21357
22311
|
}
|
|
21358
22312
|
applyAgentCapabilities(capabilities) {
|
|
21359
|
-
this.capabilities
|
|
21360
|
-
|
|
21361
|
-
|
|
22313
|
+
applyNegotiatedAcpCapabilities(this.capabilities, capabilities);
|
|
22314
|
+
}
|
|
22315
|
+
resetCapabilities() {
|
|
22316
|
+
const baseline = cloneCapabilities();
|
|
22317
|
+
for (const section of Object.keys(baseline)) {
|
|
22318
|
+
Object.assign(this.capabilities[section], baseline[section]);
|
|
22319
|
+
}
|
|
22320
|
+
}
|
|
22321
|
+
goalFromSessionInfoUpdate(providerSessionId, update) {
|
|
22322
|
+
const meta = isRecord13(update._meta) ? update._meta : null;
|
|
22323
|
+
if (!meta || !Object.hasOwn(meta, "goal")) {
|
|
22324
|
+
return void 0;
|
|
22325
|
+
}
|
|
22326
|
+
if (meta.goal === null) {
|
|
22327
|
+
return null;
|
|
22328
|
+
}
|
|
22329
|
+
if (!isRecord13(meta.goal)) {
|
|
22330
|
+
return void 0;
|
|
22331
|
+
}
|
|
22332
|
+
const objective = typeof meta.goal.objective === "string" ? meta.goal.objective.trim() : "";
|
|
22333
|
+
if (!objective) {
|
|
22334
|
+
return void 0;
|
|
22335
|
+
}
|
|
22336
|
+
return {
|
|
22337
|
+
providerSessionId,
|
|
22338
|
+
objective,
|
|
22339
|
+
status: typeof meta.goal.status === "string" ? meta.goal.status : "active",
|
|
22340
|
+
tokenBudget: typeof meta.goal.tokenBudget === "number" ? meta.goal.tokenBudget : null,
|
|
22341
|
+
tokensUsed: typeof meta.goal.tokensUsed === "number" ? meta.goal.tokensUsed : 0,
|
|
22342
|
+
timeUsedSeconds: typeof meta.goal.timeUsedSeconds === "number" ? meta.goal.timeUsedSeconds : 0,
|
|
22343
|
+
createdAt: typeof meta.goal.createdAt === "number" ? meta.goal.createdAt : Date.now(),
|
|
22344
|
+
updatedAt: typeof meta.goal.updatedAt === "number" ? meta.goal.updatedAt : Date.now(),
|
|
22345
|
+
rawGoal: structuredClone(meta.goal)
|
|
22346
|
+
};
|
|
22347
|
+
}
|
|
22348
|
+
async invokeAcpGoal(providerSessionId, action) {
|
|
22349
|
+
if (!this.extensionRegistry.supports("acp.goal", 1, action.action)) {
|
|
22350
|
+
throw new AgentRuntimeError(
|
|
22351
|
+
"The selected ACP agent does not expose goal control.",
|
|
22352
|
+
"acp",
|
|
22353
|
+
"request_failed"
|
|
22354
|
+
);
|
|
22355
|
+
}
|
|
22356
|
+
const operationId = randomUUID4();
|
|
22357
|
+
await this.extensionRegistry.invoke({
|
|
22358
|
+
extensionId: "acp.goal",
|
|
22359
|
+
extensionVersion: 1,
|
|
22360
|
+
method: action.action,
|
|
22361
|
+
operationId,
|
|
22362
|
+
idempotencyKey: `${providerSessionId}:goal:${operationId}`,
|
|
22363
|
+
params: { providerSessionId, ...action },
|
|
22364
|
+
timeoutMs: 18e4
|
|
22365
|
+
});
|
|
22366
|
+
}
|
|
22367
|
+
async runControlPrompt(providerSessionId, prompt, signal) {
|
|
22368
|
+
const state = this.sessions.get(providerSessionId);
|
|
22369
|
+
if (!state || state.activeMapper) {
|
|
22370
|
+
throw new AgentRuntimeError(
|
|
22371
|
+
"ACP control prompt requires an idle loaded session.",
|
|
22372
|
+
"acp",
|
|
22373
|
+
"request_failed"
|
|
22374
|
+
);
|
|
22375
|
+
}
|
|
22376
|
+
let startedTurnId = null;
|
|
22377
|
+
let cleanupCompletion;
|
|
22378
|
+
const completed = new Promise(
|
|
22379
|
+
(resolve, reject) => {
|
|
22380
|
+
const cleanup = () => {
|
|
22381
|
+
this.off("event", onEvent);
|
|
22382
|
+
signal.removeEventListener("abort", onAbort);
|
|
22383
|
+
};
|
|
22384
|
+
const onEvent = (event) => {
|
|
22385
|
+
if (event.type === "turn.completed" && event.providerSessionId === providerSessionId && (!startedTurnId || event.turn.providerTurnId === startedTurnId)) {
|
|
22386
|
+
cleanup();
|
|
22387
|
+
resolve(event);
|
|
22388
|
+
}
|
|
22389
|
+
};
|
|
22390
|
+
const onAbort = () => {
|
|
22391
|
+
cleanup();
|
|
22392
|
+
reject(signal.reason ?? new Error("ACP control prompt cancelled."));
|
|
22393
|
+
};
|
|
22394
|
+
cleanupCompletion = cleanup;
|
|
22395
|
+
this.on("event", onEvent);
|
|
22396
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
22397
|
+
}
|
|
22398
|
+
);
|
|
22399
|
+
try {
|
|
22400
|
+
const turn = await this.startTurn({
|
|
22401
|
+
providerSessionId,
|
|
22402
|
+
prompt,
|
|
22403
|
+
hidden: true
|
|
22404
|
+
});
|
|
22405
|
+
startedTurnId = turn.providerTurnId;
|
|
22406
|
+
const event = await completed;
|
|
22407
|
+
if (event.turn.status === "failed") {
|
|
22408
|
+
throw new Error(event.turn.error?.message ?? "ACP control prompt failed.");
|
|
22409
|
+
}
|
|
22410
|
+
return { providerTurnId: event.turn.providerTurnId, status: event.turn.status };
|
|
22411
|
+
} catch (error) {
|
|
22412
|
+
cleanupCompletion();
|
|
22413
|
+
throw error;
|
|
22414
|
+
}
|
|
22415
|
+
}
|
|
22416
|
+
registerNegotiatedExtensions(response) {
|
|
22417
|
+
const snapshot = snapshotAcpInitializeResponse(response);
|
|
22418
|
+
if (!snapshot || !this.context) {
|
|
22419
|
+
return;
|
|
22420
|
+
}
|
|
22421
|
+
const wireTransport = {
|
|
22422
|
+
request: (method, params, signal) => this.context.request(method, params, {
|
|
22423
|
+
cancellationSignal: signal
|
|
22424
|
+
})
|
|
22425
|
+
};
|
|
22426
|
+
for (const descriptor of snapshot.harnessExtensions) {
|
|
22427
|
+
this.extensionRegistry.register({
|
|
22428
|
+
ownerId: "acp-agent",
|
|
22429
|
+
descriptor,
|
|
22430
|
+
transport: wireTransport
|
|
22431
|
+
});
|
|
22432
|
+
}
|
|
22433
|
+
if (snapshot.legacyExtensions.steering?.supported) {
|
|
22434
|
+
this.extensionRegistry.register({
|
|
22435
|
+
ownerId: "acp-agent",
|
|
22436
|
+
descriptor: {
|
|
22437
|
+
id: "acp.steering",
|
|
22438
|
+
version: 1,
|
|
22439
|
+
stability: "experimental",
|
|
22440
|
+
methods: ["steer"],
|
|
22441
|
+
events: []
|
|
22442
|
+
},
|
|
22443
|
+
transport: wireTransport,
|
|
22444
|
+
wireMethods: { steer: "_session/steering" },
|
|
22445
|
+
paramMappers: {
|
|
22446
|
+
steer: (envelope) => {
|
|
22447
|
+
const params = isRecord13(envelope.params) ? envelope.params : {};
|
|
22448
|
+
return {
|
|
22449
|
+
sessionId: params.providerSessionId,
|
|
22450
|
+
prompt: params.prompt
|
|
22451
|
+
};
|
|
22452
|
+
}
|
|
22453
|
+
},
|
|
22454
|
+
capabilityPatch: { turns: { steer: true } }
|
|
22455
|
+
});
|
|
22456
|
+
}
|
|
22457
|
+
const goal = snapshot.legacyExtensions.goal;
|
|
22458
|
+
const goalVersion = typeof goal?.version === "number" ? goal.version : goal?.version === "1" ? 1 : null;
|
|
22459
|
+
if (goal?.controlMethod && goalVersion === 1 && goal.actions.includes("set") && goal.actions.includes("clear")) {
|
|
22460
|
+
const actions = goal.actions.filter((action) => ["set", "pause", "resume", "clear"].includes(action));
|
|
22461
|
+
this.extensionRegistry.register({
|
|
22462
|
+
ownerId: "acp-agent",
|
|
22463
|
+
descriptor: {
|
|
22464
|
+
id: "acp.goal",
|
|
22465
|
+
version: goalVersion,
|
|
22466
|
+
stability: "experimental",
|
|
22467
|
+
methods: actions,
|
|
22468
|
+
events: []
|
|
22469
|
+
},
|
|
22470
|
+
transport: wireTransport,
|
|
22471
|
+
wireMethods: Object.fromEntries(actions.map((action) => [action, goal.controlMethod])),
|
|
22472
|
+
paramMappers: Object.fromEntries(actions.map((action) => [
|
|
22473
|
+
action,
|
|
22474
|
+
(envelope) => {
|
|
22475
|
+
const params = isRecord13(envelope.params) ? envelope.params : {};
|
|
22476
|
+
return {
|
|
22477
|
+
sessionId: params.providerSessionId,
|
|
22478
|
+
action,
|
|
22479
|
+
...action === "set" ? { objective: params.objective } : {}
|
|
22480
|
+
};
|
|
22481
|
+
}
|
|
22482
|
+
])),
|
|
22483
|
+
capabilityPatch: { controls: { goals: true } }
|
|
22484
|
+
});
|
|
22485
|
+
}
|
|
22486
|
+
if (snapshot.agentInfo?.name === "@agentclientprotocol/codex-acp") {
|
|
22487
|
+
this.extensionRegistry.register({
|
|
22488
|
+
ownerId: "acp-agent",
|
|
22489
|
+
descriptor: {
|
|
22490
|
+
id: "codex.control",
|
|
22491
|
+
version: 1,
|
|
22492
|
+
stability: "experimental",
|
|
22493
|
+
methods: ["compact"],
|
|
22494
|
+
events: []
|
|
22495
|
+
},
|
|
22496
|
+
transport: {
|
|
22497
|
+
request: async (_method, params, signal) => {
|
|
22498
|
+
const providerSessionId = isRecord13(params) ? String(params.providerSessionId ?? "") : "";
|
|
22499
|
+
return this.runControlPrompt(providerSessionId, "/compact", signal);
|
|
22500
|
+
}
|
|
22501
|
+
},
|
|
22502
|
+
paramMappers: {
|
|
22503
|
+
compact: (envelope) => ({
|
|
22504
|
+
providerSessionId: isRecord13(envelope.params) ? envelope.params.providerSessionId : null
|
|
22505
|
+
})
|
|
22506
|
+
},
|
|
22507
|
+
capabilityPatch: { turns: { compact: true } }
|
|
22508
|
+
});
|
|
22509
|
+
}
|
|
22510
|
+
const effective = this.extensionRegistry.effectiveCapabilities(this.capabilities);
|
|
22511
|
+
for (const section of Object.keys(effective)) {
|
|
22512
|
+
Object.assign(this.capabilities[section], effective[section]);
|
|
22513
|
+
}
|
|
21362
22514
|
}
|
|
21363
22515
|
withStartupTimeout(promise) {
|
|
21364
22516
|
const timeoutMs = this.options.startupTimeoutMs ?? 1e4;
|
|
@@ -21381,6 +22533,8 @@ ${input.prompt}` : input.prompt;
|
|
|
21381
22533
|
}
|
|
21382
22534
|
markFailed(error) {
|
|
21383
22535
|
const message = errorMessage3(error);
|
|
22536
|
+
this.settleActiveTurns("failed", message);
|
|
22537
|
+
this.cancelPendingPermissions();
|
|
21384
22538
|
this.status = {
|
|
21385
22539
|
...this.status,
|
|
21386
22540
|
state: "failed",
|
|
@@ -21389,14 +22543,49 @@ ${input.prompt}` : input.prompt;
|
|
|
21389
22543
|
this.installation.lastError = message;
|
|
21390
22544
|
this.emit("status", this.getStatus());
|
|
21391
22545
|
}
|
|
22546
|
+
cancelPendingPermissions() {
|
|
22547
|
+
for (const [id, permission] of this.pendingPermissions) {
|
|
22548
|
+
clearTimeout(permission.timer);
|
|
22549
|
+
permission.resolve(cancelledPermission());
|
|
22550
|
+
this.pendingPermissions.delete(id);
|
|
22551
|
+
}
|
|
22552
|
+
}
|
|
22553
|
+
settleActiveTurns(status, error) {
|
|
22554
|
+
for (const state of this.sessions.values()) {
|
|
22555
|
+
const mapper = state.activeMapper;
|
|
22556
|
+
if (!mapper) {
|
|
22557
|
+
continue;
|
|
22558
|
+
}
|
|
22559
|
+
const completed = mapper.complete(status, error);
|
|
22560
|
+
for (const itemUpdate of completed.updates) {
|
|
22561
|
+
this.emitItemUpdate(state, mapper.turnId, itemUpdate);
|
|
22562
|
+
}
|
|
22563
|
+
const turn = {
|
|
22564
|
+
...completed.turn,
|
|
22565
|
+
startedAt: state.turns.find(
|
|
22566
|
+
(candidate) => candidate.providerTurnId === mapper.turnId
|
|
22567
|
+
)?.startedAt ?? null
|
|
22568
|
+
};
|
|
22569
|
+
this.replaceTurn(state, turn);
|
|
22570
|
+
state.activeMapper = null;
|
|
22571
|
+
state.status = status;
|
|
22572
|
+
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22573
|
+
this.emitRuntimeEvent({
|
|
22574
|
+
type: "turn.completed",
|
|
22575
|
+
provider: "acp",
|
|
22576
|
+
providerSessionId: state.providerSessionId,
|
|
22577
|
+
turn
|
|
22578
|
+
});
|
|
22579
|
+
}
|
|
22580
|
+
}
|
|
21392
22581
|
emitRuntimeEvent(event) {
|
|
21393
22582
|
this.emit("event", event);
|
|
21394
22583
|
}
|
|
21395
22584
|
};
|
|
21396
22585
|
|
|
21397
22586
|
// ../../packages/acp/src/codex-environment.ts
|
|
21398
|
-
import
|
|
21399
|
-
import
|
|
22587
|
+
import fs14 from "fs/promises";
|
|
22588
|
+
import path17 from "path";
|
|
21400
22589
|
|
|
21401
22590
|
// ../../node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/date.js
|
|
21402
22591
|
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
@@ -22060,7 +23249,7 @@ async function loadCodexAcpEnvironment(codexHome, inheritedEnv = process.env, co
|
|
|
22060
23249
|
return env;
|
|
22061
23250
|
}
|
|
22062
23251
|
try {
|
|
22063
|
-
const source = await
|
|
23252
|
+
const source = await fs14.readFile(path17.join(normalizedHome, "config.toml"), "utf8");
|
|
22064
23253
|
env.CODEX_CONFIG = JSON.stringify(parse(source));
|
|
22065
23254
|
} catch {
|
|
22066
23255
|
}
|
|
@@ -22113,7 +23302,12 @@ var catalogCapabilities = {
|
|
|
22113
23302
|
};
|
|
22114
23303
|
var catalogManagementSchema = {
|
|
22115
23304
|
hostConfigFiles: [],
|
|
22116
|
-
toolboxItems: [
|
|
23305
|
+
toolboxItems: [
|
|
23306
|
+
{ action: "fast", command: "/fast", label: "Fast mode" },
|
|
23307
|
+
{ action: "compact", command: "/compact", label: "Compact context" },
|
|
23308
|
+
{ action: "goal", command: "/goal", label: "Goal" },
|
|
23309
|
+
{ action: "fork", command: "/fork", label: "Fork" }
|
|
23310
|
+
],
|
|
22117
23311
|
hookCommandTemplates: [],
|
|
22118
23312
|
providerConfigFormat: "none",
|
|
22119
23313
|
mcpConfigFormat: "none",
|
|
@@ -22137,12 +23331,14 @@ function decodeScopedId(value) {
|
|
|
22137
23331
|
function scopedSession(agentId, session) {
|
|
22138
23332
|
return {
|
|
22139
23333
|
...session,
|
|
23334
|
+
agentId,
|
|
22140
23335
|
providerSessionId: encodeSessionId(agentId, session.providerSessionId)
|
|
22141
23336
|
};
|
|
22142
23337
|
}
|
|
22143
23338
|
function scopedSummary(agentId, session) {
|
|
22144
23339
|
return {
|
|
22145
23340
|
...session,
|
|
23341
|
+
agentId,
|
|
22146
23342
|
providerSessionId: encodeSessionId(agentId, session.providerSessionId)
|
|
22147
23343
|
};
|
|
22148
23344
|
}
|
|
@@ -22151,7 +23347,7 @@ function delegatedSessionInput(input) {
|
|
|
22151
23347
|
delete delegated.agentId;
|
|
22152
23348
|
return delegated;
|
|
22153
23349
|
}
|
|
22154
|
-
var AcpCatalogRuntimeAdapter = class extends
|
|
23350
|
+
var AcpCatalogRuntimeAdapter = class extends EventEmitter9 {
|
|
22155
23351
|
constructor(options = {}) {
|
|
22156
23352
|
super();
|
|
22157
23353
|
this.options = options;
|
|
@@ -22167,8 +23363,8 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22167
23363
|
managementSchema = catalogManagementSchema;
|
|
22168
23364
|
installation = {
|
|
22169
23365
|
packageName: null,
|
|
22170
|
-
installed:
|
|
22171
|
-
installedVersion:
|
|
23366
|
+
installed: true,
|
|
23367
|
+
installedVersion: "Built in \xB7 0 ACP agents ready",
|
|
22172
23368
|
latestVersion: null,
|
|
22173
23369
|
installCommand: null,
|
|
22174
23370
|
updateCommand: null,
|
|
@@ -22178,6 +23374,11 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22178
23374
|
catalog;
|
|
22179
23375
|
agents = /* @__PURE__ */ new Map();
|
|
22180
23376
|
modelCache = /* @__PURE__ */ new Map();
|
|
23377
|
+
operationalMetrics = {
|
|
23378
|
+
sessionStartFailures: 0,
|
|
23379
|
+
resumeFailures: 0,
|
|
23380
|
+
capabilityProbeFailures: 0
|
|
23381
|
+
};
|
|
22181
23382
|
status = {
|
|
22182
23383
|
state: "stopped",
|
|
22183
23384
|
transport: "stdio",
|
|
@@ -22186,7 +23387,10 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22186
23387
|
restartCount: 0
|
|
22187
23388
|
};
|
|
22188
23389
|
getStatus() {
|
|
22189
|
-
return {
|
|
23390
|
+
return {
|
|
23391
|
+
...this.status,
|
|
23392
|
+
operationalMetrics: { ...this.operationalMetrics }
|
|
23393
|
+
};
|
|
22190
23394
|
}
|
|
22191
23395
|
async start() {
|
|
22192
23396
|
this.status = {
|
|
@@ -22201,6 +23405,7 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22201
23405
|
async stop() {
|
|
22202
23406
|
await Promise.allSettled([...this.agents.values()].map((agent) => agent.stop()));
|
|
22203
23407
|
this.agents.clear();
|
|
23408
|
+
this.recomputeAgentCapabilities();
|
|
22204
23409
|
this.status = {
|
|
22205
23410
|
...this.status,
|
|
22206
23411
|
state: "stopped",
|
|
@@ -22228,21 +23433,58 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22228
23433
|
}));
|
|
22229
23434
|
}
|
|
22230
23435
|
async listModelsForAgent(agentId, cwd) {
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
23436
|
+
try {
|
|
23437
|
+
const cacheKey = `${agentId}\0${cwd}`;
|
|
23438
|
+
const cached = this.modelCache.get(cacheKey);
|
|
23439
|
+
if (cached && Date.now() - cached.at < 3e4) {
|
|
23440
|
+
return cached.models;
|
|
23441
|
+
}
|
|
23442
|
+
const agent = await this.agentFor(agentId);
|
|
23443
|
+
let models = await agent.inspectModelOptions(cwd);
|
|
23444
|
+
if (agent.capabilities.controls.performanceMode) {
|
|
23445
|
+
models = models.map((model) => ({
|
|
23446
|
+
...model,
|
|
23447
|
+
supportsPerformanceMode: true
|
|
23448
|
+
}));
|
|
23449
|
+
}
|
|
23450
|
+
if (models.length === 1 && models[0]?.model === "default") {
|
|
23451
|
+
const commandModels = await this.catalog.listCommandModels(agentId);
|
|
23452
|
+
if (commandModels.length > 0) {
|
|
23453
|
+
models = commandModels;
|
|
23454
|
+
}
|
|
22242
23455
|
}
|
|
23456
|
+
this.modelCache.set(cacheKey, { at: Date.now(), models });
|
|
23457
|
+
return models;
|
|
23458
|
+
} catch (error) {
|
|
23459
|
+
this.operationalMetrics.capabilityProbeFailures += 1;
|
|
23460
|
+
throw error;
|
|
22243
23461
|
}
|
|
22244
|
-
|
|
22245
|
-
|
|
23462
|
+
}
|
|
23463
|
+
async getAgentCapabilitySnapshot(agentId) {
|
|
23464
|
+
const entry = (await this.refreshCatalog()).find((candidate) => candidate.id === agentId);
|
|
23465
|
+
if (!entry) {
|
|
23466
|
+
throw new AgentRuntimeError(`Unknown ACP agent: ${agentId}`, "acp", "request_failed");
|
|
23467
|
+
}
|
|
23468
|
+
if (entry.availability !== "ready") {
|
|
23469
|
+
return snapshotAcpAgentCapabilities({
|
|
23470
|
+
agentId,
|
|
23471
|
+
availability: entry.availability,
|
|
23472
|
+
effectiveCapabilities: this.capabilities
|
|
23473
|
+
});
|
|
23474
|
+
}
|
|
23475
|
+
const agent = await this.agentFor(agentId);
|
|
23476
|
+
return snapshotAcpAgentCapabilities({
|
|
23477
|
+
agentId,
|
|
23478
|
+
availability: entry.availability,
|
|
23479
|
+
negotiated: agent.getProtocolSnapshot(),
|
|
23480
|
+
effectiveCapabilities: agent.capabilities
|
|
23481
|
+
});
|
|
23482
|
+
}
|
|
23483
|
+
getScopedCapabilities(input) {
|
|
23484
|
+
const sessionOwner = input.providerSessionId ? decodeScopedId(input.providerSessionId) : null;
|
|
23485
|
+
const agentId = sessionOwner?.agentId ?? input.agentId ?? null;
|
|
23486
|
+
const child = agentId ? this.agents.get(agentId) : null;
|
|
23487
|
+
return structuredClone(child?.capabilities ?? catalogCapabilities);
|
|
22246
23488
|
}
|
|
22247
23489
|
async installModel(modelId) {
|
|
22248
23490
|
await this.catalog.installAdapter(modelId);
|
|
@@ -22256,6 +23498,23 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22256
23498
|
);
|
|
22257
23499
|
return sessions.flat();
|
|
22258
23500
|
}
|
|
23501
|
+
async listImportSessions(agentId) {
|
|
23502
|
+
const entries = agentId ? (await this.refreshCatalog()).filter((entry) => entry.id === agentId) : [];
|
|
23503
|
+
const sessions = await Promise.all(
|
|
23504
|
+
entries.map(async (entry) => {
|
|
23505
|
+
if (entry.availability !== "ready") {
|
|
23506
|
+
return [];
|
|
23507
|
+
}
|
|
23508
|
+
try {
|
|
23509
|
+
const agent = await this.agentFor(entry.id);
|
|
23510
|
+
return (await agent.listSessions()).map((session) => scopedSummary(entry.id, session));
|
|
23511
|
+
} catch {
|
|
23512
|
+
return [];
|
|
23513
|
+
}
|
|
23514
|
+
})
|
|
23515
|
+
);
|
|
23516
|
+
return sessions.flat();
|
|
23517
|
+
}
|
|
22259
23518
|
async listLoadedSessions() {
|
|
22260
23519
|
const sessions = await Promise.all(
|
|
22261
23520
|
[...this.agents.entries()].map(
|
|
@@ -22280,25 +23539,43 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22280
23539
|
if (!agentId) {
|
|
22281
23540
|
throw new AgentRuntimeError("Select an ACP agent before creating the thread.", "acp");
|
|
22282
23541
|
}
|
|
22283
|
-
|
|
22284
|
-
|
|
23542
|
+
let agent;
|
|
23543
|
+
let response;
|
|
23544
|
+
try {
|
|
23545
|
+
agent = await this.agentFor(agentId);
|
|
23546
|
+
response = await agent.startSession(delegatedSessionInput(input));
|
|
23547
|
+
} catch (error) {
|
|
23548
|
+
this.operationalMetrics.sessionStartFailures += 1;
|
|
23549
|
+
throw error;
|
|
23550
|
+
}
|
|
23551
|
+
const probedDefaultModel = this.modelCache.get(`${agentId}\0${input.cwd}`)?.models.find((model) => model.isDefault)?.model ?? null;
|
|
23552
|
+
const resolvedModel = response.model && response.model !== "default" ? response.model : input.model !== "default" ? input.model : probedDefaultModel ?? input.model;
|
|
23553
|
+
this.refreshAgentSessionCapabilities(agentId, agent);
|
|
22285
23554
|
const session = scopedSession(agentId, response.session);
|
|
22286
23555
|
return {
|
|
22287
23556
|
...response,
|
|
22288
23557
|
agentId,
|
|
22289
23558
|
providerSessionId: session.providerSessionId,
|
|
22290
|
-
model:
|
|
23559
|
+
model: resolvedModel,
|
|
22291
23560
|
reasoningEffort: response.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
22292
23561
|
session
|
|
22293
23562
|
};
|
|
22294
23563
|
}
|
|
22295
23564
|
async resumeSession(input) {
|
|
22296
23565
|
const owner = this.requireSessionOwner(input.providerSessionId);
|
|
22297
|
-
|
|
22298
|
-
|
|
22299
|
-
|
|
22300
|
-
|
|
22301
|
-
|
|
23566
|
+
let agent;
|
|
23567
|
+
let response;
|
|
23568
|
+
try {
|
|
23569
|
+
agent = await this.agentFor(owner.agentId);
|
|
23570
|
+
response = await agent.resumeSession({
|
|
23571
|
+
...input,
|
|
23572
|
+
providerSessionId: owner.rawId
|
|
23573
|
+
});
|
|
23574
|
+
} catch (error) {
|
|
23575
|
+
this.operationalMetrics.resumeFailures += 1;
|
|
23576
|
+
throw error;
|
|
23577
|
+
}
|
|
23578
|
+
this.refreshAgentSessionCapabilities(owner.agentId, agent);
|
|
22302
23579
|
const session = scopedSession(owner.agentId, response.session);
|
|
22303
23580
|
return {
|
|
22304
23581
|
...response,
|
|
@@ -22324,15 +23601,63 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22324
23601
|
providerSessionId: owner.rawId
|
|
22325
23602
|
});
|
|
22326
23603
|
}
|
|
23604
|
+
async sendInput(input) {
|
|
23605
|
+
const owner = this.requireSessionOwner(input.providerSessionId);
|
|
23606
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23607
|
+
return agent.sendInput({
|
|
23608
|
+
...input,
|
|
23609
|
+
providerSessionId: owner.rawId
|
|
23610
|
+
});
|
|
23611
|
+
}
|
|
23612
|
+
async compactSession(providerSessionId) {
|
|
23613
|
+
const owner = this.requireSessionOwner(providerSessionId);
|
|
23614
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23615
|
+
return agent.compactSession(owner.rawId);
|
|
23616
|
+
}
|
|
23617
|
+
async forkSession(input) {
|
|
23618
|
+
const owner = this.requireSessionOwner(input.providerSessionId);
|
|
23619
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23620
|
+
return scopedSession(owner.agentId, await agent.forkSession({
|
|
23621
|
+
...input,
|
|
23622
|
+
providerSessionId: owner.rawId
|
|
23623
|
+
}));
|
|
23624
|
+
}
|
|
23625
|
+
async getGoal(providerSessionId) {
|
|
23626
|
+
const owner = this.requireSessionOwner(providerSessionId);
|
|
23627
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23628
|
+
return agent.getGoal(owner.rawId);
|
|
23629
|
+
}
|
|
23630
|
+
async setGoal(input) {
|
|
23631
|
+
const owner = this.requireSessionOwner(input.providerSessionId);
|
|
23632
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23633
|
+
const goal = await agent.setGoal({
|
|
23634
|
+
...input,
|
|
23635
|
+
providerSessionId: owner.rawId
|
|
23636
|
+
});
|
|
23637
|
+
return {
|
|
23638
|
+
...goal,
|
|
23639
|
+
providerSessionId: encodeSessionId(owner.agentId, goal.providerSessionId)
|
|
23640
|
+
};
|
|
23641
|
+
}
|
|
23642
|
+
async clearGoal(providerSessionId) {
|
|
23643
|
+
const owner = this.requireSessionOwner(providerSessionId);
|
|
23644
|
+
const agent = await this.agentFor(owner.agentId);
|
|
23645
|
+
return agent.clearGoal(owner.rawId);
|
|
23646
|
+
}
|
|
22327
23647
|
mapProviderRequest(request, options) {
|
|
22328
23648
|
const owner = decodeScopedId(request.id);
|
|
22329
23649
|
const agent = owner ? this.agents.get(owner.agentId) : null;
|
|
22330
23650
|
if (!owner || !agent) {
|
|
22331
23651
|
return null;
|
|
22332
23652
|
}
|
|
23653
|
+
const scopedProviderSessionId = request.params && typeof request.params === "object" ? String(request.params.sessionId ?? "") : "";
|
|
23654
|
+
const sessionOwner = decodeScopedId(scopedProviderSessionId);
|
|
23655
|
+
if (!sessionOwner || sessionOwner.agentId !== owner.agentId || !sessionOwner.rawId) {
|
|
23656
|
+
return null;
|
|
23657
|
+
}
|
|
22333
23658
|
const params = request.params && typeof request.params === "object" ? {
|
|
22334
23659
|
...request.params,
|
|
22335
|
-
sessionId:
|
|
23660
|
+
sessionId: sessionOwner.rawId
|
|
22336
23661
|
} : request.params;
|
|
22337
23662
|
const mapping = agent.mapProviderRequest({
|
|
22338
23663
|
...request,
|
|
@@ -22377,8 +23702,8 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22377
23702
|
const entries = await this.catalog.list({ force });
|
|
22378
23703
|
const ready = entries.filter((entry) => entry.availability === "ready");
|
|
22379
23704
|
const baseInstalled = entries.filter((entry) => entry.availability !== "base_missing");
|
|
22380
|
-
this.installation.installed =
|
|
22381
|
-
this.installation.installedVersion =
|
|
23705
|
+
this.installation.installed = true;
|
|
23706
|
+
this.installation.installedVersion = `Built in \xB7 ${ready.length} ACP agent${ready.length === 1 ? "" : "s"} ready`;
|
|
22382
23707
|
this.installation.lastError = ready.length > 0 ? null : baseInstalled.length > 0 ? "ACP adapters or native ACP servers are not ready." : "No supported base agent was detected.";
|
|
22383
23708
|
this.status = {
|
|
22384
23709
|
...this.status,
|
|
@@ -22393,6 +23718,7 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22393
23718
|
const existing = this.agents.get(agentId);
|
|
22394
23719
|
if (existing) {
|
|
22395
23720
|
await existing.start();
|
|
23721
|
+
this.mergeAgentCapabilities(existing);
|
|
22396
23722
|
return existing;
|
|
22397
23723
|
}
|
|
22398
23724
|
const entry = (await this.catalog.list()).find((candidate) => candidate.id === agentId);
|
|
@@ -22411,12 +23737,41 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22411
23737
|
this.agents.set(agentId, agent);
|
|
22412
23738
|
try {
|
|
22413
23739
|
await agent.start();
|
|
23740
|
+
this.mergeAgentCapabilities(agent);
|
|
22414
23741
|
return agent;
|
|
22415
23742
|
} catch (error) {
|
|
22416
23743
|
this.agents.delete(agentId);
|
|
22417
23744
|
throw error;
|
|
22418
23745
|
}
|
|
22419
23746
|
}
|
|
23747
|
+
mergeAgentCapabilities(agent) {
|
|
23748
|
+
void agent;
|
|
23749
|
+
this.recomputeAgentCapabilities();
|
|
23750
|
+
}
|
|
23751
|
+
refreshAgentSessionCapabilities(agentId, agent) {
|
|
23752
|
+
for (const key of this.modelCache.keys()) {
|
|
23753
|
+
if (key.startsWith(`${agentId}\0`)) {
|
|
23754
|
+
this.modelCache.delete(key);
|
|
23755
|
+
}
|
|
23756
|
+
}
|
|
23757
|
+
this.mergeAgentCapabilities(agent);
|
|
23758
|
+
}
|
|
23759
|
+
recomputeAgentCapabilities() {
|
|
23760
|
+
const next = structuredClone(catalogCapabilities);
|
|
23761
|
+
for (const child of this.agents.values()) {
|
|
23762
|
+
next.turns.steer ||= child.capabilities.turns.steer;
|
|
23763
|
+
next.turns.compact ||= child.capabilities.turns.compact;
|
|
23764
|
+
next.branching.fork ||= child.capabilities.branching.fork;
|
|
23765
|
+
next.controls.goals ||= child.capabilities.controls.goals;
|
|
23766
|
+
next.controls.performanceMode ||= child.capabilities.controls.performanceMode;
|
|
23767
|
+
next.management.mcpStatus ||= child.capabilities.management.mcpStatus;
|
|
23768
|
+
next.management.skills ||= child.capabilities.management.skills;
|
|
23769
|
+
next.management.hooks ||= child.capabilities.management.hooks;
|
|
23770
|
+
}
|
|
23771
|
+
for (const section of Object.keys(next)) {
|
|
23772
|
+
Object.assign(this.capabilities[section], next[section]);
|
|
23773
|
+
}
|
|
23774
|
+
}
|
|
22420
23775
|
async createAgent(entry) {
|
|
22421
23776
|
const env = entry.id === "codex" ? await loadCodexAcpEnvironment(
|
|
22422
23777
|
this.options.codexHome,
|
|
@@ -22430,9 +23785,16 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22430
23785
|
...this.options.clientInfo !== void 0 ? { clientInfo: this.options.clientInfo } : {}
|
|
22431
23786
|
});
|
|
22432
23787
|
agent.on("event", (event) => {
|
|
23788
|
+
const providerSessionId = encodeSessionId(entry.id, event.providerSessionId);
|
|
22433
23789
|
this.emit("event", {
|
|
22434
23790
|
...event,
|
|
22435
|
-
providerSessionId
|
|
23791
|
+
providerSessionId,
|
|
23792
|
+
...event.type === "goal.updated" ? {
|
|
23793
|
+
goal: {
|
|
23794
|
+
...event.goal,
|
|
23795
|
+
providerSessionId
|
|
23796
|
+
}
|
|
23797
|
+
} : {}
|
|
22436
23798
|
});
|
|
22437
23799
|
});
|
|
22438
23800
|
agent.on("provider-request", (request) => {
|
|
@@ -22467,9 +23829,9 @@ var AcpCatalogRuntimeAdapter = class extends EventEmitter8 {
|
|
|
22467
23829
|
};
|
|
22468
23830
|
|
|
22469
23831
|
// src/e2e-fake-runtime.ts
|
|
22470
|
-
import { EventEmitter as
|
|
23832
|
+
import { EventEmitter as EventEmitter10 } from "events";
|
|
22471
23833
|
import { mkdir, writeFile } from "fs/promises";
|
|
22472
|
-
import
|
|
23834
|
+
import path18 from "path";
|
|
22473
23835
|
var provider = "claude";
|
|
22474
23836
|
var firstDelta = "IOS_STREAM_DELTA_READY";
|
|
22475
23837
|
var secondDelta = " IOS_STREAM_COMPLETED";
|
|
@@ -22481,7 +23843,7 @@ var historyDetailPromptMarker = "IOS_HISTORY_DETAIL";
|
|
|
22481
23843
|
var historyPagePromptMarker = "IOS_HISTORY_PAGE";
|
|
22482
23844
|
var imageAssetPromptMarker = "IOS_IMAGE_ASSET";
|
|
22483
23845
|
var imageAssetPngBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=";
|
|
22484
|
-
var E2EFakeRuntime = class extends
|
|
23846
|
+
var E2EFakeRuntime = class extends EventEmitter10 {
|
|
22485
23847
|
provider = provider;
|
|
22486
23848
|
displayName = "E2E Fake Runtime";
|
|
22487
23849
|
description = "Deterministic runtime for live iOS and web end-to-end tests.";
|
|
@@ -22891,8 +24253,8 @@ var E2EFakeRuntime = class extends EventEmitter9 {
|
|
|
22891
24253
|
}
|
|
22892
24254
|
if (input.prompt.includes(imageAssetPromptMarker)) {
|
|
22893
24255
|
const relativeImagePath = `./.temp/threads/${providerTurnId}/ios-webview-image.png`;
|
|
22894
|
-
const absoluteImagePath =
|
|
22895
|
-
await mkdir(
|
|
24256
|
+
const absoluteImagePath = path18.join(session.cwd, relativeImagePath);
|
|
24257
|
+
await mkdir(path18.dirname(absoluteImagePath), { recursive: true });
|
|
22896
24258
|
await writeFile(absoluteImagePath, Buffer.from(imageAssetPngBase64, "base64"));
|
|
22897
24259
|
const imageItem = {
|
|
22898
24260
|
id: `${providerTurnId}:image-asset`,
|
|
@@ -23029,20 +24391,20 @@ var E2EFakeRuntime = class extends EventEmitter9 {
|
|
|
23029
24391
|
return [];
|
|
23030
24392
|
}
|
|
23031
24393
|
mapProviderRequest(request) {
|
|
23032
|
-
if (request.provider !== provider || !
|
|
24394
|
+
if (request.provider !== provider || !isRecord14(request.params)) {
|
|
23033
24395
|
return null;
|
|
23034
24396
|
}
|
|
23035
|
-
const providerSessionId =
|
|
23036
|
-
const providerTurnId =
|
|
24397
|
+
const providerSessionId = stringValue5(request.params.providerSessionId);
|
|
24398
|
+
const providerTurnId = stringValue5(request.params.providerTurnId);
|
|
23037
24399
|
if (!providerSessionId) {
|
|
23038
24400
|
return null;
|
|
23039
24401
|
}
|
|
23040
24402
|
if (request.method === "item/commandExecution/requestApproval") {
|
|
23041
24403
|
const requestId = String(request.id);
|
|
23042
24404
|
const description = [
|
|
23043
|
-
|
|
23044
|
-
|
|
23045
|
-
|
|
24405
|
+
stringValue5(request.params.reason),
|
|
24406
|
+
stringValue5(request.params.command) ? `Command: ${stringValue5(request.params.command)}` : null,
|
|
24407
|
+
stringValue5(request.params.cwd) ? `CWD: ${stringValue5(request.params.cwd)}` : null
|
|
23046
24408
|
].filter(Boolean).join("\n");
|
|
23047
24409
|
return {
|
|
23048
24410
|
providerRequestId: request.id,
|
|
@@ -23057,7 +24419,7 @@ var E2EFakeRuntime = class extends EventEmitter9 {
|
|
|
23057
24419
|
title: "Command approval required",
|
|
23058
24420
|
description: description || "iOS E2E approval request.",
|
|
23059
24421
|
turnId: providerTurnId,
|
|
23060
|
-
itemId:
|
|
24422
|
+
itemId: stringValue5(request.params.itemId),
|
|
23061
24423
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23062
24424
|
questions: [
|
|
23063
24425
|
{
|
|
@@ -23091,7 +24453,7 @@ var E2EFakeRuntime = class extends EventEmitter9 {
|
|
|
23091
24453
|
title: "Mode",
|
|
23092
24454
|
description: "Which iOS E2E path should continue?",
|
|
23093
24455
|
turnId: providerTurnId,
|
|
23094
|
-
itemId:
|
|
24456
|
+
itemId: stringValue5(request.params.toolUseId),
|
|
23095
24457
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23096
24458
|
questions: [
|
|
23097
24459
|
{
|
|
@@ -23179,10 +24541,10 @@ function isE2EFakeRuntimeEnabled(env = process.env) {
|
|
|
23179
24541
|
const value = env.REMOTE_CODEX_E2E_FAKE_RUNTIME;
|
|
23180
24542
|
return value ? ["1", "true", "yes", "on"].includes(value.toLowerCase()) : false;
|
|
23181
24543
|
}
|
|
23182
|
-
function
|
|
24544
|
+
function isRecord14(value) {
|
|
23183
24545
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23184
24546
|
}
|
|
23185
|
-
function
|
|
24547
|
+
function stringValue5(value) {
|
|
23186
24548
|
return typeof value === "string" ? value : null;
|
|
23187
24549
|
}
|
|
23188
24550
|
function deltasForPrompt(prompt) {
|
|
@@ -23279,8 +24641,8 @@ function createOpenCodeRuntime(config) {
|
|
|
23279
24641
|
}
|
|
23280
24642
|
|
|
23281
24643
|
// src/event-bus.ts
|
|
23282
|
-
import { EventEmitter as
|
|
23283
|
-
var SupervisorEventBus = class extends
|
|
24644
|
+
import { EventEmitter as EventEmitter11 } from "events";
|
|
24645
|
+
var SupervisorEventBus = class extends EventEmitter11 {
|
|
23284
24646
|
emitThreadEvent(event) {
|
|
23285
24647
|
this.emit("thread-event", event);
|
|
23286
24648
|
}
|
|
@@ -23302,7 +24664,7 @@ var SupervisorEventBus = class extends EventEmitter10 {
|
|
|
23302
24664
|
};
|
|
23303
24665
|
|
|
23304
24666
|
// src/thread-service.ts
|
|
23305
|
-
import
|
|
24667
|
+
import fs19 from "fs/promises";
|
|
23306
24668
|
|
|
23307
24669
|
// src/thread-auxiliary-state-store.ts
|
|
23308
24670
|
var ThreadAuxiliaryStateStore = class {
|
|
@@ -23544,7 +24906,8 @@ var ThreadGoalCoordinator = class {
|
|
|
23544
24906
|
});
|
|
23545
24907
|
}
|
|
23546
24908
|
const runtime = this.callbacks.runtimeForProvider(record2.provider);
|
|
23547
|
-
|
|
24909
|
+
const capabilities = await this.callbacks.capabilitiesForRecord(record2);
|
|
24910
|
+
if (!runtime.setGoal || !capabilities.controls.goals) {
|
|
23548
24911
|
throw new HttpError(409, {
|
|
23549
24912
|
code: "conflict",
|
|
23550
24913
|
message: "This backend does not support goals."
|
|
@@ -23621,7 +24984,8 @@ var ThreadGoalCoordinator = class {
|
|
|
23621
24984
|
});
|
|
23622
24985
|
}
|
|
23623
24986
|
const runtime = this.callbacks.runtimeForProvider(record2.provider);
|
|
23624
|
-
|
|
24987
|
+
const capabilities = await this.callbacks.capabilitiesForRecord(record2);
|
|
24988
|
+
if (!runtime.clearGoal || !capabilities.controls.goals) {
|
|
23625
24989
|
throw new HttpError(409, {
|
|
23626
24990
|
code: "conflict",
|
|
23627
24991
|
message: "This backend does not support goals."
|
|
@@ -23647,7 +25011,8 @@ var ThreadGoalCoordinator = class {
|
|
|
23647
25011
|
await this.ensureGoalsFeatureEnabled(record2.provider);
|
|
23648
25012
|
}
|
|
23649
25013
|
const runtime = this.callbacks.runtimeForProvider(record2.provider);
|
|
23650
|
-
|
|
25014
|
+
const capabilities = await this.callbacks.capabilitiesForRecord(record2);
|
|
25015
|
+
if (!runtime.getGoal || !capabilities.controls.goals) {
|
|
23651
25016
|
return null;
|
|
23652
25017
|
}
|
|
23653
25018
|
const goal = await runtime.getGoal(record2.providerSessionId);
|
|
@@ -24609,7 +25974,7 @@ function agentMessageMatchScore(finalText, liveText) {
|
|
|
24609
25974
|
}
|
|
24610
25975
|
|
|
24611
25976
|
// src/thread-usage-accounting.ts
|
|
24612
|
-
function
|
|
25977
|
+
function isRecord15(value) {
|
|
24613
25978
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24614
25979
|
}
|
|
24615
25980
|
function numberOrNull2(value) {
|
|
@@ -24655,11 +26020,11 @@ function computeContextRemainingPercent(tokensInContextWindow, contextWindow) {
|
|
|
24655
26020
|
return clampPercentage(Math.round(remaining / contextWindow * 100));
|
|
24656
26021
|
}
|
|
24657
26022
|
function buildThreadContextUsageFromPayload(payload, model = null, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
24658
|
-
const tokenUsage =
|
|
26023
|
+
const tokenUsage = isRecord15(payload) ? payload : null;
|
|
24659
26024
|
const modelContextWindow = numberOrNull2(
|
|
24660
26025
|
tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
|
|
24661
26026
|
) ?? contextWindowForModel(model);
|
|
24662
|
-
const lastUsage =
|
|
26027
|
+
const lastUsage = isRecord15(tokenUsage?.last) ? tokenUsage.last : null;
|
|
24663
26028
|
const tokensInContextWindow = numberOrNull2(
|
|
24664
26029
|
lastUsage?.totalTokens ?? lastUsage?.total_tokens
|
|
24665
26030
|
);
|
|
@@ -24679,7 +26044,7 @@ function buildThreadContextUsageFromPayload(payload, model = null, timestamp = (
|
|
|
24679
26044
|
}
|
|
24680
26045
|
function mergeThreadContextUsageFromPayload(current, payload, model = null, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
24681
26046
|
const next = buildThreadContextUsageFromPayload(payload, model, timestamp);
|
|
24682
|
-
const payloadRecord =
|
|
26047
|
+
const payloadRecord = isRecord15(payload) ? payload : null;
|
|
24683
26048
|
const reportedModelContextWindow = numberOrNull2(
|
|
24684
26049
|
payloadRecord?.modelContextWindow ?? payloadRecord?.model_context_window
|
|
24685
26050
|
);
|
|
@@ -24705,11 +26070,11 @@ function shouldResetThreadContextUsageForTurnStart(current) {
|
|
|
24705
26070
|
return current?.availability !== "available";
|
|
24706
26071
|
}
|
|
24707
26072
|
function buildTurnTokenBreakdown(payload) {
|
|
24708
|
-
const usage =
|
|
24709
|
-
const inputDetails =
|
|
26073
|
+
const usage = isRecord15(payload) ? payload : null;
|
|
26074
|
+
const inputDetails = isRecord15(
|
|
24710
26075
|
usage?.inputTokensDetails ?? usage?.input_tokens_details
|
|
24711
26076
|
) ? usage?.inputTokensDetails ?? usage?.input_tokens_details : null;
|
|
24712
|
-
const cache =
|
|
26077
|
+
const cache = isRecord15(usage?.cache) ? usage.cache : null;
|
|
24713
26078
|
const totalTokens2 = numberOrNull2(usage?.totalTokens ?? usage?.total_tokens);
|
|
24714
26079
|
const inputTokens = numberOrNull2(usage?.inputTokens ?? usage?.input_tokens);
|
|
24715
26080
|
const cachedInputTokens = numberOrNull2(
|
|
@@ -24764,12 +26129,12 @@ function subtractTurnTokenBreakdowns(current, previous) {
|
|
|
24764
26129
|
};
|
|
24765
26130
|
}
|
|
24766
26131
|
function parseThreadTurnTokenUsage(payload) {
|
|
24767
|
-
const tokenUsage =
|
|
26132
|
+
const tokenUsage = isRecord15(payload) ? payload : null;
|
|
24768
26133
|
const total = buildTurnTokenBreakdown(
|
|
24769
|
-
|
|
26134
|
+
isRecord15(tokenUsage?.total) ? tokenUsage.total : null
|
|
24770
26135
|
);
|
|
24771
26136
|
const last = buildTurnTokenBreakdown(
|
|
24772
|
-
|
|
26137
|
+
isRecord15(tokenUsage?.last) ? tokenUsage.last : null
|
|
24773
26138
|
);
|
|
24774
26139
|
const modelContextWindow = numberOrNull2(
|
|
24775
26140
|
tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
|
|
@@ -24794,7 +26159,7 @@ function parseStoredThreadTurnTokenUsageState(value) {
|
|
|
24794
26159
|
try {
|
|
24795
26160
|
const parsed = JSON.parse(value);
|
|
24796
26161
|
const baselineTotal = buildTurnTokenBreakdown(
|
|
24797
|
-
|
|
26162
|
+
isRecord15(parsed?.baselineTotal) ? parsed.baselineTotal : null
|
|
24798
26163
|
);
|
|
24799
26164
|
const usage = parseThreadTurnTokenUsage(parsed);
|
|
24800
26165
|
const modelContextWindow = usage?.modelContextWindow ?? numberOrNull2(parsed?.modelContextWindow ?? parsed?.model_context_window);
|
|
@@ -24842,12 +26207,12 @@ function stringifyStoredThreadTurnTokenUsageState(state) {
|
|
|
24842
26207
|
});
|
|
24843
26208
|
}
|
|
24844
26209
|
function buildThreadTurnTokenUsage(payload, baselineTotal, previous = null, fallbackModelContextWindow = null) {
|
|
24845
|
-
const tokenUsage =
|
|
26210
|
+
const tokenUsage = isRecord15(payload) ? payload : null;
|
|
24846
26211
|
const cumulativeTotal = buildTurnTokenBreakdown(
|
|
24847
|
-
|
|
26212
|
+
isRecord15(tokenUsage?.total) ? tokenUsage.total : null
|
|
24848
26213
|
);
|
|
24849
26214
|
const last = buildTurnTokenBreakdown(
|
|
24850
|
-
|
|
26215
|
+
isRecord15(tokenUsage?.last) ? tokenUsage.last : null
|
|
24851
26216
|
);
|
|
24852
26217
|
const modelContextWindow = numberOrNull2(
|
|
24853
26218
|
tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
|
|
@@ -24941,7 +26306,7 @@ var ThreadUsageAccounting = class {
|
|
|
24941
26306
|
input.localThreadId
|
|
24942
26307
|
);
|
|
24943
26308
|
const currentCumulativeTotal = buildTurnTokenBreakdown(
|
|
24944
|
-
|
|
26309
|
+
isRecord15(input.tokenUsage?.total) ? input.tokenUsage.total : null
|
|
24945
26310
|
);
|
|
24946
26311
|
if (currentCumulativeTotal) {
|
|
24947
26312
|
this.threadCumulativeTokenUsage.set(input.localThreadId, currentCumulativeTotal);
|
|
@@ -25148,7 +26513,10 @@ var ThreadRuntimeEventProjector = class {
|
|
|
25148
26513
|
}
|
|
25149
26514
|
const pricingSnapshot = buildThreadTurnPricingSnapshot(
|
|
25150
26515
|
record2.model,
|
|
25151
|
-
callbacks.fastModeForProvider(record2.provider, record2.fastMode
|
|
26516
|
+
callbacks.fastModeForProvider(record2.provider, record2.fastMode, {
|
|
26517
|
+
agentId: record2.agentId ?? null,
|
|
26518
|
+
providerSessionId: record2.providerSessionId ?? null
|
|
26519
|
+
})
|
|
25152
26520
|
);
|
|
25153
26521
|
upsertThreadTurnMetadata(db, {
|
|
25154
26522
|
threadId: record2.id,
|
|
@@ -25279,7 +26647,8 @@ var ThreadRuntimeEventProjector = class {
|
|
|
25279
26647
|
itemId: event.itemId,
|
|
25280
26648
|
delta: event.delta,
|
|
25281
26649
|
sequence,
|
|
25282
|
-
createdAt
|
|
26650
|
+
createdAt,
|
|
26651
|
+
checkpoint: event.provider === "acp"
|
|
25283
26652
|
});
|
|
25284
26653
|
callbacks.invalidateThreadDetailCache(record2.id);
|
|
25285
26654
|
callbacks.emitThreadEvent("thread.output.delta", record2.id, {
|
|
@@ -25291,6 +26660,37 @@ var ThreadRuntimeEventProjector = class {
|
|
|
25291
26660
|
});
|
|
25292
26661
|
return;
|
|
25293
26662
|
}
|
|
26663
|
+
case "harness.extension": {
|
|
26664
|
+
const record2 = this.findRecordByProviderSessionId(
|
|
26665
|
+
event.provider,
|
|
26666
|
+
event.providerSessionId
|
|
26667
|
+
);
|
|
26668
|
+
if (!record2 || !event.providerTurnId || !event.providerItemId) {
|
|
26669
|
+
return;
|
|
26670
|
+
}
|
|
26671
|
+
const turnId = liveState.displayTurnIdForRuntimeTurn(record2.id, event.providerTurnId) ?? event.providerTurnId;
|
|
26672
|
+
const item = {
|
|
26673
|
+
id: event.providerItemId,
|
|
26674
|
+
kind: "other",
|
|
26675
|
+
text: `${event.extensionId}: ${event.event}`,
|
|
26676
|
+
status: "completed",
|
|
26677
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26678
|
+
sequence: liveState.recordTurnItemOrder(
|
|
26679
|
+
record2.id,
|
|
26680
|
+
turnId,
|
|
26681
|
+
event.providerItemId
|
|
26682
|
+
),
|
|
26683
|
+
sourceTurnId: event.providerTurnId
|
|
26684
|
+
};
|
|
26685
|
+
callbacks.persistLiveHistoryItem(record2.id, turnId, item);
|
|
26686
|
+
liveState.upsertLiveItem(record2.id, turnId, item);
|
|
26687
|
+
callbacks.invalidateThreadDetailCache(record2.id);
|
|
26688
|
+
callbacks.emitThreadEvent("thread.item.completed", record2.id, {
|
|
26689
|
+
turnId,
|
|
26690
|
+
item
|
|
26691
|
+
});
|
|
26692
|
+
return;
|
|
26693
|
+
}
|
|
25294
26694
|
case "turn.completed": {
|
|
25295
26695
|
const record2 = this.findRecordByProviderSessionId(
|
|
25296
26696
|
event.provider,
|
|
@@ -25542,7 +26942,7 @@ var ProviderRequestCoordinator = class {
|
|
|
25542
26942
|
const defaultMappedRequest = runtime.mapProviderRequest?.(request, {
|
|
25543
26943
|
approvalMode: "guarded"
|
|
25544
26944
|
});
|
|
25545
|
-
const providerSessionIdFromParams =
|
|
26945
|
+
const providerSessionIdFromParams = isRecord16(request.params) ? request.params.providerSessionId ?? request.params.threadId ?? request.params.conversationId ?? request.params.sessionId : null;
|
|
25546
26946
|
const providerSessionId = defaultMappedRequest?.providerSessionId ?? (typeof providerSessionIdFromParams === "string" ? providerSessionIdFromParams : null);
|
|
25547
26947
|
const record2 = providerSessionId ? this.callbacks.findRecordByProviderSessionId(request.provider, providerSessionId) : null;
|
|
25548
26948
|
if (!record2) {
|
|
@@ -25755,7 +27155,7 @@ function buildRequestAnswerLines(request, input) {
|
|
|
25755
27155
|
return `- ${question.question}: ${answers.join(", ")}`;
|
|
25756
27156
|
}).filter((line) => Boolean(line));
|
|
25757
27157
|
}
|
|
25758
|
-
function
|
|
27158
|
+
function isRecord16(value) {
|
|
25759
27159
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25760
27160
|
}
|
|
25761
27161
|
|
|
@@ -25867,6 +27267,15 @@ var ThreadDetailAssembler = class {
|
|
|
25867
27267
|
const persistedItemsByTurnIdForPatch = this.input.callbacks.listPersistedHistoryItemsByTurnId(
|
|
25868
27268
|
input.localThreadId
|
|
25869
27269
|
);
|
|
27270
|
+
if (input.record.provider === "acp") {
|
|
27271
|
+
remoteSession = {
|
|
27272
|
+
...remoteSession,
|
|
27273
|
+
turns: alignHydratedAcpTurnsWithPersistedHistory(
|
|
27274
|
+
remoteSession.turns,
|
|
27275
|
+
persistedItemsByTurnIdForPatch
|
|
27276
|
+
)
|
|
27277
|
+
};
|
|
27278
|
+
}
|
|
25870
27279
|
const activeDisplayTurnId = this.input.liveState.displayTurnIdForRuntimeTurn(
|
|
25871
27280
|
input.localThreadId,
|
|
25872
27281
|
input.record.providerTurnId
|
|
@@ -26117,6 +27526,101 @@ function appendPersistedFailureTurnsIfMissing(turns, persistedItemsByTurnId, met
|
|
|
26117
27526
|
}
|
|
26118
27527
|
return sortTurnsByStartedAt([...turns, ...missingFailureTurns]);
|
|
26119
27528
|
}
|
|
27529
|
+
function normalizedConversationText(value) {
|
|
27530
|
+
return value?.replace(/\s+/g, " ").trim() ?? "";
|
|
27531
|
+
}
|
|
27532
|
+
function turnConversationText(items, kind) {
|
|
27533
|
+
return items.filter((item) => item.kind === kind).map((item) => normalizedConversationText(item.text)).filter(Boolean).join("\n");
|
|
27534
|
+
}
|
|
27535
|
+
function conversationMatchScore(turn, persistedItems) {
|
|
27536
|
+
const remoteUser = turnConversationText(turn.items, "userMessage");
|
|
27537
|
+
const persistedUser = turnConversationText(persistedItems, "userMessage");
|
|
27538
|
+
if (!remoteUser || !persistedUser) {
|
|
27539
|
+
return 0;
|
|
27540
|
+
}
|
|
27541
|
+
const userScore = remoteUser === persistedUser ? 2 : Math.min(remoteUser.length, persistedUser.length) >= 16 && (remoteUser.includes(persistedUser) || persistedUser.includes(remoteUser)) ? 1 : 0;
|
|
27542
|
+
if (userScore === 0) {
|
|
27543
|
+
return 0;
|
|
27544
|
+
}
|
|
27545
|
+
const remoteAgent = turnConversationText(turn.items, "agentMessage");
|
|
27546
|
+
const persistedAgent = turnConversationText(persistedItems, "agentMessage");
|
|
27547
|
+
if (!remoteAgent || !persistedAgent) {
|
|
27548
|
+
return userScore;
|
|
27549
|
+
}
|
|
27550
|
+
if (remoteAgent === persistedAgent) {
|
|
27551
|
+
return userScore + 4;
|
|
27552
|
+
}
|
|
27553
|
+
if (remoteAgent.includes(persistedAgent) || persistedAgent.includes(remoteAgent)) {
|
|
27554
|
+
return userScore + 3;
|
|
27555
|
+
}
|
|
27556
|
+
return userScore;
|
|
27557
|
+
}
|
|
27558
|
+
function historyItemMatchScore(remote, persisted) {
|
|
27559
|
+
if (remote.id === persisted.id) {
|
|
27560
|
+
return 100;
|
|
27561
|
+
}
|
|
27562
|
+
if (remote.kind !== persisted.kind) {
|
|
27563
|
+
return 0;
|
|
27564
|
+
}
|
|
27565
|
+
const remoteText = normalizedConversationText(remote.detailText ?? remote.text);
|
|
27566
|
+
const persistedText = normalizedConversationText(persisted.detailText ?? persisted.text);
|
|
27567
|
+
if (!remoteText || !persistedText) {
|
|
27568
|
+
return 0;
|
|
27569
|
+
}
|
|
27570
|
+
if (remoteText === persistedText) {
|
|
27571
|
+
return 50;
|
|
27572
|
+
}
|
|
27573
|
+
if ((remote.kind === "agentMessage" || remote.kind === "reasoning") && (remoteText.includes(persistedText) || persistedText.includes(remoteText))) {
|
|
27574
|
+
return 40;
|
|
27575
|
+
}
|
|
27576
|
+
return 0;
|
|
27577
|
+
}
|
|
27578
|
+
function alignHydratedItems(remoteItems, persistedItems, turnId) {
|
|
27579
|
+
const used = /* @__PURE__ */ new Set();
|
|
27580
|
+
return remoteItems.map((remote) => {
|
|
27581
|
+
const match = persistedItems.filter((persisted) => !used.has(persisted.id)).map((persisted, index) => ({
|
|
27582
|
+
persisted,
|
|
27583
|
+
index,
|
|
27584
|
+
score: historyItemMatchScore(remote, persisted)
|
|
27585
|
+
})).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.index - right.index)[0];
|
|
27586
|
+
if (!match) {
|
|
27587
|
+
return remote.sourceTurnId ? { ...remote, sourceTurnId: turnId } : remote;
|
|
27588
|
+
}
|
|
27589
|
+
used.add(match.persisted.id);
|
|
27590
|
+
return {
|
|
27591
|
+
...remote,
|
|
27592
|
+
id: match.persisted.id,
|
|
27593
|
+
...remote.sourceTurnId || remote.kind === "agentMessage" ? { sourceTurnId: turnId } : {}
|
|
27594
|
+
};
|
|
27595
|
+
});
|
|
27596
|
+
}
|
|
27597
|
+
function alignHydratedAcpTurnsWithPersistedHistory(turns, persistedItemsByTurnId) {
|
|
27598
|
+
const remoteTurnIds = new Set(turns.map((turn) => turn.providerTurnId));
|
|
27599
|
+
const candidates = [...persistedItemsByTurnId.entries()].filter(
|
|
27600
|
+
([turnId]) => !remoteTurnIds.has(turnId)
|
|
27601
|
+
);
|
|
27602
|
+
const used = /* @__PURE__ */ new Set();
|
|
27603
|
+
return turns.map((turn) => {
|
|
27604
|
+
if (!turn.providerTurnId.startsWith("acp-hydrated:")) {
|
|
27605
|
+
return turn;
|
|
27606
|
+
}
|
|
27607
|
+
const match = candidates.filter(([turnId]) => !used.has(turnId)).map(([turnId, items], index) => ({
|
|
27608
|
+
turnId,
|
|
27609
|
+
items,
|
|
27610
|
+
index,
|
|
27611
|
+
score: conversationMatchScore(turn, items)
|
|
27612
|
+
})).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.index - right.index)[0];
|
|
27613
|
+
if (!match) {
|
|
27614
|
+
return turn;
|
|
27615
|
+
}
|
|
27616
|
+
used.add(match.turnId);
|
|
27617
|
+
return {
|
|
27618
|
+
...turn,
|
|
27619
|
+
providerTurnId: match.turnId,
|
|
27620
|
+
items: alignHydratedItems(turn.items, match.items, match.turnId)
|
|
27621
|
+
};
|
|
27622
|
+
});
|
|
27623
|
+
}
|
|
26120
27624
|
function appendPersistedTurnsIfMissing(turns, persistedItemsByTurnId, metadataById) {
|
|
26121
27625
|
if (persistedItemsByTurnId.size === 0) {
|
|
26122
27626
|
return turns;
|
|
@@ -26389,15 +27893,37 @@ var ThreadProviderRuntimeCoordinator = class {
|
|
|
26389
27893
|
isCodexProvider(provider2) {
|
|
26390
27894
|
return this.providerForRecord({ provider: provider2 }) === "codex";
|
|
26391
27895
|
}
|
|
26392
|
-
|
|
26393
|
-
|
|
27896
|
+
capabilitiesFor(input) {
|
|
27897
|
+
const runtime = this.runtimeForProvider(input.provider);
|
|
27898
|
+
return runtime.getScopedCapabilities?.({
|
|
27899
|
+
agentId: input.agentId ?? null,
|
|
27900
|
+
providerSessionId: input.providerSessionId ?? null
|
|
27901
|
+
}) ?? runtime.capabilities;
|
|
27902
|
+
}
|
|
27903
|
+
async resolveCapabilitiesFor(input) {
|
|
27904
|
+
const runtime = this.runtimeForProvider(input.provider);
|
|
27905
|
+
if (input.agentId && runtime.getAgentCapabilitySnapshot) {
|
|
27906
|
+
const snapshot = await runtime.getAgentCapabilitySnapshot(input.agentId);
|
|
27907
|
+
if (snapshot.effectiveCapabilities) {
|
|
27908
|
+
return snapshot.effectiveCapabilities;
|
|
27909
|
+
}
|
|
27910
|
+
}
|
|
27911
|
+
return this.capabilitiesFor(input);
|
|
27912
|
+
}
|
|
27913
|
+
runtimeSupportsFastMode(provider2, scope = {}) {
|
|
27914
|
+
const runtime = this.optionalRuntimeForProvider(provider2);
|
|
27915
|
+
if (!runtime) return false;
|
|
27916
|
+
return (runtime.getScopedCapabilities?.({
|
|
27917
|
+
agentId: scope.agentId ?? null,
|
|
27918
|
+
providerSessionId: scope.providerSessionId ?? null
|
|
27919
|
+
}) ?? runtime.capabilities).controls.performanceMode;
|
|
26394
27920
|
}
|
|
26395
|
-
fastModeForProvider(provider2, fastMode) {
|
|
26396
|
-
return this.runtimeSupportsFastMode(provider2) ? normalizeFastMode(fastMode) : false;
|
|
27921
|
+
fastModeForProvider(provider2, fastMode, scope = {}) {
|
|
27922
|
+
return this.runtimeSupportsFastMode(provider2, scope) ? normalizeFastMode(fastMode) : false;
|
|
26397
27923
|
}
|
|
26398
27924
|
performanceModeForRecord(record2) {
|
|
26399
27925
|
return performanceModeForFastMode(
|
|
26400
|
-
this.fastModeForProvider(record2.provider, record2.fastMode)
|
|
27926
|
+
this.fastModeForProvider(record2.provider, record2.fastMode, record2)
|
|
26401
27927
|
);
|
|
26402
27928
|
}
|
|
26403
27929
|
async listLoadedProviderSessionIds(provider2 = "codex") {
|
|
@@ -26691,7 +28217,7 @@ var ThreadPromptTurnCoordinator = class {
|
|
|
26691
28217
|
async startPromptTurn(localThreadId, record2, input) {
|
|
26692
28218
|
const displayPrompt = input.displayPrompt ?? input.prompt;
|
|
26693
28219
|
const runtime = this.callbacks.runtimeForProvider(record2.provider);
|
|
26694
|
-
const modelRecords = await runtime.listModels().catch(() => []);
|
|
28220
|
+
const modelRecords = await (record2.agentId && runtime.listModelsForAgent ? runtime.listModelsForAgent(record2.agentId, input.workspacePath) : runtime.listModels()).catch(() => []);
|
|
26695
28221
|
ensureFastModeSupported(
|
|
26696
28222
|
input.effectiveModel,
|
|
26697
28223
|
input.performanceMode === "fast",
|
|
@@ -27011,7 +28537,10 @@ function toThreadDto(record2, loadedIds, callbacks) {
|
|
|
27011
28537
|
title: record2.title,
|
|
27012
28538
|
model,
|
|
27013
28539
|
reasoningEffort: normalizeReasoningEffort2(record2.reasoningEffort),
|
|
27014
|
-
fastMode: callbacks.fastModeForProvider(record2.provider, record2.fastMode
|
|
28540
|
+
fastMode: callbacks.fastModeForProvider(record2.provider, record2.fastMode, {
|
|
28541
|
+
agentId,
|
|
28542
|
+
providerSessionId: record2.providerSessionId ?? null
|
|
28543
|
+
}),
|
|
27015
28544
|
collaborationMode: normalizeCollaborationMode(record2.collaborationMode),
|
|
27016
28545
|
approvalMode: record2.approvalMode ?? "yolo",
|
|
27017
28546
|
sandboxMode: normalizeSandboxMode(record2.sandboxMode) ?? defaultSandboxModeForApprovalMode(record2.approvalMode ?? "yolo"),
|
|
@@ -27055,24 +28584,32 @@ var ThreadSessionCoordinator = class {
|
|
|
27055
28584
|
agentId: input.threadInput.agentId,
|
|
27056
28585
|
workspacePath: input.workspacePath
|
|
27057
28586
|
}).catch(() => []);
|
|
28587
|
+
const effectiveModel = input.threadInput.model === "default" ? modelRecords.find((model) => model.isDefault)?.model ?? input.threadInput.model : input.threadInput.model;
|
|
27058
28588
|
const reasoningEffort = this.providerRuntime.normalizeReasoningForModel(
|
|
27059
28589
|
modelRecords,
|
|
27060
|
-
|
|
28590
|
+
effectiveModel,
|
|
27061
28591
|
input.threadInput.reasoningEffort ?? null
|
|
27062
28592
|
);
|
|
27063
28593
|
const sandboxMode = defaultSandboxModeForApprovalMode(input.threadInput.approvalMode);
|
|
27064
|
-
const
|
|
27065
|
-
|
|
27066
|
-
|
|
28594
|
+
const capabilityScope = {
|
|
28595
|
+
agentId: input.threadInput.agentId ?? null
|
|
28596
|
+
};
|
|
28597
|
+
const supportsFastMode2 = this.providerRuntime.runtimeSupportsFastMode(
|
|
28598
|
+
provider2,
|
|
28599
|
+
capabilityScope
|
|
28600
|
+
);
|
|
28601
|
+
const fastMode = supportsFastMode2 ? this.performanceModeSettings.readFastMode() : false;
|
|
28602
|
+
if (supportsFastMode2) {
|
|
28603
|
+
ensureFastModeSupported(effectiveModel, fastMode, modelRecords);
|
|
27067
28604
|
}
|
|
27068
28605
|
const response = await runtime.startSession({
|
|
27069
28606
|
cwd: input.workspacePath,
|
|
27070
28607
|
...input.threadInput.agentId ? { agentId: input.threadInput.agentId } : {},
|
|
27071
|
-
model:
|
|
28608
|
+
model: effectiveModel,
|
|
27072
28609
|
reasoningEffort,
|
|
27073
28610
|
approvalMode: input.threadInput.approvalMode,
|
|
27074
28611
|
sandboxMode,
|
|
27075
|
-
performanceMode: performanceModeForFastMode(fastMode)
|
|
28612
|
+
...supportsFastMode2 ? { performanceMode: performanceModeForFastMode(fastMode) } : {}
|
|
27076
28613
|
});
|
|
27077
28614
|
return {
|
|
27078
28615
|
provider: provider2,
|
|
@@ -27113,6 +28650,10 @@ var ThreadSessionCoordinator = class {
|
|
|
27113
28650
|
provider: provider2
|
|
27114
28651
|
});
|
|
27115
28652
|
}
|
|
28653
|
+
async listImportSessions(provider2, agentId) {
|
|
28654
|
+
const runtime = this.providerRuntime.runtimeForProvider(provider2);
|
|
28655
|
+
return runtime.listImportSessions ? runtime.listImportSessions(agentId) : runtime.listSessions();
|
|
28656
|
+
}
|
|
27116
28657
|
async resolveRuntimeImportSession(provider2, sessionId) {
|
|
27117
28658
|
try {
|
|
27118
28659
|
const session = await this.providerRuntime.runtimeForProvider(provider2).readSession(sessionId);
|
|
@@ -27121,13 +28662,17 @@ var ThreadSessionCoordinator = class {
|
|
|
27121
28662
|
}
|
|
27122
28663
|
return {
|
|
27123
28664
|
provider: provider2,
|
|
27124
|
-
|
|
28665
|
+
agentId: session.agentId ?? null,
|
|
28666
|
+
source: "local_provider_import",
|
|
27125
28667
|
sessionId,
|
|
27126
28668
|
cwd: session.cwd,
|
|
27127
28669
|
title: session.title?.trim() || session.preview?.trim() || "Untitled imported session",
|
|
27128
28670
|
model: null,
|
|
27129
28671
|
summaryText: session.preview,
|
|
27130
|
-
fastMode: this.providerRuntime.runtimeSupportsFastMode(provider2
|
|
28672
|
+
fastMode: this.providerRuntime.runtimeSupportsFastMode(provider2, {
|
|
28673
|
+
agentId: session.agentId ?? null,
|
|
28674
|
+
providerSessionId: sessionId
|
|
28675
|
+
}) ? this.performanceModeSettings.readFastMode() : false
|
|
27131
28676
|
};
|
|
27132
28677
|
} catch {
|
|
27133
28678
|
return null;
|
|
@@ -27153,12 +28698,24 @@ var ThreadSessionCoordinator = class {
|
|
|
27153
28698
|
async resumeThreadSession(input) {
|
|
27154
28699
|
const runtime = this.providerRuntime.runtimeForProvider(input.provider);
|
|
27155
28700
|
const sandboxMode = input.resumeInput.sandboxMode ?? normalizeSandboxMode(input.currentSandboxMode) ?? defaultSandboxModeForApprovalMode(input.approvalMode);
|
|
27156
|
-
const fastMode = this.providerRuntime.fastModeForProvider(input.provider, input.fastMode);
|
|
27157
28701
|
const modelRecords = await this.listSessionModels({
|
|
27158
28702
|
provider: input.provider,
|
|
27159
28703
|
agentId: input.agentId,
|
|
27160
28704
|
workspacePath: input.workspacePath
|
|
27161
28705
|
}).catch(() => []);
|
|
28706
|
+
const capabilityScope = {
|
|
28707
|
+
agentId: input.agentId ?? null,
|
|
28708
|
+
providerSessionId: input.providerSessionId
|
|
28709
|
+
};
|
|
28710
|
+
const supportsFastMode2 = this.providerRuntime.runtimeSupportsFastMode(
|
|
28711
|
+
input.provider,
|
|
28712
|
+
capabilityScope
|
|
28713
|
+
);
|
|
28714
|
+
const fastMode = this.providerRuntime.fastModeForProvider(
|
|
28715
|
+
input.provider,
|
|
28716
|
+
input.fastMode,
|
|
28717
|
+
capabilityScope
|
|
28718
|
+
);
|
|
27162
28719
|
let response;
|
|
27163
28720
|
try {
|
|
27164
28721
|
ensureFastModeSupported(
|
|
@@ -27170,7 +28727,7 @@ var ThreadSessionCoordinator = class {
|
|
|
27170
28727
|
providerSessionId: input.providerSessionId,
|
|
27171
28728
|
model: input.resumeInput.model ?? input.currentModel ?? null,
|
|
27172
28729
|
sandboxMode,
|
|
27173
|
-
performanceMode: performanceModeForFastMode(fastMode)
|
|
28730
|
+
...supportsFastMode2 ? { performanceMode: performanceModeForFastMode(fastMode) } : {}
|
|
27174
28731
|
});
|
|
27175
28732
|
} catch (error) {
|
|
27176
28733
|
if (!isRemoteThreadBootstrapError(error)) {
|
|
@@ -27195,7 +28752,8 @@ var ThreadSessionCoordinator = class {
|
|
|
27195
28752
|
}
|
|
27196
28753
|
async compactThreadSession(input) {
|
|
27197
28754
|
const runtime = this.providerRuntime.runtimeForProvider(input.provider);
|
|
27198
|
-
|
|
28755
|
+
const capabilities = this.providerRuntime.capabilitiesFor(input);
|
|
28756
|
+
if (!runtime.compactSession || !capabilities.turns.compact) {
|
|
27199
28757
|
throw new HttpError(409, {
|
|
27200
28758
|
code: "conflict",
|
|
27201
28759
|
message: "This backend does not support context compaction."
|
|
@@ -27212,25 +28770,27 @@ var ThreadSessionCoordinator = class {
|
|
|
27212
28770
|
});
|
|
27213
28771
|
}
|
|
27214
28772
|
const runtime = this.providerRuntime.runtimeForProvider(input.provider);
|
|
27215
|
-
|
|
28773
|
+
const capabilities = this.providerRuntime.capabilitiesFor(input);
|
|
28774
|
+
if (!runtime.forkSession || !capabilities.branching.fork) {
|
|
27216
28775
|
throw new HttpError(409, {
|
|
27217
28776
|
code: "conflict",
|
|
27218
28777
|
message: "This backend does not support session fork."
|
|
27219
28778
|
});
|
|
27220
28779
|
}
|
|
28780
|
+
const turnsToRollback = selectedTurn == null ? 0 : Math.max(0, input.turnOptions.length - selectedTurn.turnIndex);
|
|
28781
|
+
const rollbackSession = runtime.rollbackSession?.bind(runtime);
|
|
28782
|
+
if (turnsToRollback > 0 && !rollbackSession) {
|
|
28783
|
+
throw new HttpError(409, {
|
|
28784
|
+
code: "conflict",
|
|
28785
|
+
message: "This backend supports latest-session fork only."
|
|
28786
|
+
});
|
|
28787
|
+
}
|
|
27221
28788
|
let forkedSession = await runtime.forkSession({
|
|
27222
28789
|
providerSessionId: input.providerSessionId,
|
|
27223
28790
|
atTurnId: selectedTurn?.turnId ?? null
|
|
27224
28791
|
});
|
|
27225
|
-
const turnsToRollback = selectedTurn == null ? 0 : Math.max(0, input.turnOptions.length - selectedTurn.turnIndex);
|
|
27226
28792
|
if (turnsToRollback > 0) {
|
|
27227
|
-
|
|
27228
|
-
throw new HttpError(409, {
|
|
27229
|
-
code: "conflict",
|
|
27230
|
-
message: "This backend does not support rollback after fork."
|
|
27231
|
-
});
|
|
27232
|
-
}
|
|
27233
|
-
forkedSession = await runtime.rollbackSession({
|
|
28793
|
+
forkedSession = await rollbackSession({
|
|
27234
28794
|
providerSessionId: forkedSession.providerSessionId,
|
|
27235
28795
|
count: turnsToRollback
|
|
27236
28796
|
});
|
|
@@ -27266,10 +28826,18 @@ var ThreadSessionCoordinator = class {
|
|
|
27266
28826
|
workspacePath: input.workspacePath
|
|
27267
28827
|
});
|
|
27268
28828
|
const fallbackModel = modelRecords.find((entry) => entry.isDefault) ?? modelRecords[0] ?? null;
|
|
27269
|
-
const
|
|
28829
|
+
const capabilityScope = {
|
|
28830
|
+
agentId: input.agentId ?? null,
|
|
28831
|
+
providerSessionId: null
|
|
28832
|
+
};
|
|
28833
|
+
const supportsFastMode2 = this.providerRuntime.runtimeSupportsFastMode(
|
|
28834
|
+
input.provider,
|
|
28835
|
+
capabilityScope
|
|
28836
|
+
);
|
|
27270
28837
|
const currentFastMode = this.providerRuntime.fastModeForProvider(
|
|
27271
28838
|
input.provider,
|
|
27272
|
-
input.currentFastMode
|
|
28839
|
+
input.currentFastMode,
|
|
28840
|
+
capabilityScope
|
|
27273
28841
|
);
|
|
27274
28842
|
const nextFastMode = supportsFastMode2 && input.settings.fastMode !== void 0 ? input.settings.fastMode : currentFastMode;
|
|
27275
28843
|
const currentModel = input.currentModel ?? fallbackModel?.model ?? null;
|
|
@@ -27314,9 +28882,15 @@ var ThreadSessionCoordinator = class {
|
|
|
27314
28882
|
);
|
|
27315
28883
|
const collaborationMode = input.promptInput?.collaborationMode ?? normalizeCollaborationMode(input.currentCollaborationMode);
|
|
27316
28884
|
const sandboxMode = (input.promptInput?.sandboxMode !== void 0 ? normalizeSandboxMode(input.promptInput.sandboxMode) : normalizeSandboxMode(input.currentSandboxMode)) ?? defaultSandboxModeForApprovalMode(input.approvalMode);
|
|
28885
|
+
const capabilities = this.providerRuntime.capabilitiesFor({
|
|
28886
|
+
provider: input.provider,
|
|
28887
|
+
agentId: input.agentId ?? null
|
|
28888
|
+
});
|
|
28889
|
+
const supportsFastMode2 = capabilities.controls.performanceMode;
|
|
27317
28890
|
const fastMode = this.providerRuntime.fastModeForProvider(
|
|
27318
28891
|
input.provider,
|
|
27319
|
-
input.currentFastMode
|
|
28892
|
+
input.currentFastMode,
|
|
28893
|
+
{ agentId: input.agentId ?? null }
|
|
27320
28894
|
);
|
|
27321
28895
|
ensureFastModeSupported(effectiveModel, fastMode, modelRecords);
|
|
27322
28896
|
return {
|
|
@@ -27324,8 +28898,8 @@ var ThreadSessionCoordinator = class {
|
|
|
27324
28898
|
normalizedReasoning,
|
|
27325
28899
|
collaborationMode,
|
|
27326
28900
|
sandboxMode,
|
|
27327
|
-
performanceMode: performanceModeForFastMode(fastMode),
|
|
27328
|
-
supportsRunningTurnInput: Boolean(runtime.sendInput &&
|
|
28901
|
+
performanceMode: supportsFastMode2 ? performanceModeForFastMode(fastMode) : null,
|
|
28902
|
+
supportsRunningTurnInput: Boolean(runtime.sendInput && capabilities.turns.steer)
|
|
27329
28903
|
};
|
|
27330
28904
|
}
|
|
27331
28905
|
};
|
|
@@ -27437,12 +29011,15 @@ var ThreadSessionLifecycleCoordinator = class {
|
|
|
27437
29011
|
|
|
27438
29012
|
// src/thread-history-persistence-coordinator.ts
|
|
27439
29013
|
var ThreadHistoryPersistenceCoordinator = class {
|
|
27440
|
-
constructor(db, liveState) {
|
|
29014
|
+
constructor(db, liveState, checkpointOptions = {}) {
|
|
27441
29015
|
this.db = db;
|
|
27442
29016
|
this.liveState = liveState;
|
|
29017
|
+
this.checkpointOptions = checkpointOptions;
|
|
27443
29018
|
}
|
|
27444
29019
|
db;
|
|
27445
29020
|
liveState;
|
|
29021
|
+
checkpointOptions;
|
|
29022
|
+
liveAgentMessageCheckpoints = /* @__PURE__ */ new Map();
|
|
27446
29023
|
listPersistedHistoryItemsByTurnId(localThreadId) {
|
|
27447
29024
|
const itemsByTurnId = /* @__PURE__ */ new Map();
|
|
27448
29025
|
for (const record2 of listThreadHistoryItemRecordsByThreadId(this.db, localThreadId)) {
|
|
@@ -27480,10 +29057,64 @@ var ThreadHistoryPersistenceCoordinator = class {
|
|
|
27480
29057
|
itemJson: JSON.stringify(item)
|
|
27481
29058
|
});
|
|
27482
29059
|
}
|
|
29060
|
+
checkpointLiveAgentMessage(localThreadId, turnId, item) {
|
|
29061
|
+
if (item.kind !== "agentMessage") {
|
|
29062
|
+
return;
|
|
29063
|
+
}
|
|
29064
|
+
const key = `${localThreadId}\0${turnId}\0${item.id}`;
|
|
29065
|
+
const previous = this.liveAgentMessageCheckpoints.get(key);
|
|
29066
|
+
const now = (this.checkpointOptions.now ?? Date.now)();
|
|
29067
|
+
const intervalMs = this.checkpointOptions.intervalMs ?? 250;
|
|
29068
|
+
const textDelta = this.checkpointOptions.textDelta ?? 512;
|
|
29069
|
+
if (previous && now - previous.savedAt < intervalMs && item.text.length - previous.textLength < textDelta) {
|
|
29070
|
+
return;
|
|
29071
|
+
}
|
|
29072
|
+
this.persistLiveHistoryItem(localThreadId, turnId, item);
|
|
29073
|
+
this.liveAgentMessageCheckpoints.set(key, {
|
|
29074
|
+
savedAt: now,
|
|
29075
|
+
textLength: item.text.length
|
|
29076
|
+
});
|
|
29077
|
+
}
|
|
29078
|
+
clearThread(localThreadId) {
|
|
29079
|
+
const prefix = `${localThreadId}\0`;
|
|
29080
|
+
for (const key of this.liveAgentMessageCheckpoints.keys()) {
|
|
29081
|
+
if (key.startsWith(prefix)) {
|
|
29082
|
+
this.liveAgentMessageCheckpoints.delete(key);
|
|
29083
|
+
}
|
|
29084
|
+
}
|
|
29085
|
+
}
|
|
29086
|
+
persistHydratedTurns(localThreadId, turns) {
|
|
29087
|
+
for (const turn of turns) {
|
|
29088
|
+
if (turn.startedAt) {
|
|
29089
|
+
upsertThreadTurnMetadata(this.db, {
|
|
29090
|
+
threadId: localThreadId,
|
|
29091
|
+
turnId: turn.providerTurnId,
|
|
29092
|
+
createdAt: turn.startedAt
|
|
29093
|
+
});
|
|
29094
|
+
}
|
|
29095
|
+
turn.items.forEach((item, index) => {
|
|
29096
|
+
if (!shouldPersistRuntimeFinalHistoryItem(item)) {
|
|
29097
|
+
return;
|
|
29098
|
+
}
|
|
29099
|
+
const createdAt = item.createdAt ?? turn.startedAt;
|
|
29100
|
+
this.persistProjectedHistoryItem(
|
|
29101
|
+
localThreadId,
|
|
29102
|
+
turn.providerTurnId,
|
|
29103
|
+
{
|
|
29104
|
+
...item,
|
|
29105
|
+
...createdAt ? { createdAt } : {},
|
|
29106
|
+
sequence: item.sequence ?? index,
|
|
29107
|
+
...item.kind === "agentMessage" && !item.sourceTurnId ? { sourceTurnId: turn.providerTurnId } : {}
|
|
29108
|
+
}
|
|
29109
|
+
);
|
|
29110
|
+
});
|
|
29111
|
+
}
|
|
29112
|
+
}
|
|
27483
29113
|
deletePersistedHistoryItemsForTurn(localThreadId, turnId) {
|
|
27484
29114
|
deleteThreadHistoryItemRecordsByThreadAndTurnId(this.db, localThreadId, turnId);
|
|
27485
29115
|
}
|
|
27486
29116
|
persistFinalTurnOrderingHints(localThreadId, turnId, items) {
|
|
29117
|
+
this.clearTurnCheckpoints(localThreadId, turnId);
|
|
27487
29118
|
const orderingHints = this.liveState.finalTurnAgentMessageOrderingMetadata(
|
|
27488
29119
|
localThreadId,
|
|
27489
29120
|
turnId,
|
|
@@ -27510,6 +29141,14 @@ var ThreadHistoryPersistenceCoordinator = class {
|
|
|
27510
29141
|
});
|
|
27511
29142
|
}
|
|
27512
29143
|
}
|
|
29144
|
+
clearTurnCheckpoints(localThreadId, turnId) {
|
|
29145
|
+
const prefix = `${localThreadId}\0${turnId}\0`;
|
|
29146
|
+
for (const key of this.liveAgentMessageCheckpoints.keys()) {
|
|
29147
|
+
if (key.startsWith(prefix)) {
|
|
29148
|
+
this.liveAgentMessageCheckpoints.delete(key);
|
|
29149
|
+
}
|
|
29150
|
+
}
|
|
29151
|
+
}
|
|
27513
29152
|
persistRuntimeTurnItemsAsDisplayTurn(localThreadId, runtimeTurnId, displayTurnId, items) {
|
|
27514
29153
|
if (runtimeTurnId === displayTurnId) {
|
|
27515
29154
|
return;
|
|
@@ -27574,10 +29213,10 @@ var ThreadHistoryPersistenceCoordinator = class {
|
|
|
27574
29213
|
};
|
|
27575
29214
|
|
|
27576
29215
|
// src/thread-deletion-coordinator.ts
|
|
27577
|
-
import
|
|
27578
|
-
import
|
|
29216
|
+
import fs15 from "fs/promises";
|
|
29217
|
+
import path19 from "path";
|
|
27579
29218
|
function threadTempDirectoryPath(workspacePath, localThreadId) {
|
|
27580
|
-
return
|
|
29219
|
+
return path19.join(workspacePath, ".temp", "threads", localThreadId);
|
|
27581
29220
|
}
|
|
27582
29221
|
var ThreadDeletionCoordinator = class {
|
|
27583
29222
|
constructor(db, requestCoordinator, usageAccounting, liveState, auxiliaryState, callbacks) {
|
|
@@ -27605,10 +29244,11 @@ var ThreadDeletionCoordinator = class {
|
|
|
27605
29244
|
const workspace = getWorkspaceRecordById(this.db, record2.workspaceId);
|
|
27606
29245
|
if (workspace) {
|
|
27607
29246
|
const tempDirectory = threadTempDirectoryPath(workspace.absPath, localThreadId);
|
|
27608
|
-
await
|
|
29247
|
+
await fs15.rm(tempDirectory, { recursive: true, force: true }).catch(() => {
|
|
27609
29248
|
});
|
|
27610
29249
|
}
|
|
27611
29250
|
this.requestCoordinator.clearThread(localThreadId);
|
|
29251
|
+
this.callbacks.clearHistoryPersistence(localThreadId);
|
|
27612
29252
|
this.callbacks.invalidateThreadDetailCache(localThreadId);
|
|
27613
29253
|
this.usageAccounting.clearThread(localThreadId);
|
|
27614
29254
|
this.liveState.clearThread(localThreadId);
|
|
@@ -27629,9 +29269,9 @@ var ThreadDeletionCoordinator = class {
|
|
|
27629
29269
|
};
|
|
27630
29270
|
|
|
27631
29271
|
// src/exports/thread-pdf-export.ts
|
|
27632
|
-
import
|
|
29272
|
+
import fs16 from "fs";
|
|
27633
29273
|
import { createRequire as createRequire3 } from "module";
|
|
27634
|
-
import
|
|
29274
|
+
import path20 from "path";
|
|
27635
29275
|
import puppeteer from "puppeteer-core";
|
|
27636
29276
|
import { marked } from "marked";
|
|
27637
29277
|
var MAX_TEXT_CHARS = 12e3;
|
|
@@ -27682,10 +29322,10 @@ function renderEmbeddedSystemCjkFontCss() {
|
|
|
27682
29322
|
embeddedSystemCjkFontCss = "";
|
|
27683
29323
|
for (const candidate of EMBEDDED_CJK_FONT_CANDIDATES) {
|
|
27684
29324
|
try {
|
|
27685
|
-
if (!
|
|
29325
|
+
if (!fs16.existsSync(candidate.path)) {
|
|
27686
29326
|
continue;
|
|
27687
29327
|
}
|
|
27688
|
-
const font =
|
|
29328
|
+
const font = fs16.readFileSync(candidate.path);
|
|
27689
29329
|
embeddedSystemCjkFontCss = `
|
|
27690
29330
|
@font-face {
|
|
27691
29331
|
font-family: "RemoteCodexCJK";
|
|
@@ -27748,7 +29388,7 @@ function containsCjkCodePoint(codePoints) {
|
|
|
27748
29388
|
}
|
|
27749
29389
|
function renderBundledFontCss(source, usedCodePoints) {
|
|
27750
29390
|
const packageRoot = resolvePackageRoot3(source.packageName);
|
|
27751
|
-
const cssPath =
|
|
29391
|
+
const cssPath = path20.join(packageRoot, source.cssFile);
|
|
27752
29392
|
const blocks = getFontFaceBlocks(cssPath);
|
|
27753
29393
|
return blocks.filter((block) => !usedCodePoints || fontFaceIntersects(block.ranges, usedCodePoints)).map((block) => inlineFontFaceBlock(block, packageRoot)).filter(Boolean);
|
|
27754
29394
|
}
|
|
@@ -27757,7 +29397,7 @@ function resolvePackageRoot3(packageName) {
|
|
|
27757
29397
|
if (cached) {
|
|
27758
29398
|
return cached;
|
|
27759
29399
|
}
|
|
27760
|
-
const packageRoot =
|
|
29400
|
+
const packageRoot = path20.dirname(require2.resolve(`${packageName}/package.json`));
|
|
27761
29401
|
packageRootCache.set(packageName, packageRoot);
|
|
27762
29402
|
return packageRoot;
|
|
27763
29403
|
}
|
|
@@ -27766,7 +29406,7 @@ function getFontFaceBlocks(cssPath) {
|
|
|
27766
29406
|
if (cached) {
|
|
27767
29407
|
return cached;
|
|
27768
29408
|
}
|
|
27769
|
-
const css =
|
|
29409
|
+
const css = fs16.readFileSync(cssPath, "utf8");
|
|
27770
29410
|
const blocks = Array.from(css.matchAll(/@font-face\s*{[\s\S]*?}/g)).map((match) => {
|
|
27771
29411
|
const block = match[0];
|
|
27772
29412
|
const fontPath = block.match(/url\((['"]?)(\.\/files\/[^)'"]+\.woff2)\1\)\s*format\((['"]?)woff2\3\)/)?.[2] ?? "";
|
|
@@ -27805,9 +29445,9 @@ function fontFaceIntersects(ranges, usedCodePoints) {
|
|
|
27805
29445
|
return false;
|
|
27806
29446
|
}
|
|
27807
29447
|
function inlineFontFaceBlock(block, packageRoot) {
|
|
27808
|
-
const fontPath =
|
|
29448
|
+
const fontPath = path20.join(packageRoot, block.fontPath);
|
|
27809
29449
|
try {
|
|
27810
|
-
const font =
|
|
29450
|
+
const font = fs16.readFileSync(fontPath);
|
|
27811
29451
|
return block.block.replace(/font-display:\s*swap;/g, "font-display: block;").replace(/src:\s*[^;]+;/, `src: url("data:font/woff2;base64,${font.toString("base64")}") format("woff2");`);
|
|
27812
29452
|
} catch {
|
|
27813
29453
|
return "";
|
|
@@ -28806,7 +30446,7 @@ endobj
|
|
|
28806
30446
|
function resolvePdfBrowserExecutablePath() {
|
|
28807
30447
|
try {
|
|
28808
30448
|
const executablePath = process.env.PUPPETEER_EXECUTABLE_PATH ? process.env.PUPPETEER_EXECUTABLE_PATH : puppeteer.executablePath(PUPPETEER_CHANNEL);
|
|
28809
|
-
if (!
|
|
30449
|
+
if (!fs16.existsSync(executablePath)) {
|
|
28810
30450
|
throw new Error(`Browser executable was not found at ${executablePath}`);
|
|
28811
30451
|
}
|
|
28812
30452
|
return executablePath;
|
|
@@ -29075,6 +30715,7 @@ var ThreadForkCoordinator = class {
|
|
|
29075
30715
|
const turnOptions = await this.listForkTurnOptions(localThreadId);
|
|
29076
30716
|
const forkResult = await this.sessionCoordinator.forkThreadSession({
|
|
29077
30717
|
provider: record2.provider,
|
|
30718
|
+
agentId: record2.agentId,
|
|
29078
30719
|
providerSessionId,
|
|
29079
30720
|
mode: input.mode,
|
|
29080
30721
|
...input.turnId ? { turnId: input.turnId } : {},
|
|
@@ -29085,11 +30726,15 @@ var ThreadForkCoordinator = class {
|
|
|
29085
30726
|
const created = createThreadRecord(this.db, {
|
|
29086
30727
|
workspaceId: record2.workspaceId,
|
|
29087
30728
|
provider: this.callbacks.providerForRecord(record2),
|
|
30729
|
+
agentId: forkedSession.agentId ?? record2.agentId ?? null,
|
|
29088
30730
|
providerSessionId: forkedSession.providerSessionId,
|
|
29089
30731
|
title: `${forkTitleBase} / fork`,
|
|
29090
30732
|
model: record2.model,
|
|
29091
30733
|
reasoningEffort: record2.reasoningEffort,
|
|
29092
|
-
fastMode: this.callbacks.fastModeForProvider(record2.provider, record2.fastMode
|
|
30734
|
+
fastMode: this.callbacks.fastModeForProvider(record2.provider, record2.fastMode, {
|
|
30735
|
+
agentId: record2.agentId ?? null,
|
|
30736
|
+
providerSessionId
|
|
30737
|
+
}),
|
|
29093
30738
|
fastBaseModel: record2.fastBaseModel,
|
|
29094
30739
|
fastBaseReasoningEffort: record2.fastBaseReasoningEffort,
|
|
29095
30740
|
collaborationMode: this.callbacks.normalizeCollaborationMode(record2.collaborationMode),
|
|
@@ -29126,19 +30771,19 @@ var ThreadForkCoordinator = class {
|
|
|
29126
30771
|
|
|
29127
30772
|
// src/thread-attachment-coordinator.ts
|
|
29128
30773
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
29129
|
-
import
|
|
29130
|
-
import
|
|
30774
|
+
import fs17 from "fs/promises";
|
|
30775
|
+
import path21 from "path";
|
|
29131
30776
|
async function pathExists(absPath) {
|
|
29132
30777
|
try {
|
|
29133
|
-
await
|
|
30778
|
+
await fs17.access(absPath);
|
|
29134
30779
|
return true;
|
|
29135
30780
|
} catch {
|
|
29136
30781
|
return false;
|
|
29137
30782
|
}
|
|
29138
30783
|
}
|
|
29139
30784
|
function sanitizeAttachmentFileName(originalName) {
|
|
29140
|
-
const basename =
|
|
29141
|
-
const extension =
|
|
30785
|
+
const basename = path21.basename(originalName).trim() || "attachment";
|
|
30786
|
+
const extension = path21.extname(basename).replace(/[^a-zA-Z0-9.]/g, "");
|
|
29142
30787
|
const rawStem = extension ? basename.slice(0, -extension.length) : basename;
|
|
29143
30788
|
const sanitizedStem = rawStem.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 64);
|
|
29144
30789
|
const stem = sanitizedStem || "attachment";
|
|
@@ -29146,7 +30791,7 @@ function sanitizeAttachmentFileName(originalName) {
|
|
|
29146
30791
|
return `${stem}-${randomUUID6().slice(0, 8)}${normalizedExtension}`;
|
|
29147
30792
|
}
|
|
29148
30793
|
function threadTempDirectoryPath2(workspacePath, localThreadId) {
|
|
29149
|
-
return
|
|
30794
|
+
return path21.join(workspacePath, ".temp", "threads", localThreadId);
|
|
29150
30795
|
}
|
|
29151
30796
|
var ThreadAttachmentCoordinator = class {
|
|
29152
30797
|
constructor(db) {
|
|
@@ -29175,7 +30820,7 @@ var ThreadAttachmentCoordinator = class {
|
|
|
29175
30820
|
});
|
|
29176
30821
|
}
|
|
29177
30822
|
const tempDirectory = threadTempDirectoryPath2(workspace.absPath, localThreadId);
|
|
29178
|
-
await
|
|
30823
|
+
await fs17.mkdir(tempDirectory, { recursive: true });
|
|
29179
30824
|
let rewrittenPrompt = input.prompt;
|
|
29180
30825
|
for (const attachment of attachments) {
|
|
29181
30826
|
if (!rewrittenPrompt.includes(attachment.manifest.placeholder)) {
|
|
@@ -29187,7 +30832,7 @@ var ThreadAttachmentCoordinator = class {
|
|
|
29187
30832
|
const savedFileName = sanitizeAttachmentFileName(
|
|
29188
30833
|
attachment.manifest.originalName
|
|
29189
30834
|
);
|
|
29190
|
-
await
|
|
30835
|
+
await fs17.writeFile(path21.join(tempDirectory, savedFileName), attachment.buffer);
|
|
29191
30836
|
const relativePath = `./.temp/threads/${localThreadId}/${savedFileName}`;
|
|
29192
30837
|
const replacementToken = attachment.manifest.kind === "photo" ? `[PHOTO ${relativePath}]` : `[FILE ${relativePath}]`;
|
|
29193
30838
|
rewrittenPrompt = rewrittenPrompt.split(attachment.manifest.placeholder).join(replacementToken);
|
|
@@ -29200,30 +30845,30 @@ var ThreadAttachmentCoordinator = class {
|
|
|
29200
30845
|
};
|
|
29201
30846
|
|
|
29202
30847
|
// src/thread-import-coordinator.ts
|
|
29203
|
-
import
|
|
29204
|
-
import
|
|
30848
|
+
import fs18 from "fs/promises";
|
|
30849
|
+
import path22 from "path";
|
|
29205
30850
|
async function pathExists2(absPath) {
|
|
29206
30851
|
try {
|
|
29207
|
-
await
|
|
30852
|
+
await fs18.access(absPath);
|
|
29208
30853
|
return true;
|
|
29209
30854
|
} catch {
|
|
29210
30855
|
return false;
|
|
29211
30856
|
}
|
|
29212
30857
|
}
|
|
29213
30858
|
async function resolveComparablePath2(absPath) {
|
|
29214
|
-
const resolved =
|
|
30859
|
+
const resolved = path22.resolve(absPath);
|
|
29215
30860
|
if (await pathExists2(resolved)) {
|
|
29216
|
-
return
|
|
30861
|
+
return fs18.realpath(resolved);
|
|
29217
30862
|
}
|
|
29218
|
-
const parentPath =
|
|
30863
|
+
const parentPath = path22.dirname(resolved);
|
|
29219
30864
|
if (parentPath === resolved) {
|
|
29220
30865
|
return resolved;
|
|
29221
30866
|
}
|
|
29222
30867
|
const resolvedParent = await resolveComparablePath2(parentPath);
|
|
29223
|
-
return
|
|
30868
|
+
return path22.join(resolvedParent, path22.basename(resolved));
|
|
29224
30869
|
}
|
|
29225
30870
|
async function resolveImportedWorkspacePath(candidatePath) {
|
|
29226
|
-
if (!
|
|
30871
|
+
if (!path22.isAbsolute(candidatePath)) {
|
|
29227
30872
|
throw new HttpError(400, {
|
|
29228
30873
|
code: "bad_request",
|
|
29229
30874
|
message: "Imported session path must be absolute."
|
|
@@ -29232,12 +30877,33 @@ async function resolveImportedWorkspacePath(candidatePath) {
|
|
|
29232
30877
|
return resolveComparablePath2(candidatePath);
|
|
29233
30878
|
}
|
|
29234
30879
|
var ThreadImportCoordinator = class {
|
|
29235
|
-
constructor(db, sessionCoordinator
|
|
30880
|
+
constructor(db, sessionCoordinator) {
|
|
29236
30881
|
this.db = db;
|
|
29237
30882
|
this.sessionCoordinator = sessionCoordinator;
|
|
29238
30883
|
}
|
|
29239
30884
|
db;
|
|
29240
30885
|
sessionCoordinator;
|
|
30886
|
+
async listImportCandidates(providerInput, agentId) {
|
|
30887
|
+
const provider2 = normalizeAgentBackendId(providerInput ?? "codex") ?? "codex";
|
|
30888
|
+
const sessions = await this.sessionCoordinator.listImportSessions(provider2, agentId);
|
|
30889
|
+
return sessions.filter((session) => Boolean(session.providerSessionId.trim()) && path22.isAbsolute(session.cwd) && !getThreadRecordByProviderSessionId(
|
|
30890
|
+
this.db,
|
|
30891
|
+
provider2,
|
|
30892
|
+
session.providerSessionId
|
|
30893
|
+
)).map((session) => ({
|
|
30894
|
+
provider: provider2,
|
|
30895
|
+
agentId: session.agentId ?? null,
|
|
30896
|
+
sessionId: session.providerSessionId,
|
|
30897
|
+
cwd: session.cwd,
|
|
30898
|
+
title: session.title?.trim() || session.preview?.trim() || "Untitled session",
|
|
30899
|
+
preview: session.preview,
|
|
30900
|
+
createdAt: session.createdAt,
|
|
30901
|
+
updatedAt: session.updatedAt,
|
|
30902
|
+
historyStatus: "unknown"
|
|
30903
|
+
})).sort((left, right) => (right.updatedAt ?? right.createdAt ?? "").localeCompare(
|
|
30904
|
+
left.updatedAt ?? left.createdAt ?? ""
|
|
30905
|
+
));
|
|
30906
|
+
}
|
|
29241
30907
|
async importLocalThread(input) {
|
|
29242
30908
|
const normalizedSessionId = input.sessionId.trim();
|
|
29243
30909
|
if (!normalizedSessionId) {
|
|
@@ -29270,12 +30936,13 @@ var ThreadImportCoordinator = class {
|
|
|
29270
30936
|
if (!workspace) {
|
|
29271
30937
|
workspace = createWorkspaceRecord(this.db, {
|
|
29272
30938
|
absPath: importedPath,
|
|
29273
|
-
label:
|
|
30939
|
+
label: path22.basename(importedPath) || "workspace"
|
|
29274
30940
|
});
|
|
29275
30941
|
}
|
|
29276
30942
|
const created = createThreadRecord(this.db, {
|
|
29277
30943
|
workspaceId: workspace.id,
|
|
29278
30944
|
provider: importSession.provider,
|
|
30945
|
+
agentId: importSession.agentId ?? null,
|
|
29279
30946
|
providerSessionId: importSession.sessionId,
|
|
29280
30947
|
title: importSession.title,
|
|
29281
30948
|
model: importSession.model,
|
|
@@ -29291,7 +30958,7 @@ var ThreadImportCoordinator = class {
|
|
|
29291
30958
|
return created.id;
|
|
29292
30959
|
}
|
|
29293
30960
|
async assertImportedThreadReadyForPrompt(input) {
|
|
29294
|
-
if (input.source !== "local_codex_import") {
|
|
30961
|
+
if (input.source !== "local_codex_import" && input.source !== "local_provider_import") {
|
|
29295
30962
|
return;
|
|
29296
30963
|
}
|
|
29297
30964
|
const loadedIds = await input.listLoadedProviderSessionIds(input.provider);
|
|
@@ -29303,7 +30970,7 @@ var ThreadImportCoordinator = class {
|
|
|
29303
30970
|
}
|
|
29304
30971
|
}
|
|
29305
30972
|
async ensureImportedThreadConnectedForImplementation(input) {
|
|
29306
|
-
if (input.source !== "local_codex_import") {
|
|
30973
|
+
if (input.source !== "local_codex_import" && input.source !== "local_provider_import") {
|
|
29307
30974
|
return;
|
|
29308
30975
|
}
|
|
29309
30976
|
const loadedIds = await input.listLoadedProviderSessionIds(input.provider);
|
|
@@ -29356,7 +31023,7 @@ function pluginDeveloperInstructions(pluginService) {
|
|
|
29356
31023
|
}
|
|
29357
31024
|
async function pathExists3(absPath) {
|
|
29358
31025
|
try {
|
|
29359
|
-
await
|
|
31026
|
+
await fs19.access(absPath);
|
|
29360
31027
|
return true;
|
|
29361
31028
|
} catch {
|
|
29362
31029
|
return false;
|
|
@@ -29398,8 +31065,7 @@ var ThreadService = class {
|
|
|
29398
31065
|
);
|
|
29399
31066
|
this.importCoordinator = new ThreadImportCoordinator(
|
|
29400
31067
|
db,
|
|
29401
|
-
this.sessionCoordinator
|
|
29402
|
-
workspaceRoot
|
|
31068
|
+
this.sessionCoordinator
|
|
29403
31069
|
);
|
|
29404
31070
|
this.promptTurnCoordinator = new ThreadPromptTurnCoordinator(
|
|
29405
31071
|
db,
|
|
@@ -29445,6 +31111,12 @@ var ThreadService = class {
|
|
|
29445
31111
|
},
|
|
29446
31112
|
syncAfterRemoteSession: (localThreadId, remoteSession) => {
|
|
29447
31113
|
const updated = getThreadRecordById(this.db, localThreadId);
|
|
31114
|
+
if (updated.provider === "acp") {
|
|
31115
|
+
this.historyPersistence.persistHydratedTurns(
|
|
31116
|
+
updated.id,
|
|
31117
|
+
remoteSession.turns
|
|
31118
|
+
);
|
|
31119
|
+
}
|
|
29448
31120
|
this.syncPendingPlanDecisionRequest(
|
|
29449
31121
|
updated.id,
|
|
29450
31122
|
updated.collaborationMode,
|
|
@@ -29464,6 +31136,11 @@ var ThreadService = class {
|
|
|
29464
31136
|
emitThreadEvent: (type, threadId, payload) => this.emitThreadEvent(type, threadId, payload),
|
|
29465
31137
|
requireProviderSessionId: (record2) => this.requireProviderSessionId(record2),
|
|
29466
31138
|
runtimeForProvider: (provider2) => this.runtimeForProvider(provider2),
|
|
31139
|
+
capabilitiesForRecord: (record2) => this.providerRuntime.resolveCapabilitiesFor({
|
|
31140
|
+
provider: record2.provider,
|
|
31141
|
+
agentId: record2.agentId ?? null,
|
|
31142
|
+
providerSessionId: record2.providerSessionId ?? null
|
|
31143
|
+
}),
|
|
29467
31144
|
appendGoalActivityNote: (threadId, objective) => this.auxiliaryState.appendActivityNote(threadId, {
|
|
29468
31145
|
kind: "goal",
|
|
29469
31146
|
text: objective
|
|
@@ -29492,6 +31169,7 @@ var ThreadService = class {
|
|
|
29492
31169
|
this.liveState,
|
|
29493
31170
|
this.auxiliaryState,
|
|
29494
31171
|
{
|
|
31172
|
+
clearHistoryPersistence: (localThreadId) => this.historyPersistence.clearThread(localThreadId),
|
|
29495
31173
|
invalidateThreadDetailCache: (localThreadId) => this.invalidateThreadDetailCache(localThreadId)
|
|
29496
31174
|
}
|
|
29497
31175
|
);
|
|
@@ -29509,7 +31187,7 @@ var ThreadService = class {
|
|
|
29509
31187
|
this.sessionCoordinator,
|
|
29510
31188
|
{
|
|
29511
31189
|
buildThreadPatch: (remoteSession, model, reasoningEffort) => buildThreadPatch(remoteSession, model, reasoningEffort),
|
|
29512
|
-
fastModeForProvider: (provider2, fastMode) => this.fastModeForProvider(provider2, fastMode),
|
|
31190
|
+
fastModeForProvider: (provider2, fastMode, scope) => this.fastModeForProvider(provider2, fastMode, scope),
|
|
29513
31191
|
getThreadDetail: (localThreadId) => this.getThreadDetail(localThreadId),
|
|
29514
31192
|
invalidateThreadDetailCache: (localThreadId) => this.invalidateThreadDetailCache(localThreadId),
|
|
29515
31193
|
normalizeCollaborationMode,
|
|
@@ -29530,7 +31208,8 @@ var ThreadService = class {
|
|
|
29530
31208
|
input.itemId,
|
|
29531
31209
|
input.delta,
|
|
29532
31210
|
input.sequence,
|
|
29533
|
-
input.createdAt
|
|
31211
|
+
input.createdAt,
|
|
31212
|
+
input.checkpoint
|
|
29534
31213
|
),
|
|
29535
31214
|
clearPendingPlanDecisionRequests: (localThreadId, emitEvents) => this.clearPendingPlanDecisionRequests(localThreadId, emitEvents),
|
|
29536
31215
|
clearPendingSteersForTurn: (localThreadId, turnId) => this.auxiliaryState.clearPendingSteersForTurn(localThreadId, turnId),
|
|
@@ -29670,14 +31349,8 @@ var ThreadService = class {
|
|
|
29670
31349
|
providerSessionId
|
|
29671
31350
|
);
|
|
29672
31351
|
}
|
|
29673
|
-
|
|
29674
|
-
return this.providerRuntime.
|
|
29675
|
-
}
|
|
29676
|
-
fastModeForProvider(provider2, fastMode) {
|
|
29677
|
-
return this.providerRuntime.fastModeForProvider(provider2, fastMode);
|
|
29678
|
-
}
|
|
29679
|
-
performanceModeForRecord(record2) {
|
|
29680
|
-
return this.providerRuntime.performanceModeForRecord(record2);
|
|
31352
|
+
fastModeForProvider(provider2, fastMode, scope = {}) {
|
|
31353
|
+
return this.providerRuntime.fastModeForProvider(provider2, fastMode, scope);
|
|
29681
31354
|
}
|
|
29682
31355
|
async handleProviderRequest(request) {
|
|
29683
31356
|
await this.handleProviderRuntimeRequest(request);
|
|
@@ -29767,7 +31440,7 @@ var ThreadService = class {
|
|
|
29767
31440
|
updateThreadRecord(this.db, created.id, {
|
|
29768
31441
|
...buildThreadPatch(
|
|
29769
31442
|
session.response.session,
|
|
29770
|
-
input.model,
|
|
31443
|
+
session.response.model ?? input.model,
|
|
29771
31444
|
session.response.reasoningEffort ?? session.reasoningEffort
|
|
29772
31445
|
),
|
|
29773
31446
|
title: session.normalizedTitle === DEFAULT_THREAD_TITLE2 && session.response.session.title && session.response.session.title.trim() !== GENERIC_REMOTE_THREAD_TITLE ? truncateAutoThreadTitle(session.response.session.title) : session.normalizedTitle
|
|
@@ -29779,6 +31452,9 @@ var ThreadService = class {
|
|
|
29779
31452
|
const localThreadId = await this.importCoordinator.importLocalThread(input);
|
|
29780
31453
|
return this.getThreadDetail(localThreadId);
|
|
29781
31454
|
}
|
|
31455
|
+
async listImportCandidates(provider2, agentId) {
|
|
31456
|
+
return this.importCoordinator.listImportCandidates(provider2, agentId);
|
|
31457
|
+
}
|
|
29782
31458
|
async getThreadDetail(localThreadId, options = {}) {
|
|
29783
31459
|
const record2 = this.requireThreadRecord(localThreadId);
|
|
29784
31460
|
const workspace = this.requireWorkspaceForThread(record2);
|
|
@@ -30138,6 +31814,7 @@ var ThreadService = class {
|
|
|
30138
31814
|
}
|
|
30139
31815
|
await this.sessionCoordinator.compactThreadSession({
|
|
30140
31816
|
provider: record2.provider,
|
|
31817
|
+
agentId: record2.agentId,
|
|
30141
31818
|
providerSessionId
|
|
30142
31819
|
});
|
|
30143
31820
|
const updated = getThreadRecordById(this.db, localThreadId);
|
|
@@ -30285,7 +31962,12 @@ var ThreadService = class {
|
|
|
30285
31962
|
}
|
|
30286
31963
|
const providerSessionId = this.requireProviderSessionId(record2);
|
|
30287
31964
|
const runtime = this.runtimeForProvider(record2.provider);
|
|
30288
|
-
|
|
31965
|
+
const capabilities = this.providerRuntime.capabilitiesFor({
|
|
31966
|
+
provider: record2.provider,
|
|
31967
|
+
agentId: record2.agentId ?? null,
|
|
31968
|
+
providerSessionId
|
|
31969
|
+
});
|
|
31970
|
+
if (!runtime.sendInput || !capabilities.turns.steer) {
|
|
30289
31971
|
throw new HttpError(409, {
|
|
30290
31972
|
code: "conflict",
|
|
30291
31973
|
message: "This backend does not support steering an active turn."
|
|
@@ -30399,10 +32081,6 @@ var ThreadService = class {
|
|
|
30399
32081
|
async handleRuntimeEvent(event) {
|
|
30400
32082
|
await this.runtimeEventProjector.handleRuntimeEvent(event);
|
|
30401
32083
|
}
|
|
30402
|
-
runtimeSupportsLiveRunningTurnInput(provider2) {
|
|
30403
|
-
const runtime = this.runtimeForProvider(provider2);
|
|
30404
|
-
return Boolean(runtime.sendInput && runtime.capabilities.turns.steer);
|
|
30405
|
-
}
|
|
30406
32084
|
shouldPreserveCompletedPendingSteer(localThreadId) {
|
|
30407
32085
|
const record2 = getThreadRecordById(this.db, localThreadId);
|
|
30408
32086
|
if (!record2) {
|
|
@@ -30496,7 +32174,7 @@ var ThreadService = class {
|
|
|
30496
32174
|
}
|
|
30497
32175
|
toThreadDto(record2, loadedIds) {
|
|
30498
32176
|
return toThreadDto(record2, loadedIds, {
|
|
30499
|
-
fastModeForProvider: (provider2, fastMode) => this.fastModeForProvider(provider2, fastMode),
|
|
32177
|
+
fastModeForProvider: (provider2, fastMode, scope) => this.fastModeForProvider(provider2, fastMode, scope),
|
|
30500
32178
|
getThreadContextUsage: (localThreadId) => this.getThreadContextUsage(localThreadId)
|
|
30501
32179
|
});
|
|
30502
32180
|
}
|
|
@@ -30511,8 +32189,8 @@ var ThreadService = class {
|
|
|
30511
32189
|
listPendingRequests(localThreadId, options = {}) {
|
|
30512
32190
|
return this.requestCoordinator.listPendingRequests(localThreadId, options);
|
|
30513
32191
|
}
|
|
30514
|
-
appendLiveAgentMessageDelta(localThreadId, turnId, itemId, delta, sequence, createdAt) {
|
|
30515
|
-
this.liveState.appendLiveAgentMessageDelta({
|
|
32192
|
+
appendLiveAgentMessageDelta(localThreadId, turnId, itemId, delta, sequence, createdAt, checkpoint = false) {
|
|
32193
|
+
const item = this.liveState.appendLiveAgentMessageDelta({
|
|
30516
32194
|
localThreadId,
|
|
30517
32195
|
turnId,
|
|
30518
32196
|
itemId,
|
|
@@ -30520,6 +32198,13 @@ var ThreadService = class {
|
|
|
30520
32198
|
sequence,
|
|
30521
32199
|
createdAt
|
|
30522
32200
|
});
|
|
32201
|
+
if (checkpoint) {
|
|
32202
|
+
this.historyPersistence.checkpointLiveAgentMessage(
|
|
32203
|
+
localThreadId,
|
|
32204
|
+
turnId,
|
|
32205
|
+
item
|
|
32206
|
+
);
|
|
32207
|
+
}
|
|
30523
32208
|
}
|
|
30524
32209
|
createPendingPlanDecisionRequest(localThreadId, turnId, emitEvents) {
|
|
30525
32210
|
this.requestCoordinator.createPendingPlanDecisionRequest(
|
|
@@ -30598,8 +32283,8 @@ function parseQueuedTurnConfig(value) {
|
|
|
30598
32283
|
}
|
|
30599
32284
|
|
|
30600
32285
|
// src/routes/agent-runtimes.ts
|
|
30601
|
-
import
|
|
30602
|
-
import
|
|
32286
|
+
import fs20 from "fs/promises";
|
|
32287
|
+
import path23 from "path";
|
|
30603
32288
|
import { fileURLToPath } from "url";
|
|
30604
32289
|
import { z as z6 } from "zod";
|
|
30605
32290
|
|
|
@@ -30619,8 +32304,8 @@ var modelQuerySchema = z6.object({
|
|
|
30619
32304
|
agentId: z6.string().min(1).optional(),
|
|
30620
32305
|
cwd: z6.string().min(1).optional()
|
|
30621
32306
|
});
|
|
30622
|
-
var repositoryRoot =
|
|
30623
|
-
|
|
32307
|
+
var repositoryRoot = path23.resolve(
|
|
32308
|
+
path23.dirname(fileURLToPath(import.meta.url)),
|
|
30624
32309
|
"..",
|
|
30625
32310
|
"..",
|
|
30626
32311
|
"..",
|
|
@@ -30792,6 +32477,21 @@ async function registerAgentRuntimeRoutes(app2) {
|
|
|
30792
32477
|
}
|
|
30793
32478
|
return (await runtime.listAgentOptions()).map(modelDto);
|
|
30794
32479
|
});
|
|
32480
|
+
app2.get("/api/agent-runtimes/:provider/capabilities", async (request) => {
|
|
32481
|
+
const { provider: provider2 } = providerParamSchema.parse(request.params);
|
|
32482
|
+
const query = z6.object({ agentId: z6.string().min(1) }).parse(request.query);
|
|
32483
|
+
const runtime = app2.services.agentRuntimes.getOptional(provider2);
|
|
32484
|
+
if (!runtime) {
|
|
32485
|
+
throw providerNotConfigured(provider2);
|
|
32486
|
+
}
|
|
32487
|
+
if (!runtime.getAgentCapabilitySnapshot) {
|
|
32488
|
+
throw new HttpError(409, {
|
|
32489
|
+
code: "conflict",
|
|
32490
|
+
message: `${runtime.displayName} does not expose per-agent capabilities.`
|
|
32491
|
+
});
|
|
32492
|
+
}
|
|
32493
|
+
return runtime.getAgentCapabilitySnapshot(query.agentId);
|
|
32494
|
+
});
|
|
30795
32495
|
app2.post("/api/agent-runtimes/:provider/build-restart", async (request) => {
|
|
30796
32496
|
if (!app2.services.config.managementRoutesEnabled) {
|
|
30797
32497
|
throw new HttpError(403, {
|
|
@@ -30843,7 +32543,7 @@ async function refreshBackendInstallation(app2, runtime) {
|
|
|
30843
32543
|
const [cliVersion, sdkVersion, latestCliVersion] = await Promise.all([
|
|
30844
32544
|
commandVersion(command, ["--version"]),
|
|
30845
32545
|
installedPackageVersion("@anthropic-ai/claude-agent-sdk", [
|
|
30846
|
-
|
|
32546
|
+
path23.join(repositoryRoot, "packages", "claude", "node_modules")
|
|
30847
32547
|
]),
|
|
30848
32548
|
latestPackageVersion("@anthropic-ai/claude-code")
|
|
30849
32549
|
]);
|
|
@@ -30862,7 +32562,7 @@ async function refreshBackendInstallation(app2, runtime) {
|
|
|
30862
32562
|
commandVersion(command, ["--version"]),
|
|
30863
32563
|
latestPackageVersion("opencode-ai"),
|
|
30864
32564
|
installedPackageVersion("@opencode-ai/sdk", [
|
|
30865
|
-
|
|
32565
|
+
path23.join(repositoryRoot, "packages", "opencode", "node_modules")
|
|
30866
32566
|
])
|
|
30867
32567
|
]);
|
|
30868
32568
|
runtime.installation.installed = Boolean(cliVersion && sdkVersion);
|
|
@@ -30881,13 +32581,13 @@ async function refreshBackendInstallation(app2, runtime) {
|
|
|
30881
32581
|
async function installedPackageVersion(packageName, extraNodeModuleRoots = []) {
|
|
30882
32582
|
const globalRoot = await npmGlobalRoot3();
|
|
30883
32583
|
if (globalRoot) {
|
|
30884
|
-
const global = await packageVersionFromPath(
|
|
32584
|
+
const global = await packageVersionFromPath(path23.join(globalRoot, packageName, "package.json"));
|
|
30885
32585
|
if (global) {
|
|
30886
32586
|
return global;
|
|
30887
32587
|
}
|
|
30888
32588
|
}
|
|
30889
32589
|
for (const root of extraNodeModuleRoots) {
|
|
30890
|
-
const local = await packageVersionFromPath(
|
|
32590
|
+
const local = await packageVersionFromPath(path23.join(root, packageName, "package.json"));
|
|
30891
32591
|
if (local) {
|
|
30892
32592
|
return local;
|
|
30893
32593
|
}
|
|
@@ -30940,11 +32640,11 @@ function versionStringContains(installedVersion, latestVersion) {
|
|
|
30940
32640
|
return Boolean(installedVersion && latestVersion && installedVersion.includes(latestVersion));
|
|
30941
32641
|
}
|
|
30942
32642
|
async function packageVersionFromNode(packageName) {
|
|
30943
|
-
return packageVersionFromPath(
|
|
32643
|
+
return packageVersionFromPath(path23.resolve("node_modules", packageName, "package.json"));
|
|
30944
32644
|
}
|
|
30945
32645
|
async function packageVersionFromPath(packageJsonPath) {
|
|
30946
32646
|
try {
|
|
30947
|
-
const parsed = JSON.parse(await
|
|
32647
|
+
const parsed = JSON.parse(await fs20.readFile(packageJsonPath, "utf8"));
|
|
30948
32648
|
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
30949
32649
|
} catch {
|
|
30950
32650
|
return null;
|
|
@@ -30976,7 +32676,7 @@ async function npmGlobalBin(platform = process.platform) {
|
|
|
30976
32676
|
return firstLine(result.stdout);
|
|
30977
32677
|
}
|
|
30978
32678
|
const prefix = await npmGlobalPrefix();
|
|
30979
|
-
return prefix ? platform === "win32" ? prefix :
|
|
32679
|
+
return prefix ? platform === "win32" ? prefix : path23.join(prefix, "bin") : null;
|
|
30980
32680
|
}
|
|
30981
32681
|
async function npmGlobalPrefix() {
|
|
30982
32682
|
const result = await runProcess({
|
|
@@ -31271,8 +32971,8 @@ async function registerSystemRoutes(app2) {
|
|
|
31271
32971
|
}
|
|
31272
32972
|
|
|
31273
32973
|
// src/routes/threads.ts
|
|
31274
|
-
import
|
|
31275
|
-
import
|
|
32974
|
+
import fs21 from "fs/promises";
|
|
32975
|
+
import path24 from "path";
|
|
31276
32976
|
import { z as z8 } from "zod";
|
|
31277
32977
|
var reasoningEffortValues = [
|
|
31278
32978
|
"none",
|
|
@@ -31332,6 +33032,10 @@ var importThreadSchema = z8.object({
|
|
|
31332
33032
|
sessionId: z8.string().min(1),
|
|
31333
33033
|
provider: agentBackendIdSchema.optional()
|
|
31334
33034
|
});
|
|
33035
|
+
var importThreadCandidatesQuerySchema = z8.object({
|
|
33036
|
+
provider: agentBackendIdSchema.optional(),
|
|
33037
|
+
agentId: z8.string().min(1).optional()
|
|
33038
|
+
});
|
|
31335
33039
|
var resumeThreadSchema = z8.object({
|
|
31336
33040
|
model: z8.string().min(1).optional()
|
|
31337
33041
|
});
|
|
@@ -31563,6 +33267,13 @@ async function registerThreadRoutes(app2) {
|
|
|
31563
33267
|
};
|
|
31564
33268
|
return app2.services.threadService.importThread(input);
|
|
31565
33269
|
});
|
|
33270
|
+
app2.get("/api/threads/import-candidates", async (request) => {
|
|
33271
|
+
const query = importThreadCandidatesQuerySchema.parse(request.query);
|
|
33272
|
+
return app2.services.threadService.listImportCandidates(
|
|
33273
|
+
query.provider,
|
|
33274
|
+
query.agentId
|
|
33275
|
+
);
|
|
33276
|
+
});
|
|
31566
33277
|
app2.get("/api/threads/:id", async (request) => {
|
|
31567
33278
|
const params = z8.object({ id: z8.string().uuid() }).parse(request.params);
|
|
31568
33279
|
const query = threadDetailQuerySchema.parse(request.query);
|
|
@@ -31645,15 +33356,15 @@ async function registerThreadRoutes(app2) {
|
|
|
31645
33356
|
message: "Workspace was not found for this thread."
|
|
31646
33357
|
});
|
|
31647
33358
|
}
|
|
31648
|
-
const candidatePath =
|
|
31649
|
-
const requestedPath = await
|
|
33359
|
+
const candidatePath = path24.isAbsolute(query.path) ? query.path : path24.resolve(workspace.absPath, query.path);
|
|
33360
|
+
const requestedPath = await fs21.realpath(candidatePath).catch(() => null);
|
|
31650
33361
|
if (!requestedPath) {
|
|
31651
33362
|
throw new HttpError(404, {
|
|
31652
33363
|
code: "not_found",
|
|
31653
33364
|
message: "Image file was not found."
|
|
31654
33365
|
});
|
|
31655
33366
|
}
|
|
31656
|
-
const resolvedWorkspaceRoot = await
|
|
33367
|
+
const resolvedWorkspaceRoot = await fs21.realpath(app2.services.config.workspaceRoot).catch(() => path24.resolve(app2.services.config.workspaceRoot));
|
|
31657
33368
|
try {
|
|
31658
33369
|
await assertPathWithinRoot(resolvedWorkspaceRoot, requestedPath);
|
|
31659
33370
|
} catch {
|
|
@@ -31662,7 +33373,7 @@ async function registerThreadRoutes(app2) {
|
|
|
31662
33373
|
message: "Image path must stay within the configured workspace root."
|
|
31663
33374
|
});
|
|
31664
33375
|
}
|
|
31665
|
-
const stats = await
|
|
33376
|
+
const stats = await fs21.stat(requestedPath).catch(() => null);
|
|
31666
33377
|
if (!stats?.isFile()) {
|
|
31667
33378
|
throw new HttpError(404, {
|
|
31668
33379
|
code: "not_found",
|
|
@@ -31673,7 +33384,7 @@ async function registerThreadRoutes(app2) {
|
|
|
31673
33384
|
const contentType = lowerPath.endsWith(".png") ? "image/png" : lowerPath.endsWith(".jpg") || lowerPath.endsWith(".jpeg") ? "image/jpeg" : lowerPath.endsWith(".gif") ? "image/gif" : lowerPath.endsWith(".webp") ? "image/webp" : lowerPath.endsWith(".svg") ? "image/svg+xml" : lowerPath.endsWith(".heic") ? "image/heic" : lowerPath.endsWith(".heif") ? "image/heif" : "application/octet-stream";
|
|
31674
33385
|
reply.header("Content-Type", contentType);
|
|
31675
33386
|
reply.header("Cache-Control", "private, max-age=60");
|
|
31676
|
-
return reply.send(await
|
|
33387
|
+
return reply.send(await fs21.readFile(requestedPath));
|
|
31677
33388
|
});
|
|
31678
33389
|
app2.patch("/api/threads/:id", async (request) => {
|
|
31679
33390
|
const params = z8.object({ id: z8.string().uuid() }).parse(request.params);
|
|
@@ -31869,34 +33580,34 @@ async function registerThreadRoutes(app2) {
|
|
|
31869
33580
|
}
|
|
31870
33581
|
|
|
31871
33582
|
// src/routes/workspaces.ts
|
|
31872
|
-
import
|
|
33583
|
+
import fs24 from "fs/promises";
|
|
31873
33584
|
import { createReadStream } from "fs";
|
|
31874
|
-
import
|
|
33585
|
+
import path27 from "path";
|
|
31875
33586
|
import { Readable as Readable2 } from "stream";
|
|
31876
33587
|
import { z as z9 } from "zod";
|
|
31877
33588
|
|
|
31878
33589
|
// src/workspace-artifact-service.ts
|
|
31879
|
-
import
|
|
31880
|
-
import
|
|
33590
|
+
import fs22 from "fs/promises";
|
|
33591
|
+
import path25 from "path";
|
|
31881
33592
|
function artifactRoot(record2) {
|
|
31882
|
-
return
|
|
33593
|
+
return path25.join(record2.absPath, ".remote-codex", "artifacts");
|
|
31883
33594
|
}
|
|
31884
33595
|
function artifactFilePath(record2, artifactId) {
|
|
31885
|
-
return
|
|
33596
|
+
return path25.join(artifactRoot(record2), artifactId, "artifact.bin");
|
|
31886
33597
|
}
|
|
31887
33598
|
function artifactMetadataPath(record2, artifactId) {
|
|
31888
|
-
return
|
|
33599
|
+
return path25.join(artifactRoot(record2), artifactId, "metadata.json");
|
|
31889
33600
|
}
|
|
31890
33601
|
function safeArtifactFileName(value) {
|
|
31891
|
-
return
|
|
33602
|
+
return path25.basename(value).replace(/[^a-zA-Z0-9_. -]/g, "_") || "artifact.bin";
|
|
31892
33603
|
}
|
|
31893
33604
|
function artifactIdFromName(name) {
|
|
31894
|
-
const base =
|
|
33605
|
+
const base = path25.basename(name).replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
|
|
31895
33606
|
return `${base || "artifact"}-${Date.now().toString(36)}`;
|
|
31896
33607
|
}
|
|
31897
33608
|
async function readWorkspaceArtifactMetadata(record2, artifactId) {
|
|
31898
33609
|
try {
|
|
31899
|
-
const raw = await
|
|
33610
|
+
const raw = await fs22.readFile(artifactMetadataPath(record2, artifactId), "utf8");
|
|
31900
33611
|
return JSON.parse(raw);
|
|
31901
33612
|
} catch (error) {
|
|
31902
33613
|
if (error.code === "ENOENT") {
|
|
@@ -31911,7 +33622,7 @@ async function readWorkspaceArtifactMetadata(record2, artifactId) {
|
|
|
31911
33622
|
async function listWorkspaceArtifacts(record2) {
|
|
31912
33623
|
let entries;
|
|
31913
33624
|
try {
|
|
31914
|
-
entries = await
|
|
33625
|
+
entries = await fs22.readdir(artifactRoot(record2));
|
|
31915
33626
|
} catch (error) {
|
|
31916
33627
|
if (error.code === "ENOENT") {
|
|
31917
33628
|
return [];
|
|
@@ -31947,10 +33658,10 @@ async function createWorkspaceArtifact({
|
|
|
31947
33658
|
message: "Artifact content must not be empty."
|
|
31948
33659
|
});
|
|
31949
33660
|
}
|
|
31950
|
-
const dir =
|
|
31951
|
-
await
|
|
33661
|
+
const dir = path25.dirname(artifactFilePath(record2, artifactId));
|
|
33662
|
+
await fs22.mkdir(dir, { recursive: true, mode: 448 });
|
|
31952
33663
|
const filePath = artifactFilePath(record2, artifactId);
|
|
31953
|
-
await
|
|
33664
|
+
await fs22.writeFile(filePath, content, { flag: "wx" }).catch((error) => {
|
|
31954
33665
|
if (error.code === "EEXIST") {
|
|
31955
33666
|
throw new HttpError(409, {
|
|
31956
33667
|
code: "conflict",
|
|
@@ -31970,12 +33681,12 @@ async function createWorkspaceArtifact({
|
|
|
31970
33681
|
updatedAt: now,
|
|
31971
33682
|
metadata: metadata ?? {}
|
|
31972
33683
|
};
|
|
31973
|
-
await
|
|
33684
|
+
await fs22.writeFile(artifactMetadataPath(record2, artifactId), JSON.stringify(artifact, null, 2));
|
|
31974
33685
|
return artifact;
|
|
31975
33686
|
}
|
|
31976
33687
|
async function readWorkspaceArtifactContent(record2, artifactId) {
|
|
31977
33688
|
try {
|
|
31978
|
-
return await
|
|
33689
|
+
return await fs22.readFile(artifactFilePath(record2, artifactId));
|
|
31979
33690
|
} catch (error) {
|
|
31980
33691
|
if (error.code === "ENOENT") {
|
|
31981
33692
|
throw new HttpError(404, {
|
|
@@ -31988,7 +33699,7 @@ async function readWorkspaceArtifactContent(record2, artifactId) {
|
|
|
31988
33699
|
}
|
|
31989
33700
|
async function deleteWorkspaceArtifact(record2, artifactId) {
|
|
31990
33701
|
const artifact = await readWorkspaceArtifactMetadata(record2, artifactId);
|
|
31991
|
-
await
|
|
33702
|
+
await fs22.rm(path25.dirname(artifactFilePath(record2, artifactId)), {
|
|
31992
33703
|
recursive: true,
|
|
31993
33704
|
force: true
|
|
31994
33705
|
});
|
|
@@ -31996,9 +33707,9 @@ async function deleteWorkspaceArtifact(record2, artifactId) {
|
|
|
31996
33707
|
}
|
|
31997
33708
|
|
|
31998
33709
|
// src/workspace-file-service.ts
|
|
31999
|
-
import
|
|
33710
|
+
import fs23 from "fs/promises";
|
|
32000
33711
|
import os3 from "os";
|
|
32001
|
-
import
|
|
33712
|
+
import path26 from "path";
|
|
32002
33713
|
var PREVIEW_DEFAULT_LIMIT_BYTES = 5e4;
|
|
32003
33714
|
var WORKSPACE_UPLOAD_MAX_BYTES = 50 * 1024 * 1024;
|
|
32004
33715
|
var WORKSPACE_FOLDER_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
|
|
@@ -32034,7 +33745,7 @@ function toWorkspaceFileDto(file) {
|
|
|
32034
33745
|
};
|
|
32035
33746
|
}
|
|
32036
33747
|
function languageForPath(filePath) {
|
|
32037
|
-
const extension =
|
|
33748
|
+
const extension = path26.extname(filePath).slice(1).toLowerCase();
|
|
32038
33749
|
switch (extension) {
|
|
32039
33750
|
case "js":
|
|
32040
33751
|
case "jsx":
|
|
@@ -32080,18 +33791,18 @@ function languageForPath(filePath) {
|
|
|
32080
33791
|
}
|
|
32081
33792
|
}
|
|
32082
33793
|
function relativeWorkspacePath(rootPath, absPath) {
|
|
32083
|
-
const relative =
|
|
32084
|
-
return relative === "" ? "" : relative.split(
|
|
33794
|
+
const relative = path26.relative(rootPath, absPath);
|
|
33795
|
+
return relative === "" ? "" : relative.split(path26.sep).join("/");
|
|
32085
33796
|
}
|
|
32086
33797
|
async function resolveWorkspaceItemPath(rootPath, relativePath = "") {
|
|
32087
|
-
const candidate =
|
|
33798
|
+
const candidate = path26.resolve(rootPath, relativePath || ".");
|
|
32088
33799
|
const comparable = await assertPathWithinRoot(rootPath, candidate);
|
|
32089
33800
|
return comparable;
|
|
32090
33801
|
}
|
|
32091
33802
|
async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
32092
|
-
const stats = await
|
|
33803
|
+
const stats = await fs23.stat(absPath);
|
|
32093
33804
|
const relativePath = relativeWorkspacePath(rootPath, absPath);
|
|
32094
|
-
const name = relativePath ?
|
|
33805
|
+
const name = relativePath ? path26.basename(absPath) : path26.basename(rootPath);
|
|
32095
33806
|
if (!stats.isDirectory()) {
|
|
32096
33807
|
return {
|
|
32097
33808
|
name,
|
|
@@ -32109,7 +33820,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
32109
33820
|
};
|
|
32110
33821
|
const visible = [];
|
|
32111
33822
|
try {
|
|
32112
|
-
const directory = await
|
|
33823
|
+
const directory = await fs23.opendir(absPath);
|
|
32113
33824
|
let scanned = 0;
|
|
32114
33825
|
for await (const entry of directory) {
|
|
32115
33826
|
scanned += 1;
|
|
@@ -32144,7 +33855,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
32144
33855
|
});
|
|
32145
33856
|
node.children = (await Promise.all(
|
|
32146
33857
|
visible.map(async (entry) => {
|
|
32147
|
-
const childPath =
|
|
33858
|
+
const childPath = path26.join(absPath, entry.name);
|
|
32148
33859
|
try {
|
|
32149
33860
|
const childRelativePath = relativeWorkspacePath(rootPath, childPath);
|
|
32150
33861
|
if (entry.isDirectory()) {
|
|
@@ -32156,7 +33867,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
32156
33867
|
childrenLoaded: false
|
|
32157
33868
|
};
|
|
32158
33869
|
}
|
|
32159
|
-
const childStats = await
|
|
33870
|
+
const childStats = await fs23.stat(childPath);
|
|
32160
33871
|
return {
|
|
32161
33872
|
name: entry.name,
|
|
32162
33873
|
path: childRelativePath,
|
|
@@ -32171,7 +33882,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
32171
33882
|
return node;
|
|
32172
33883
|
}
|
|
32173
33884
|
function contentTypeForPath(filePath) {
|
|
32174
|
-
switch (
|
|
33885
|
+
switch (path26.extname(filePath).slice(1).toLowerCase()) {
|
|
32175
33886
|
case "png":
|
|
32176
33887
|
return "image/png";
|
|
32177
33888
|
case "jpg":
|
|
@@ -32201,15 +33912,15 @@ function contentTypeForPath(filePath) {
|
|
|
32201
33912
|
}
|
|
32202
33913
|
}
|
|
32203
33914
|
async function collectFolderZipEntries(rootPath, folderPath) {
|
|
32204
|
-
const folderName =
|
|
33915
|
+
const folderName = path26.basename(folderPath) || "workspace-folder";
|
|
32205
33916
|
const entries = [];
|
|
32206
33917
|
let totalBytes = 0;
|
|
32207
33918
|
const pending = [folderPath];
|
|
32208
33919
|
while (pending.length > 0) {
|
|
32209
33920
|
const current = pending.pop();
|
|
32210
|
-
const children = await
|
|
33921
|
+
const children = await fs23.readdir(current, { withFileTypes: true });
|
|
32211
33922
|
for (const child of children) {
|
|
32212
|
-
const childPath = await resolveWorkspaceItemPath(rootPath,
|
|
33923
|
+
const childPath = await resolveWorkspaceItemPath(rootPath, path26.relative(rootPath, path26.join(current, child.name)));
|
|
32213
33924
|
if (child.isDirectory()) {
|
|
32214
33925
|
pending.push(childPath);
|
|
32215
33926
|
continue;
|
|
@@ -32217,7 +33928,7 @@ async function collectFolderZipEntries(rootPath, folderPath) {
|
|
|
32217
33928
|
if (!child.isFile()) {
|
|
32218
33929
|
continue;
|
|
32219
33930
|
}
|
|
32220
|
-
const stats = await
|
|
33931
|
+
const stats = await fs23.stat(childPath);
|
|
32221
33932
|
totalBytes += stats.size;
|
|
32222
33933
|
entries.push({
|
|
32223
33934
|
absPath: childPath,
|
|
@@ -32268,8 +33979,8 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
32268
33979
|
const centralParts = [];
|
|
32269
33980
|
let offset = 0;
|
|
32270
33981
|
for (const entry of entries) {
|
|
32271
|
-
const data = await
|
|
32272
|
-
const name = Buffer.from(entry.archivePath.split(
|
|
33982
|
+
const data = await fs23.readFile(entry.absPath);
|
|
33983
|
+
const name = Buffer.from(entry.archivePath.split(path26.sep).join("/"), "utf8");
|
|
32273
33984
|
const checksum = crc32(data);
|
|
32274
33985
|
const { dosDate, dosTime } = zipDosDateTime(entry.updatedAt);
|
|
32275
33986
|
const localHeader = Buffer.alloc(30);
|
|
@@ -32316,19 +34027,19 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
32316
34027
|
endRecord.writeUInt32LE(centralSize, 12);
|
|
32317
34028
|
endRecord.writeUInt32LE(offset, 16);
|
|
32318
34029
|
endRecord.writeUInt16LE(0, 20);
|
|
32319
|
-
const tempDir = await
|
|
32320
|
-
const zipPath =
|
|
32321
|
-
await
|
|
34030
|
+
const tempDir = await fs23.mkdtemp(path26.join(os3.tmpdir(), "remote-codex-folder-download-"));
|
|
34031
|
+
const zipPath = path26.join(tempDir, `${path26.basename(folderPath) || "workspace-folder"}.zip`);
|
|
34032
|
+
await fs23.writeFile(zipPath, Buffer.concat([...localParts, ...centralParts, endRecord]));
|
|
32322
34033
|
return { zipPath, tempDir };
|
|
32323
34034
|
}
|
|
32324
34035
|
function cleanupTemporaryZip(zipPath, tempDir) {
|
|
32325
34036
|
return async () => {
|
|
32326
|
-
await
|
|
32327
|
-
await
|
|
34037
|
+
await fs23.rm(zipPath, { force: true }).catch(() => void 0);
|
|
34038
|
+
await fs23.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
32328
34039
|
};
|
|
32329
34040
|
}
|
|
32330
34041
|
function sanitizeUploadFilename(filename) {
|
|
32331
|
-
const baseName =
|
|
34042
|
+
const baseName = path26.basename(filename?.trim() || "upload");
|
|
32332
34043
|
if (!baseName || baseName === "." || baseName === "..") {
|
|
32333
34044
|
return "upload";
|
|
32334
34045
|
}
|
|
@@ -32340,7 +34051,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
32340
34051
|
const normalized = withoutQuery.replace(/[\\/]+$/, "");
|
|
32341
34052
|
const rawName = normalized.split(/[\\/:]/).filter(Boolean).at(-1) ?? "";
|
|
32342
34053
|
const repoName = rawName.endsWith(".git") ? rawName.slice(0, -4) : rawName;
|
|
32343
|
-
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(
|
|
34054
|
+
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(path26.sep)) {
|
|
32344
34055
|
throw new HttpError(400, {
|
|
32345
34056
|
code: "bad_request",
|
|
32346
34057
|
message: "Unable to infer a target directory from the Git URL."
|
|
@@ -32350,7 +34061,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
32350
34061
|
}
|
|
32351
34062
|
async function pathExists4(absPath) {
|
|
32352
34063
|
try {
|
|
32353
|
-
await
|
|
34064
|
+
await fs23.stat(absPath);
|
|
32354
34065
|
return true;
|
|
32355
34066
|
} catch (error) {
|
|
32356
34067
|
if (error.code === "ENOENT") {
|
|
@@ -32474,7 +34185,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32474
34185
|
});
|
|
32475
34186
|
app2.get("/api/workspaces/tree", async (request) => {
|
|
32476
34187
|
const query = treeQuerySchema.parse(request.query);
|
|
32477
|
-
const requestedPath = query.path ?
|
|
34188
|
+
const requestedPath = query.path ? path27.resolve(query.path) : app2.services.config.workspaceRoot;
|
|
32478
34189
|
const tree = await readWorkspaceTree({
|
|
32479
34190
|
rootPath: app2.services.config.workspaceRoot,
|
|
32480
34191
|
targetPath: requestedPath,
|
|
@@ -32501,7 +34212,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32501
34212
|
const params = z9.object({ id: z9.string().uuid() }).parse(request.params);
|
|
32502
34213
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
32503
34214
|
const record2 = requireWorkspaceRecord(app2, params.id);
|
|
32504
|
-
const rootPath = await
|
|
34215
|
+
const rootPath = await fs24.realpath(record2.absPath);
|
|
32505
34216
|
const targetPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
32506
34217
|
return buildWorkspaceTreeNode(rootPath, targetPath);
|
|
32507
34218
|
});
|
|
@@ -32520,9 +34231,9 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32520
34231
|
const params = z9.object({ id: z9.string().uuid() }).parse(request.params);
|
|
32521
34232
|
const query = workspacePreviewQuerySchema.parse(request.query);
|
|
32522
34233
|
const record2 = requireWorkspaceRecord(app2, params.id);
|
|
32523
|
-
const rootPath = await
|
|
34234
|
+
const rootPath = await fs24.realpath(record2.absPath);
|
|
32524
34235
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
32525
|
-
const stats = await
|
|
34236
|
+
const stats = await fs24.stat(filePath);
|
|
32526
34237
|
if (!stats.isFile()) {
|
|
32527
34238
|
throw new HttpError(400, {
|
|
32528
34239
|
code: "bad_request",
|
|
@@ -32531,7 +34242,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32531
34242
|
}
|
|
32532
34243
|
const offset = query.offset ?? 0;
|
|
32533
34244
|
const limit = query.limit ?? PREVIEW_DEFAULT_LIMIT_BYTES;
|
|
32534
|
-
const handle = await
|
|
34245
|
+
const handle = await fs24.open(filePath, "r");
|
|
32535
34246
|
try {
|
|
32536
34247
|
const length = Math.min(limit, Math.max(0, stats.size - offset));
|
|
32537
34248
|
const buffer = Buffer.alloc(length);
|
|
@@ -32539,7 +34250,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32539
34250
|
const nextOffset = offset + read.bytesRead;
|
|
32540
34251
|
return {
|
|
32541
34252
|
path: relativeWorkspacePath(rootPath, filePath),
|
|
32542
|
-
name:
|
|
34253
|
+
name: path27.basename(filePath),
|
|
32543
34254
|
content: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
32544
34255
|
language: languageForPath(filePath),
|
|
32545
34256
|
size: stats.size,
|
|
@@ -32554,9 +34265,9 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32554
34265
|
const params = z9.object({ id: z9.string().uuid() }).parse(request.params);
|
|
32555
34266
|
const query = workspacePreviewQuerySchema.pick({ path: true }).parse(request.query);
|
|
32556
34267
|
const record2 = requireWorkspaceRecord(app2, params.id);
|
|
32557
|
-
const rootPath = await
|
|
34268
|
+
const rootPath = await fs24.realpath(record2.absPath);
|
|
32558
34269
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
32559
|
-
const stats = await
|
|
34270
|
+
const stats = await fs24.stat(filePath);
|
|
32560
34271
|
if (!stats.isFile()) {
|
|
32561
34272
|
throw new HttpError(400, {
|
|
32562
34273
|
code: "bad_request",
|
|
@@ -32564,18 +34275,18 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32564
34275
|
});
|
|
32565
34276
|
}
|
|
32566
34277
|
reply.header("content-type", contentTypeForPath(filePath));
|
|
32567
|
-
return reply.send(Readable2.from(await
|
|
34278
|
+
return reply.send(Readable2.from(await fs24.readFile(filePath)));
|
|
32568
34279
|
});
|
|
32569
34280
|
app2.get("/api/workspaces/:id/files/download", async (request, reply) => {
|
|
32570
34281
|
const params = z9.object({ id: z9.string().uuid() }).parse(request.params);
|
|
32571
34282
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
32572
34283
|
const record2 = requireWorkspaceRecord(app2, params.id);
|
|
32573
|
-
const rootPath = await
|
|
34284
|
+
const rootPath = await fs24.realpath(record2.absPath);
|
|
32574
34285
|
const itemPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
32575
|
-
const stats = await
|
|
34286
|
+
const stats = await fs24.stat(itemPath);
|
|
32576
34287
|
if (stats.isDirectory()) {
|
|
32577
34288
|
const { zipPath, tempDir } = await createFolderZipFile(rootPath, itemPath);
|
|
32578
|
-
const filename2 = `${
|
|
34289
|
+
const filename2 = `${path27.basename(itemPath) || "workspace-folder"}.zip`;
|
|
32579
34290
|
const cleanup = cleanupTemporaryZip(zipPath, tempDir);
|
|
32580
34291
|
reply.raw.once("finish", () => void cleanup());
|
|
32581
34292
|
reply.raw.once("close", () => void cleanup());
|
|
@@ -32591,17 +34302,17 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32591
34302
|
message: "Only file and folder downloads are supported from this endpoint."
|
|
32592
34303
|
});
|
|
32593
34304
|
}
|
|
32594
|
-
const filename =
|
|
34305
|
+
const filename = path27.basename(itemPath);
|
|
32595
34306
|
reply.header("content-type", contentTypeForPath(itemPath)).header(
|
|
32596
34307
|
"content-disposition",
|
|
32597
34308
|
`attachment; filename="${filename}"; filename*=UTF-8''${encodeURIComponent(filename)}`
|
|
32598
34309
|
);
|
|
32599
|
-
return reply.send(Readable2.from(await
|
|
34310
|
+
return reply.send(Readable2.from(await fs24.readFile(itemPath)));
|
|
32600
34311
|
});
|
|
32601
34312
|
app2.post("/api/workspaces/:id/files/upload", async (request) => {
|
|
32602
34313
|
const params = z9.object({ id: z9.string().uuid() }).parse(request.params);
|
|
32603
34314
|
const record2 = requireWorkspaceRecord(app2, params.id);
|
|
32604
|
-
const rootPath = await
|
|
34315
|
+
const rootPath = await fs24.realpath(record2.absPath);
|
|
32605
34316
|
const uploadRequest = request;
|
|
32606
34317
|
if (!uploadRequest.isMultipart()) {
|
|
32607
34318
|
throw new HttpError(400, {
|
|
@@ -32656,7 +34367,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32656
34367
|
kind: "file",
|
|
32657
34368
|
file: {
|
|
32658
34369
|
path: file.path,
|
|
32659
|
-
name:
|
|
34370
|
+
name: path27.basename(file.path),
|
|
32660
34371
|
size: file.size
|
|
32661
34372
|
}
|
|
32662
34373
|
};
|
|
@@ -32732,7 +34443,7 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32732
34443
|
let validated;
|
|
32733
34444
|
if ("gitUrl" in body) {
|
|
32734
34445
|
const repoName = inferGitRepoName(body.gitUrl);
|
|
32735
|
-
const targetPath =
|
|
34446
|
+
const targetPath = path27.join(settings.devHome, repoName);
|
|
32736
34447
|
if (await pathExists4(targetPath)) {
|
|
32737
34448
|
throw new HttpError(409, {
|
|
32738
34449
|
code: "conflict",
|
|
@@ -32746,14 +34457,14 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32746
34457
|
validated = await validateWorkspacePath(app2.services.config.workspaceRoot, targetPath);
|
|
32747
34458
|
} else {
|
|
32748
34459
|
const requestedPath = body.absPath.trim();
|
|
32749
|
-
const isWorkspaceName = !
|
|
32750
|
-
if (!
|
|
34460
|
+
const isWorkspaceName = !path27.isAbsolute(requestedPath) && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(requestedPath) && requestedPath !== "." && requestedPath !== "..";
|
|
34461
|
+
if (!path27.isAbsolute(requestedPath) && !isWorkspaceName) {
|
|
32751
34462
|
throw new HttpError(400, {
|
|
32752
34463
|
code: "bad_request",
|
|
32753
34464
|
message: "Use a simple directory name, an absolute path, or a Git URL."
|
|
32754
34465
|
});
|
|
32755
34466
|
}
|
|
32756
|
-
const targetPath = isWorkspaceName ?
|
|
34467
|
+
const targetPath = isWorkspaceName ? path27.join(settings.devHome, requestedPath) : requestedPath;
|
|
32757
34468
|
validated = await validateWorkspacePath(app2.services.config.workspaceRoot, targetPath, {
|
|
32758
34469
|
devHome: settings.devHome,
|
|
32759
34470
|
createMissingLeaf: true
|
|
@@ -32860,8 +34571,8 @@ async function registerWorkspaceRoutes(app2) {
|
|
|
32860
34571
|
import { z as z10 } from "zod";
|
|
32861
34572
|
|
|
32862
34573
|
// src/plugins/plugin-service.ts
|
|
32863
|
-
import
|
|
32864
|
-
import
|
|
34574
|
+
import fs25 from "fs/promises";
|
|
34575
|
+
import path28 from "path";
|
|
32865
34576
|
var MANAGED_CODEX_MCP_BEGIN = "# BEGIN remote-codex managed plugin MCP servers";
|
|
32866
34577
|
var MANAGED_CODEX_MCP_END = "# END remote-codex managed plugin MCP servers";
|
|
32867
34578
|
var REMOTE_CODEX_MOLECULE_MCP_TOOL_NAME = "remote_codex_render_molecule";
|
|
@@ -32882,7 +34593,7 @@ function normalizeManagedCommand(server, repoRoot) {
|
|
|
32882
34593
|
if (server.name === "remote_codex_plugins") {
|
|
32883
34594
|
return {
|
|
32884
34595
|
command: process.execPath,
|
|
32885
|
-
args: [
|
|
34596
|
+
args: [path28.join(repoRoot, "bin", "remote-codex-plugin-mcp.mjs")]
|
|
32886
34597
|
};
|
|
32887
34598
|
}
|
|
32888
34599
|
return {
|
|
@@ -33081,10 +34792,10 @@ var PluginService = class {
|
|
|
33081
34792
|
if (!input.codexHome) {
|
|
33082
34793
|
return;
|
|
33083
34794
|
}
|
|
33084
|
-
const configPath =
|
|
34795
|
+
const configPath = path28.join(input.codexHome, "config.toml");
|
|
33085
34796
|
let current = "";
|
|
33086
34797
|
try {
|
|
33087
|
-
current = await
|
|
34798
|
+
current = await fs25.readFile(configPath, "utf8");
|
|
33088
34799
|
} catch (error) {
|
|
33089
34800
|
if (error.code !== "ENOENT") {
|
|
33090
34801
|
throw error;
|
|
@@ -33099,8 +34810,8 @@ var PluginService = class {
|
|
|
33099
34810
|
if (next === current) {
|
|
33100
34811
|
return;
|
|
33101
34812
|
}
|
|
33102
|
-
await
|
|
33103
|
-
await
|
|
34813
|
+
await fs25.mkdir(path28.dirname(configPath), { recursive: true });
|
|
34814
|
+
await fs25.writeFile(configPath, next, "utf8");
|
|
33104
34815
|
}
|
|
33105
34816
|
async importPlugin(input) {
|
|
33106
34817
|
const manifestInput = input.manifest ?? (input.manifestUrl ? await this.fetchManifestUrl(input.manifestUrl) : this.parseManifestJson(input.manifestJson));
|
|
@@ -33466,8 +35177,8 @@ async function registerAuthRoutes(app2) {
|
|
|
33466
35177
|
}
|
|
33467
35178
|
|
|
33468
35179
|
// src/provider-host-config-service.ts
|
|
33469
|
-
import
|
|
33470
|
-
import
|
|
35180
|
+
import fs26 from "fs/promises";
|
|
35181
|
+
import path29 from "path";
|
|
33471
35182
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
33472
35183
|
function providerError(message, statusCode = 404) {
|
|
33473
35184
|
const error = new Error(message);
|
|
@@ -33475,23 +35186,23 @@ function providerError(message, statusCode = 404) {
|
|
|
33475
35186
|
return error;
|
|
33476
35187
|
}
|
|
33477
35188
|
function resolveProviderHostFilePath(providerHome, name) {
|
|
33478
|
-
return
|
|
35189
|
+
return path29.join(providerHome, name);
|
|
33479
35190
|
}
|
|
33480
35191
|
function resolveArchiveRoot(providerHome) {
|
|
33481
|
-
return
|
|
35192
|
+
return path29.join(providerHome, "supervisor-config-archives");
|
|
33482
35193
|
}
|
|
33483
35194
|
function resolveArchiveIndexPath(providerHome) {
|
|
33484
|
-
return
|
|
35195
|
+
return path29.join(resolveArchiveRoot(providerHome), "index.json");
|
|
33485
35196
|
}
|
|
33486
35197
|
function resolveArchivePath(providerHome, archiveId) {
|
|
33487
|
-
return
|
|
35198
|
+
return path29.join(resolveArchiveRoot(providerHome), archiveId);
|
|
33488
35199
|
}
|
|
33489
35200
|
function defaultArchiveLabel(createdAt) {
|
|
33490
35201
|
return `Backup ${createdAt.replace("T", " ").replace(/\.\d{3}Z$/, " UTC")}`;
|
|
33491
35202
|
}
|
|
33492
35203
|
async function readArchiveIndex(providerHome) {
|
|
33493
35204
|
try {
|
|
33494
|
-
const raw = await
|
|
35205
|
+
const raw = await fs26.readFile(resolveArchiveIndexPath(providerHome), "utf8");
|
|
33495
35206
|
const parsed = JSON.parse(raw);
|
|
33496
35207
|
return {
|
|
33497
35208
|
archives: Array.isArray(parsed.archives) ? parsed.archives : []
|
|
@@ -33505,8 +35216,8 @@ async function readArchiveIndex(providerHome) {
|
|
|
33505
35216
|
}
|
|
33506
35217
|
async function writeArchiveIndex(providerHome, index) {
|
|
33507
35218
|
const root = resolveArchiveRoot(providerHome);
|
|
33508
|
-
await
|
|
33509
|
-
await
|
|
35219
|
+
await fs26.mkdir(root, { recursive: true });
|
|
35220
|
+
await fs26.writeFile(
|
|
33510
35221
|
resolveArchiveIndexPath(providerHome),
|
|
33511
35222
|
`${JSON.stringify(index, null, 2)}
|
|
33512
35223
|
`,
|
|
@@ -33577,7 +35288,7 @@ var ProviderHostConfigService = class {
|
|
|
33577
35288
|
const fileName = this.assertHostFile(provider2, name);
|
|
33578
35289
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
33579
35290
|
try {
|
|
33580
|
-
const content = await
|
|
35291
|
+
const content = await fs26.readFile(filePath, "utf8");
|
|
33581
35292
|
return {
|
|
33582
35293
|
name: fileName,
|
|
33583
35294
|
path: filePath,
|
|
@@ -33600,8 +35311,8 @@ var ProviderHostConfigService = class {
|
|
|
33600
35311
|
const providerHome = this.providerHome(provider2);
|
|
33601
35312
|
const fileName = this.assertHostFile(provider2, name);
|
|
33602
35313
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
33603
|
-
await
|
|
33604
|
-
await
|
|
35314
|
+
await fs26.mkdir(path29.dirname(filePath), { recursive: true });
|
|
35315
|
+
await fs26.writeFile(filePath, input.content, "utf8");
|
|
33605
35316
|
return this.readFile(provider2, fileName);
|
|
33606
35317
|
}
|
|
33607
35318
|
async listArchives(provider2) {
|
|
@@ -33627,7 +35338,7 @@ var ProviderHostConfigService = class {
|
|
|
33627
35338
|
}
|
|
33628
35339
|
])
|
|
33629
35340
|
);
|
|
33630
|
-
await
|
|
35341
|
+
await fs26.mkdir(archivePath, { recursive: true });
|
|
33631
35342
|
for (const name of fileNames) {
|
|
33632
35343
|
const hostFile = await this.readFile(provider2, name);
|
|
33633
35344
|
files[name] = {
|
|
@@ -33635,7 +35346,7 @@ var ProviderHostConfigService = class {
|
|
|
33635
35346
|
exists: hostFile.exists
|
|
33636
35347
|
};
|
|
33637
35348
|
if (hostFile.exists) {
|
|
33638
|
-
await
|
|
35349
|
+
await fs26.writeFile(path29.join(archivePath, name), hostFile.content, "utf8");
|
|
33639
35350
|
}
|
|
33640
35351
|
}
|
|
33641
35352
|
const archive = {
|
|
@@ -33671,14 +35382,14 @@ var ProviderHostConfigService = class {
|
|
|
33671
35382
|
const fileNames = this.archiveFileNames(provider2);
|
|
33672
35383
|
const { archive } = await findArchiveOrThrow(providerHome, id);
|
|
33673
35384
|
const archivePath = resolveArchivePath(providerHome, archive.id);
|
|
33674
|
-
await
|
|
35385
|
+
await fs26.mkdir(providerHome, { recursive: true });
|
|
33675
35386
|
for (const name of fileNames) {
|
|
33676
35387
|
const hostPath = resolveProviderHostFilePath(providerHome, name);
|
|
33677
35388
|
if (archive.files[name]?.exists) {
|
|
33678
|
-
const content = await
|
|
33679
|
-
await
|
|
35389
|
+
const content = await fs26.readFile(path29.join(archivePath, name), "utf8");
|
|
35390
|
+
await fs26.writeFile(hostPath, content, "utf8");
|
|
33680
35391
|
} else {
|
|
33681
|
-
await
|
|
35392
|
+
await fs26.rm(hostPath, { force: true });
|
|
33682
35393
|
}
|
|
33683
35394
|
}
|
|
33684
35395
|
await runtime.stop();
|
|
@@ -33691,12 +35402,12 @@ var ProviderHostConfigService = class {
|
|
|
33691
35402
|
};
|
|
33692
35403
|
|
|
33693
35404
|
// src/shell/shell-session-service.ts
|
|
33694
|
-
import
|
|
35405
|
+
import fs28 from "fs/promises";
|
|
33695
35406
|
|
|
33696
35407
|
// src/shell/shell-prompt.ts
|
|
33697
|
-
import
|
|
35408
|
+
import fs27 from "fs/promises";
|
|
33698
35409
|
import os4 from "os";
|
|
33699
|
-
import
|
|
35410
|
+
import path30 from "path";
|
|
33700
35411
|
function basenameFromPath2(filePath) {
|
|
33701
35412
|
if (!filePath) {
|
|
33702
35413
|
return "";
|
|
@@ -33705,7 +35416,7 @@ function basenameFromPath2(filePath) {
|
|
|
33705
35416
|
if (!normalized) {
|
|
33706
35417
|
return "";
|
|
33707
35418
|
}
|
|
33708
|
-
return
|
|
35419
|
+
return path30.basename(normalized) || normalized;
|
|
33709
35420
|
}
|
|
33710
35421
|
function isInteractiveShellCommand(command) {
|
|
33711
35422
|
const normalized = (command ?? "").trim().toLowerCase();
|
|
@@ -33866,11 +35577,11 @@ function buildShellPromptInitScriptContents(command) {
|
|
|
33866
35577
|
async function ensureShellPromptInitScript(command) {
|
|
33867
35578
|
const normalized = command.trim().toLowerCase();
|
|
33868
35579
|
const extension = normalized === "zsh" ? "zsh" : "sh";
|
|
33869
|
-
const filePath =
|
|
35580
|
+
const filePath = path30.join(
|
|
33870
35581
|
os4.tmpdir(),
|
|
33871
35582
|
`remote-codex-shell-prompt.${extension}`
|
|
33872
35583
|
);
|
|
33873
|
-
await
|
|
35584
|
+
await fs27.writeFile(filePath, buildShellPromptInitScriptContents(command), "utf8");
|
|
33874
35585
|
return filePath;
|
|
33875
35586
|
}
|
|
33876
35587
|
async function buildShellPromptInitCommand(command, options = {}) {
|
|
@@ -33886,7 +35597,7 @@ clear
|
|
|
33886
35597
|
// src/shell/shell-session-service.ts
|
|
33887
35598
|
async function pathExists5(filePath) {
|
|
33888
35599
|
try {
|
|
33889
|
-
await
|
|
35600
|
+
await fs28.access(filePath);
|
|
33890
35601
|
return true;
|
|
33891
35602
|
} catch {
|
|
33892
35603
|
return false;
|
|
@@ -34623,19 +36334,19 @@ var BackendPluginHost = class {
|
|
|
34623
36334
|
|
|
34624
36335
|
// src/shell/pty-shell-backend.ts
|
|
34625
36336
|
import { createRequire as createRequire4 } from "module";
|
|
34626
|
-
import
|
|
36337
|
+
import path31 from "path";
|
|
34627
36338
|
|
|
34628
36339
|
// src/shell/default-shell.ts
|
|
34629
|
-
import
|
|
36340
|
+
import fs29 from "fs";
|
|
34630
36341
|
var POSIX_SHELL_CANDIDATES = ["/bin/bash", "/usr/bin/bash", "/bin/sh"];
|
|
34631
36342
|
function resolveDefaultShell(env = process.env, platform = process.platform) {
|
|
34632
36343
|
if (platform === "win32") {
|
|
34633
36344
|
return env.COMSPEC ?? "cmd.exe";
|
|
34634
36345
|
}
|
|
34635
|
-
if (env.SHELL &&
|
|
36346
|
+
if (env.SHELL && fs29.existsSync(env.SHELL)) {
|
|
34636
36347
|
return env.SHELL;
|
|
34637
36348
|
}
|
|
34638
|
-
return POSIX_SHELL_CANDIDATES.find((candidate) =>
|
|
36349
|
+
return POSIX_SHELL_CANDIDATES.find((candidate) => fs29.existsSync(candidate)) ?? "/bin/sh";
|
|
34639
36350
|
}
|
|
34640
36351
|
|
|
34641
36352
|
// src/shell/pty-shell-backend.ts
|
|
@@ -34647,7 +36358,7 @@ function spawnPty(...args) {
|
|
|
34647
36358
|
return nodePty.spawn(...args);
|
|
34648
36359
|
}
|
|
34649
36360
|
function shellArgs(shell) {
|
|
34650
|
-
const shellName =
|
|
36361
|
+
const shellName = path31.basename(shell).toLowerCase();
|
|
34651
36362
|
if (process.platform === "win32") {
|
|
34652
36363
|
return [];
|
|
34653
36364
|
}
|
|
@@ -34669,7 +36380,7 @@ function lastVisibleLine(snapshot) {
|
|
|
34669
36380
|
}
|
|
34670
36381
|
function inferRuntime(session) {
|
|
34671
36382
|
const promptLine = lastVisibleLine(session.scrollback);
|
|
34672
|
-
const shell =
|
|
36383
|
+
const shell = path31.basename(session.shell);
|
|
34673
36384
|
const isCommandRunning = session.exitCode !== null ? false : !/[$#>]\s*$/.test(promptLine.trimEnd());
|
|
34674
36385
|
return {
|
|
34675
36386
|
panePid: session.pty.pid,
|
|
@@ -34815,8 +36526,8 @@ var PtyShellBackend = class {
|
|
|
34815
36526
|
};
|
|
34816
36527
|
|
|
34817
36528
|
// src/shell/tmux-manager.ts
|
|
34818
|
-
import
|
|
34819
|
-
import
|
|
36529
|
+
import fs30 from "fs";
|
|
36530
|
+
import path32 from "path";
|
|
34820
36531
|
import { spawn as spawnChild } from "child_process";
|
|
34821
36532
|
async function defaultExecCommand(command, args) {
|
|
34822
36533
|
return await new Promise((resolve, reject) => {
|
|
@@ -34843,17 +36554,17 @@ async function defaultExecCommand(command, args) {
|
|
|
34843
36554
|
});
|
|
34844
36555
|
}
|
|
34845
36556
|
function resolveExecutablePath(command) {
|
|
34846
|
-
if (command.includes(
|
|
36557
|
+
if (command.includes(path32.sep)) {
|
|
34847
36558
|
return command;
|
|
34848
36559
|
}
|
|
34849
36560
|
const searchPath = process.env.PATH ?? "";
|
|
34850
|
-
for (const entry of searchPath.split(
|
|
36561
|
+
for (const entry of searchPath.split(path32.delimiter)) {
|
|
34851
36562
|
const trimmed = entry.trim();
|
|
34852
36563
|
if (!trimmed) {
|
|
34853
36564
|
continue;
|
|
34854
36565
|
}
|
|
34855
|
-
const candidate =
|
|
34856
|
-
if (
|
|
36566
|
+
const candidate = path32.join(trimmed, command);
|
|
36567
|
+
if (fs30.existsSync(candidate)) {
|
|
34857
36568
|
return candidate;
|
|
34858
36569
|
}
|
|
34859
36570
|
}
|
|
@@ -35331,13 +37042,17 @@ var UnsupportedShellBackend = class {
|
|
|
35331
37042
|
async listSessionNames() {
|
|
35332
37043
|
return [];
|
|
35333
37044
|
}
|
|
35334
|
-
async hasSession(
|
|
37045
|
+
async hasSession(sessionId) {
|
|
37046
|
+
void sessionId;
|
|
35335
37047
|
return false;
|
|
35336
37048
|
}
|
|
35337
|
-
async createSession(
|
|
37049
|
+
async createSession(input) {
|
|
37050
|
+
void input;
|
|
35338
37051
|
this.throwUnavailable();
|
|
35339
37052
|
}
|
|
35340
|
-
async attach(
|
|
37053
|
+
async attach(sessionId, options) {
|
|
37054
|
+
void sessionId;
|
|
37055
|
+
void options;
|
|
35341
37056
|
return this.throwUnavailable();
|
|
35342
37057
|
}
|
|
35343
37058
|
async sendInput() {
|
|
@@ -36090,16 +37805,16 @@ var HttpError = class extends Error {
|
|
|
36090
37805
|
};
|
|
36091
37806
|
function findRepoRoot(start = process.cwd()) {
|
|
36092
37807
|
if (process.env.REMOTE_CODEX_REPO_ROOT) {
|
|
36093
|
-
return
|
|
37808
|
+
return path33.resolve(process.env.REMOTE_CODEX_REPO_ROOT);
|
|
36094
37809
|
}
|
|
36095
|
-
let current =
|
|
36096
|
-
while (current !==
|
|
36097
|
-
if (
|
|
37810
|
+
let current = path33.resolve(start);
|
|
37811
|
+
while (current !== path33.dirname(current)) {
|
|
37812
|
+
if (fs31.existsSync(path33.join(current, "pnpm-workspace.yaml")) && fs31.existsSync(path33.join(current, "scripts", "service-restart.mjs"))) {
|
|
36098
37813
|
return current;
|
|
36099
37814
|
}
|
|
36100
|
-
current =
|
|
37815
|
+
current = path33.dirname(current);
|
|
36101
37816
|
}
|
|
36102
|
-
return
|
|
37817
|
+
return path33.resolve(process.cwd());
|
|
36103
37818
|
}
|
|
36104
37819
|
function createServiceLifecycle() {
|
|
36105
37820
|
return {
|
|
@@ -36111,12 +37826,12 @@ function createServiceLifecycle() {
|
|
|
36111
37826
|
});
|
|
36112
37827
|
}
|
|
36113
37828
|
const repoRoot = findRepoRoot();
|
|
36114
|
-
const restartScript =
|
|
37829
|
+
const restartScript = path33.join(
|
|
36115
37830
|
repoRoot,
|
|
36116
37831
|
"scripts",
|
|
36117
37832
|
"service-restart.mjs"
|
|
36118
37833
|
);
|
|
36119
|
-
if (!
|
|
37834
|
+
if (!fs31.existsSync(restartScript) || !fs31.existsSync(path33.join(repoRoot, "pnpm-workspace.yaml"))) {
|
|
36120
37835
|
throw new HttpError(503, {
|
|
36121
37836
|
code: "service_unavailable",
|
|
36122
37837
|
message: "Build and restart requires a Remote Codex source checkout. Set REMOTE_CODEX_REPO_ROOT to the checkout path, or update the npm package with npm install -g remote-codex@latest."
|
|
@@ -36599,9 +38314,9 @@ function relayResponseHeaders(headers) {
|
|
|
36599
38314
|
}
|
|
36600
38315
|
|
|
36601
38316
|
// src/platform/lifecycle-control.ts
|
|
36602
|
-
import
|
|
38317
|
+
import fs32 from "fs/promises";
|
|
36603
38318
|
import net2 from "net";
|
|
36604
|
-
import
|
|
38319
|
+
import path34 from "path";
|
|
36605
38320
|
var LifecycleControlServer = class {
|
|
36606
38321
|
constructor(options) {
|
|
36607
38322
|
this.options = options;
|
|
@@ -36613,12 +38328,12 @@ var LifecycleControlServer = class {
|
|
|
36613
38328
|
return;
|
|
36614
38329
|
}
|
|
36615
38330
|
if (process.platform !== "win32") {
|
|
36616
|
-
await
|
|
38331
|
+
await fs32.unlink(this.options.endpoint).catch((error) => {
|
|
36617
38332
|
if (error.code !== "ENOENT") {
|
|
36618
38333
|
throw error;
|
|
36619
38334
|
}
|
|
36620
38335
|
});
|
|
36621
|
-
await
|
|
38336
|
+
await fs32.mkdir(path34.dirname(this.options.endpoint), { recursive: true });
|
|
36622
38337
|
}
|
|
36623
38338
|
const server = net2.createServer((socket) => {
|
|
36624
38339
|
socket.setEncoding("utf8");
|
|
@@ -36655,7 +38370,7 @@ var LifecycleControlServer = class {
|
|
|
36655
38370
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
36656
38371
|
}
|
|
36657
38372
|
if (process.platform !== "win32") {
|
|
36658
|
-
await
|
|
38373
|
+
await fs32.unlink(this.options.endpoint).catch(() => void 0);
|
|
36659
38374
|
}
|
|
36660
38375
|
}
|
|
36661
38376
|
async handleRequest(line, socket) {
|
|
@@ -36698,7 +38413,7 @@ var LifecycleControlServer = class {
|
|
|
36698
38413
|
};
|
|
36699
38414
|
|
|
36700
38415
|
// src/index.ts
|
|
36701
|
-
if (
|
|
38416
|
+
if (fs33.existsSync(".env")) {
|
|
36702
38417
|
process.loadEnvFile?.(".env");
|
|
36703
38418
|
}
|
|
36704
38419
|
var app = buildApp();
|