agentbox-sdk 0.1.302 → 0.1.304
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Sandbox-CByFJI8X.d.ts → Sandbox-K6VNqKeo.d.ts} +1 -1
- package/dist/agents/index.d.ts +5 -5
- package/dist/agents/index.js +2 -2
- package/dist/{chunk-NWAXQ7UD.js → chunk-EK6GC6YA.js} +3 -1
- package/dist/{chunk-DDGBIACF.js → chunk-QNNAAVJV.js} +9 -8
- package/dist/{chunk-YAMFJAU6.js → chunk-T7C77LCW.js} +440 -277
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/sandboxes/index.d.ts +4 -4
- package/dist/sandboxes/index.js +1 -1
- package/dist/{types-Dj_HCr6Q.d.ts → types-Ozo1rHKs.d.ts} +2 -11
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createNormalizedEvent,
|
|
3
3
|
normalizeRawAgentEvent,
|
|
4
4
|
toAISDKStream
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EK6GC6YA.js";
|
|
6
6
|
import {
|
|
7
7
|
AgentBoxError,
|
|
8
8
|
AsyncQueue,
|
|
@@ -998,6 +998,28 @@ var HostSetupTarget = class {
|
|
|
998
998
|
}
|
|
999
999
|
);
|
|
1000
1000
|
}
|
|
1001
|
+
async probe(command, extraEnv) {
|
|
1002
|
+
return time(
|
|
1003
|
+
debugRuntime,
|
|
1004
|
+
`host probe ${shortLabel(command)}`,
|
|
1005
|
+
async () => {
|
|
1006
|
+
const handle = spawnCommand({
|
|
1007
|
+
command: process.env.SHELL || "sh",
|
|
1008
|
+
args: ["-c", command],
|
|
1009
|
+
cwd: this.cwd,
|
|
1010
|
+
env: {
|
|
1011
|
+
...process.env,
|
|
1012
|
+
...this.baseEnv,
|
|
1013
|
+
...this.env,
|
|
1014
|
+
...extraEnv ?? {}
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
const exitCode = await handle.wait();
|
|
1018
|
+
return exitCode === 0;
|
|
1019
|
+
},
|
|
1020
|
+
(ok) => ({ ok })
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1001
1023
|
async cleanup() {
|
|
1002
1024
|
await rm(this.layout.rootDir, { recursive: true, force: true });
|
|
1003
1025
|
}
|
|
@@ -1052,13 +1074,34 @@ var SandboxSetupTarget = class {
|
|
|
1052
1074
|
}
|
|
1053
1075
|
});
|
|
1054
1076
|
if (result && result.exitCode !== 0) {
|
|
1077
|
+
const output = result.combinedOutput?.trim();
|
|
1078
|
+
const detail = output ? `
|
|
1079
|
+
${output}` : "";
|
|
1055
1080
|
throw new Error(
|
|
1056
|
-
`Sandbox setup command failed (${result.exitCode}): ${command}`
|
|
1081
|
+
`Sandbox setup command failed (${result.exitCode}): ${command}${detail}`
|
|
1057
1082
|
);
|
|
1058
1083
|
}
|
|
1059
1084
|
}
|
|
1060
1085
|
);
|
|
1061
1086
|
}
|
|
1087
|
+
async probe(command, extraEnv) {
|
|
1088
|
+
return time(
|
|
1089
|
+
debugRuntime,
|
|
1090
|
+
`sandbox probe ${shortLabel(command)}`,
|
|
1091
|
+
async () => {
|
|
1092
|
+
const result = await this.options.sandbox?.run(command, {
|
|
1093
|
+
cwd: this.options.cwd,
|
|
1094
|
+
env: {
|
|
1095
|
+
...this.options.env ?? {},
|
|
1096
|
+
...this.env,
|
|
1097
|
+
...extraEnv ?? {}
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
return Boolean(result && result.exitCode === 0);
|
|
1101
|
+
},
|
|
1102
|
+
(ok) => ({ ok })
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1062
1105
|
async cleanup() {
|
|
1063
1106
|
}
|
|
1064
1107
|
};
|
|
@@ -1092,6 +1135,7 @@ import path5 from "path";
|
|
|
1092
1135
|
var MANIFEST_FILENAME = "setup-manifest.json";
|
|
1093
1136
|
var TARGET_MANIFEST_FILENAME = "setup-target.json";
|
|
1094
1137
|
var INSTALL_SCRIPT_FILENAME = "install.sh";
|
|
1138
|
+
var SETUP_ID_FILENAME = "setup.id";
|
|
1095
1139
|
var MANIFEST_VERSION = 1;
|
|
1096
1140
|
function hashArtifact(artifact) {
|
|
1097
1141
|
const hasher = createHash("sha256");
|
|
@@ -1110,6 +1154,74 @@ function computeTargetArtifacts(artifacts) {
|
|
|
1110
1154
|
}
|
|
1111
1155
|
return result;
|
|
1112
1156
|
}
|
|
1157
|
+
function computeSetupId(parts) {
|
|
1158
|
+
const hasher = createHash("sha256");
|
|
1159
|
+
hasher.update(`v${MANIFEST_VERSION}
|
|
1160
|
+
`);
|
|
1161
|
+
for (const a of [...parts.artifacts ?? []].sort(
|
|
1162
|
+
(x, y) => x.path.localeCompare(y.path)
|
|
1163
|
+
)) {
|
|
1164
|
+
hasher.update(`a:${a.path}:${hashArtifact(a)}
|
|
1165
|
+
`);
|
|
1166
|
+
}
|
|
1167
|
+
for (const cmd of parts.installCommands ?? []) {
|
|
1168
|
+
hasher.update(`c:${hashCommand(cmd)}
|
|
1169
|
+
`);
|
|
1170
|
+
}
|
|
1171
|
+
if (parts.daemon) {
|
|
1172
|
+
hasher.update(
|
|
1173
|
+
`d:${parts.daemon.port}:${parts.daemon.healthPath}:${parts.daemon.expectedVersionMatch ?? ""}
|
|
1174
|
+
`
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
for (const extra of parts.extras ?? []) {
|
|
1178
|
+
hasher.update(`x:${extra}
|
|
1179
|
+
`);
|
|
1180
|
+
}
|
|
1181
|
+
return hasher.digest("hex");
|
|
1182
|
+
}
|
|
1183
|
+
async function preflightSetup(target, setupId, daemon) {
|
|
1184
|
+
return time(
|
|
1185
|
+
debugSetup,
|
|
1186
|
+
`preflightSetup ${target.provider}`,
|
|
1187
|
+
async () => {
|
|
1188
|
+
const setupIdFile = path5.posix.join(
|
|
1189
|
+
target.layout.rootDir,
|
|
1190
|
+
SETUP_ID_FILENAME
|
|
1191
|
+
);
|
|
1192
|
+
const checks = [
|
|
1193
|
+
`[ -f ${shellQuote(setupIdFile)} ]`,
|
|
1194
|
+
`[ "$(cat ${shellQuote(setupIdFile)} 2>/dev/null)" = ${shellQuote(setupId)} ]`
|
|
1195
|
+
];
|
|
1196
|
+
if (daemon) {
|
|
1197
|
+
const url = `http://127.0.0.1:${daemon.port}${daemon.healthPath}`;
|
|
1198
|
+
if (daemon.expectedVersionMatch) {
|
|
1199
|
+
checks.push(
|
|
1200
|
+
`curl -fsS --max-time 2 ${shellQuote(url)} 2>/dev/null | grep -q ${shellQuote(daemon.expectedVersionMatch)}`
|
|
1201
|
+
);
|
|
1202
|
+
} else {
|
|
1203
|
+
checks.push(
|
|
1204
|
+
`curl -fsS --max-time 2 ${shellQuote(url)} >/dev/null 2>&1`
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return target.probe(checks.join(" && "));
|
|
1209
|
+
},
|
|
1210
|
+
(ok) => ({ ok })
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
async function markSetupComplete(target, setupId) {
|
|
1214
|
+
await time(
|
|
1215
|
+
debugSetup,
|
|
1216
|
+
`markSetupComplete ${target.provider}`,
|
|
1217
|
+
() => target.runCommand(
|
|
1218
|
+
[
|
|
1219
|
+
`mkdir -p ${shellQuote(target.layout.rootDir)}`,
|
|
1220
|
+
`printf '%s' ${shellQuote(setupId)} > ${shellQuote(path5.posix.join(target.layout.rootDir, SETUP_ID_FILENAME))}`
|
|
1221
|
+
].join(" && ")
|
|
1222
|
+
)
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1113
1225
|
function buildInstallScript(rootDir, installCommandsByKey) {
|
|
1114
1226
|
const commandsB64 = Buffer.from(
|
|
1115
1227
|
JSON.stringify(installCommandsByKey),
|
|
@@ -1935,6 +2047,11 @@ var ClaudeCodeAgentAdapter = class {
|
|
|
1935
2047
|
/**
|
|
1936
2048
|
* Sandbox-side preparation. Uploads `.claude/` artifacts and ensures
|
|
1937
2049
|
* the daemon is running. `execute()` then dials the daemon directly.
|
|
2050
|
+
*
|
|
2051
|
+
* Warm-path short-circuit: a single no-upload `preflightSetup` probes
|
|
2052
|
+
* the `setup.id` marker + daemon `/__version` on loopback. If both
|
|
2053
|
+
* match what we'd produce, we skip the artifact upload AND the daemon
|
|
2054
|
+
* boot entirely.
|
|
1938
2055
|
*/
|
|
1939
2056
|
async setup(request) {
|
|
1940
2057
|
await time(debugClaude, "claude-code setup()", async () => {
|
|
@@ -1968,6 +2085,20 @@ var ClaudeCodeAgentAdapter = class {
|
|
|
1968
2085
|
{ path: settingsPath, content: JSON.stringify(hookSettings, null, 2) },
|
|
1969
2086
|
{ path: mcpConfigPath, content: mcpConfigJson }
|
|
1970
2087
|
];
|
|
2088
|
+
const daemonInfo = {
|
|
2089
|
+
port: DAEMON_PORT,
|
|
2090
|
+
healthPath: "/__version",
|
|
2091
|
+
expectedVersionMatch: DAEMON_PROTOCOL_VERSION
|
|
2092
|
+
};
|
|
2093
|
+
const setupId = computeSetupId({
|
|
2094
|
+
artifacts,
|
|
2095
|
+
installCommands,
|
|
2096
|
+
daemon: daemonInfo
|
|
2097
|
+
});
|
|
2098
|
+
if (await preflightSetup(target, setupId, daemonInfo)) {
|
|
2099
|
+
debugClaude("claude-code setup() preflight hit \u2014 skipping");
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
1971
2102
|
const env = { ...options.env ?? {}, ...target.env };
|
|
1972
2103
|
await Promise.all([
|
|
1973
2104
|
time(
|
|
@@ -1977,6 +2108,7 @@ var ClaudeCodeAgentAdapter = class {
|
|
|
1977
2108
|
),
|
|
1978
2109
|
ensureClaudeCodeDaemon(options, env)
|
|
1979
2110
|
]);
|
|
2111
|
+
await markSetupComplete(target, setupId);
|
|
1980
2112
|
});
|
|
1981
2113
|
}
|
|
1982
2114
|
async execute(request, sink) {
|
|
@@ -2372,6 +2504,31 @@ async function* streamSse(url, init) {
|
|
|
2372
2504
|
})();
|
|
2373
2505
|
yield* queue;
|
|
2374
2506
|
}
|
|
2507
|
+
async function* streamSseResilient(url, init) {
|
|
2508
|
+
let lastEventId;
|
|
2509
|
+
let attempt = 0;
|
|
2510
|
+
const signal = init?.signal;
|
|
2511
|
+
while (true) {
|
|
2512
|
+
try {
|
|
2513
|
+
const headers = {
|
|
2514
|
+
...init?.headers ?? {},
|
|
2515
|
+
...lastEventId ? { "Last-Event-ID": lastEventId } : {}
|
|
2516
|
+
};
|
|
2517
|
+
for await (const ev of streamSse(url, { ...init, headers })) {
|
|
2518
|
+
attempt = 0;
|
|
2519
|
+
if (ev.id) lastEventId = ev.id;
|
|
2520
|
+
yield ev;
|
|
2521
|
+
}
|
|
2522
|
+
return;
|
|
2523
|
+
} catch (err) {
|
|
2524
|
+
if (signal?.aborted) throw err;
|
|
2525
|
+
const delay = Math.min(500 * Math.pow(2, attempt), 5e3);
|
|
2526
|
+
attempt++;
|
|
2527
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2528
|
+
if (signal?.aborted) throw err;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2375
2532
|
async function connectJsonRpcWebSocket(url, options) {
|
|
2376
2533
|
const notifications = new AsyncQueue();
|
|
2377
2534
|
const socket = new WebSocket(url, { headers: options?.headers });
|
|
@@ -2819,7 +2976,12 @@ async function ensureCodexLoginViaConfig(request, target) {
|
|
|
2819
2976
|
[
|
|
2820
2977
|
'if [ -z "${OPENAI_API_KEY:-}" ]; then exit 0; fi',
|
|
2821
2978
|
'mkdir -p "${CODEX_HOME:-$HOME/.codex}"',
|
|
2822
|
-
|
|
2979
|
+
// Merge stderr into stdout so providers that don't surface stderr
|
|
2980
|
+
// (e.g. Daytona's `executeCommand`, which collapses to a single
|
|
2981
|
+
// `result` field) still propagate the underlying error message
|
|
2982
|
+
// back to the caller — otherwise a failed login leaves us with a
|
|
2983
|
+
// bare "exit 1" and no diagnostic.
|
|
2984
|
+
"printenv OPENAI_API_KEY | env -u XDG_CONFIG_HOME codex login --with-api-key 2>&1"
|
|
2823
2985
|
].join("; "),
|
|
2824
2986
|
Object.keys(extraEnv).length > 0 ? extraEnv : void 0
|
|
2825
2987
|
);
|
|
@@ -2925,12 +3087,26 @@ async function setupCodex(request) {
|
|
|
2925
3087
|
...sharedTarget.env,
|
|
2926
3088
|
...options.provider?.env ?? {}
|
|
2927
3089
|
});
|
|
3090
|
+
const { artifacts: serverArtifacts } = buildArtifactsFor(sharedTarget);
|
|
3091
|
+
const { artifacts: skillArtifacts2, installCommands: installCommands2 } = await prepareSkillArtifacts(provider, options.skills, target2.layout);
|
|
3092
|
+
const daemonInfo = {
|
|
3093
|
+
port: REMOTE_CODEX_APP_SERVER_PORT,
|
|
3094
|
+
healthPath: "/readyz"
|
|
3095
|
+
};
|
|
3096
|
+
const setupId2 = computeSetupId({
|
|
3097
|
+
artifacts: [...serverArtifacts, ...skillArtifacts2],
|
|
3098
|
+
installCommands: installCommands2,
|
|
3099
|
+
daemon: daemonInfo
|
|
3100
|
+
});
|
|
3101
|
+
if (await preflightSetup(sharedTarget, setupId2, daemonInfo)) {
|
|
3102
|
+
debugCodex("codex remote setup() preflight hit \u2014 skipping");
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
2928
3105
|
await time(
|
|
2929
3106
|
debugCodex,
|
|
2930
3107
|
"ensureCodexLogin",
|
|
2931
3108
|
() => ensureCodexLoginViaConfig(request, sharedTarget)
|
|
2932
3109
|
);
|
|
2933
|
-
const { artifacts: serverArtifacts } = buildArtifactsFor(sharedTarget);
|
|
2934
3110
|
await applyDifferentialSetup(sharedTarget, serverArtifacts, []);
|
|
2935
3111
|
const binary = options.provider?.binary ?? "codex";
|
|
2936
3112
|
const pidFilePath = path9.posix.join(
|
|
@@ -2978,28 +3154,34 @@ async function setupCodex(request) {
|
|
|
2978
3154
|
);
|
|
2979
3155
|
}
|
|
2980
3156
|
try {
|
|
2981
|
-
const { artifacts: skillArtifacts2, installCommands: installCommands2 } = await prepareSkillArtifacts(provider, options.skills, target2.layout);
|
|
2982
3157
|
await applyDifferentialSetup(target2, skillArtifacts2, installCommands2);
|
|
2983
3158
|
} catch (error) {
|
|
2984
3159
|
await target2.cleanup().catch(() => void 0);
|
|
2985
3160
|
throw error;
|
|
2986
3161
|
}
|
|
3162
|
+
await markSetupComplete(sharedTarget, setupId2);
|
|
2987
3163
|
return;
|
|
2988
3164
|
}
|
|
2989
3165
|
const target = await createSetupTarget(provider, "shared-setup", options);
|
|
3166
|
+
const { artifacts: skillArtifacts, installCommands } = await prepareSkillArtifacts(provider, options.skills, target.layout);
|
|
3167
|
+
const { artifacts: configArtifacts } = buildArtifactsFor(target);
|
|
3168
|
+
const allArtifacts = [...skillArtifacts, ...configArtifacts];
|
|
3169
|
+
const setupId = computeSetupId({
|
|
3170
|
+
artifacts: allArtifacts,
|
|
3171
|
+
installCommands
|
|
3172
|
+
});
|
|
3173
|
+
if (await preflightSetup(target, setupId)) {
|
|
3174
|
+
debugCodex("codex local setup() preflight hit \u2014 skipping");
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
2990
3177
|
try {
|
|
2991
3178
|
await ensureCodexLoginViaConfig(request, target);
|
|
2992
3179
|
} catch (error) {
|
|
2993
3180
|
await target.cleanup().catch(() => void 0);
|
|
2994
3181
|
throw error;
|
|
2995
3182
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
await applyDifferentialSetup(
|
|
2999
|
-
target,
|
|
3000
|
-
[...skillArtifacts, ...configArtifacts],
|
|
3001
|
-
installCommands
|
|
3002
|
-
);
|
|
3183
|
+
await applyDifferentialSetup(target, allArtifacts, installCommands);
|
|
3184
|
+
await markSetupComplete(target, setupId);
|
|
3003
3185
|
}
|
|
3004
3186
|
async function createRuntime(request, inputParts) {
|
|
3005
3187
|
const options = request.options;
|
|
@@ -3383,7 +3565,7 @@ var CodexAgentAdapter = class {
|
|
|
3383
3565
|
Date.now() - executeStartedAt,
|
|
3384
3566
|
text?.length ?? 0
|
|
3385
3567
|
);
|
|
3386
|
-
sink.complete({
|
|
3568
|
+
sink.complete({ costData: extractCodexCostData(rawPayloads) });
|
|
3387
3569
|
}
|
|
3388
3570
|
}
|
|
3389
3571
|
} finally {
|
|
@@ -3481,73 +3663,6 @@ function toRawEvent3(runId, payload, type) {
|
|
|
3481
3663
|
payload
|
|
3482
3664
|
};
|
|
3483
3665
|
}
|
|
3484
|
-
function extractText(value) {
|
|
3485
|
-
if (!value) {
|
|
3486
|
-
return "";
|
|
3487
|
-
}
|
|
3488
|
-
if (typeof value === "string") {
|
|
3489
|
-
return value;
|
|
3490
|
-
}
|
|
3491
|
-
if (Array.isArray(value)) {
|
|
3492
|
-
return value.map(extractText).filter(Boolean).join("");
|
|
3493
|
-
}
|
|
3494
|
-
if (typeof value === "object") {
|
|
3495
|
-
const record = value;
|
|
3496
|
-
if (record.type === "text" && typeof record.text === "string") {
|
|
3497
|
-
return record.text;
|
|
3498
|
-
}
|
|
3499
|
-
if (record.type === "reasoning") {
|
|
3500
|
-
return "";
|
|
3501
|
-
}
|
|
3502
|
-
if (record.message) {
|
|
3503
|
-
return extractText(record.message);
|
|
3504
|
-
}
|
|
3505
|
-
if (record.content) {
|
|
3506
|
-
return extractText(record.content);
|
|
3507
|
-
}
|
|
3508
|
-
if (record.parts) {
|
|
3509
|
-
return extractText(record.parts);
|
|
3510
|
-
}
|
|
3511
|
-
if (record.text) {
|
|
3512
|
-
return extractText(record.text);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
return "";
|
|
3516
|
-
}
|
|
3517
|
-
function extractAssistantMessageId(response) {
|
|
3518
|
-
if (!response || typeof response !== "object") {
|
|
3519
|
-
return void 0;
|
|
3520
|
-
}
|
|
3521
|
-
const record = response;
|
|
3522
|
-
const info = record.info && typeof record.info === "object" ? record.info : record.message && typeof record.message === "object" ? record.message : void 0;
|
|
3523
|
-
const id = info?.id ?? record.id;
|
|
3524
|
-
return typeof id === "string" ? id : void 0;
|
|
3525
|
-
}
|
|
3526
|
-
function extractReasoning(value) {
|
|
3527
|
-
if (!value) {
|
|
3528
|
-
return "";
|
|
3529
|
-
}
|
|
3530
|
-
if (Array.isArray(value)) {
|
|
3531
|
-
return value.map(extractReasoning).filter(Boolean).join("");
|
|
3532
|
-
}
|
|
3533
|
-
if (typeof value === "object") {
|
|
3534
|
-
const record = value;
|
|
3535
|
-
if (record.type === "reasoning") {
|
|
3536
|
-
if (typeof record.text === "string") {
|
|
3537
|
-
return record.text;
|
|
3538
|
-
}
|
|
3539
|
-
if (typeof record.reasoning === "string") {
|
|
3540
|
-
return record.reasoning;
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
return [
|
|
3544
|
-
extractReasoning(record.message),
|
|
3545
|
-
extractReasoning(record.content),
|
|
3546
|
-
extractReasoning(record.parts)
|
|
3547
|
-
].filter(Boolean).join("");
|
|
3548
|
-
}
|
|
3549
|
-
return "";
|
|
3550
|
-
}
|
|
3551
3666
|
function toOpenCodeModel(model) {
|
|
3552
3667
|
if (!model) {
|
|
3553
3668
|
return void 0;
|
|
@@ -3609,12 +3724,19 @@ var OPEN_CODE_REASONING_LEVELS = ["low", "medium", "high", "xhigh"];
|
|
|
3609
3724
|
function openCodeAgentSlug(reasoning) {
|
|
3610
3725
|
return reasoning ? `agentbox-${reasoning}` : "agentbox";
|
|
3611
3726
|
}
|
|
3612
|
-
function buildOpenCodeConfig(options,
|
|
3727
|
+
function buildOpenCodeConfig(options, interactiveApproval) {
|
|
3613
3728
|
const mcpConfig = buildOpenCodeMcpConfig(options.mcps);
|
|
3614
3729
|
const commandsConfig = buildOpenCodeCommandsConfig(options.commands);
|
|
3615
3730
|
const baseAgent = {
|
|
3616
3731
|
mode: "primary",
|
|
3617
|
-
prompt
|
|
3732
|
+
// Suppress opencode's default provider system prompt (e.g.
|
|
3733
|
+
// PROMPT_ANTHROPIC's "You are OpenCode...") so request.run.systemPrompt
|
|
3734
|
+
// is the dominant identity statement the model sees. opencode's
|
|
3735
|
+
// session/llm.ts uses agent.prompt instead of SystemPrompt.provider(model)
|
|
3736
|
+
// when this field is truthy — codex already takes that branch via a
|
|
3737
|
+
// separate options.instructions channel, so this brings anthropic et al.
|
|
3738
|
+
// in line. Constant value, so setupId stays stable.
|
|
3739
|
+
prompt: "You are an AI coding assistant. Follow the user's instructions.",
|
|
3618
3740
|
permission: buildOpenCodePermissionConfig(interactiveApproval),
|
|
3619
3741
|
tools: {
|
|
3620
3742
|
write: true,
|
|
@@ -3651,19 +3773,6 @@ async function ensureSandboxOpenCodeServer(request) {
|
|
|
3651
3773
|
const sandbox = request.options.sandbox;
|
|
3652
3774
|
const options = request.options;
|
|
3653
3775
|
const port = SANDBOX_OPENCODE_PORT;
|
|
3654
|
-
const healthCheck = await time(
|
|
3655
|
-
debugOpencode,
|
|
3656
|
-
"health probe (warm path)",
|
|
3657
|
-
() => sandbox.run(
|
|
3658
|
-
`curl -fsS http://127.0.0.1:${port}/global/health >/dev/null 2>&1`,
|
|
3659
|
-
{ cwd: options.cwd, timeoutMs: 5e3 }
|
|
3660
|
-
)
|
|
3661
|
-
);
|
|
3662
|
-
if (healthCheck.exitCode === 0) {
|
|
3663
|
-
debugOpencode("opencode server already running \u2014 reusing");
|
|
3664
|
-
return;
|
|
3665
|
-
}
|
|
3666
|
-
debugOpencode("opencode server not running \u2014 cold-spawning");
|
|
3667
3776
|
const plugins = assertHooksSupported(request.provider, options);
|
|
3668
3777
|
assertCommandsSupported(request.provider, options.commands);
|
|
3669
3778
|
const interactiveApproval = isInteractiveApproval(options);
|
|
@@ -3684,26 +3793,32 @@ async function ensureSandboxOpenCodeServer(request) {
|
|
|
3684
3793
|
const configPath = path10.join(target.layout.opencodeDir, "agentbox.json");
|
|
3685
3794
|
const openCodeConfig = buildOpenCodeConfig(
|
|
3686
3795
|
options,
|
|
3687
|
-
request.config.systemPrompt ?? "",
|
|
3688
3796
|
interactiveApproval
|
|
3689
3797
|
);
|
|
3798
|
+
const allArtifacts = [
|
|
3799
|
+
...skillArtifacts,
|
|
3800
|
+
...pluginArtifacts,
|
|
3801
|
+
{
|
|
3802
|
+
path: configPath,
|
|
3803
|
+
content: JSON.stringify(openCodeConfig, null, 2)
|
|
3804
|
+
}
|
|
3805
|
+
];
|
|
3806
|
+
const daemonInfo = { port, healthPath: "/global/health" };
|
|
3807
|
+
const setupId = computeSetupId({
|
|
3808
|
+
artifacts: allArtifacts,
|
|
3809
|
+
installCommands,
|
|
3810
|
+
daemon: daemonInfo
|
|
3811
|
+
});
|
|
3812
|
+
if (await preflightSetup(target, setupId, daemonInfo)) {
|
|
3813
|
+
debugOpencode("opencode setup() preflight hit \u2014 skipping");
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3690
3816
|
const commonEnv = {
|
|
3691
3817
|
OPENCODE_CONFIG: configPath,
|
|
3692
3818
|
OPENCODE_CONFIG_DIR: target.layout.opencodeDir,
|
|
3693
3819
|
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true"
|
|
3694
3820
|
};
|
|
3695
|
-
await applyDifferentialSetup(
|
|
3696
|
-
target,
|
|
3697
|
-
[
|
|
3698
|
-
...skillArtifacts,
|
|
3699
|
-
...pluginArtifacts,
|
|
3700
|
-
{
|
|
3701
|
-
path: configPath,
|
|
3702
|
-
content: JSON.stringify(openCodeConfig, null, 2)
|
|
3703
|
-
}
|
|
3704
|
-
],
|
|
3705
|
-
installCommands
|
|
3706
|
-
);
|
|
3821
|
+
await applyDifferentialSetup(target, allArtifacts, installCommands);
|
|
3707
3822
|
const binary = options.provider?.binary ?? "opencode";
|
|
3708
3823
|
const pidFilePath = path10.posix.join(
|
|
3709
3824
|
target.layout.rootDir,
|
|
@@ -3766,6 +3881,7 @@ async function ensureSandboxOpenCodeServer(request) {
|
|
|
3766
3881
|
`OpenCode server did not become ready within ${SANDBOX_OPENCODE_READY_TIMEOUT_MS}ms.`
|
|
3767
3882
|
);
|
|
3768
3883
|
});
|
|
3884
|
+
await markSetupComplete(target, setupId);
|
|
3769
3885
|
});
|
|
3770
3886
|
}
|
|
3771
3887
|
async function ensureLocalOpenCodeServer(request) {
|
|
@@ -3798,28 +3914,28 @@ async function ensureLocalOpenCodeServer(request) {
|
|
|
3798
3914
|
target.layout.opencodeDir
|
|
3799
3915
|
);
|
|
3800
3916
|
const configPath = path10.join(target.layout.opencodeDir, "agentbox.json");
|
|
3801
|
-
const openCodeConfig = buildOpenCodeConfig(
|
|
3802
|
-
options,
|
|
3803
|
-
request.config.systemPrompt ?? "",
|
|
3804
|
-
interactiveApproval
|
|
3805
|
-
);
|
|
3917
|
+
const openCodeConfig = buildOpenCodeConfig(options, interactiveApproval);
|
|
3806
3918
|
const commonEnv = {
|
|
3807
3919
|
OPENCODE_CONFIG: configPath,
|
|
3808
3920
|
OPENCODE_CONFIG_DIR: target.layout.opencodeDir,
|
|
3809
3921
|
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true"
|
|
3810
3922
|
};
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3923
|
+
const allArtifacts = [
|
|
3924
|
+
...skillArtifacts,
|
|
3925
|
+
...pluginArtifacts,
|
|
3926
|
+
{
|
|
3927
|
+
path: configPath,
|
|
3928
|
+
content: JSON.stringify(openCodeConfig, null, 2)
|
|
3929
|
+
}
|
|
3930
|
+
];
|
|
3931
|
+
const setupId = computeSetupId({
|
|
3932
|
+
artifacts: allArtifacts,
|
|
3821
3933
|
installCommands
|
|
3822
|
-
);
|
|
3934
|
+
});
|
|
3935
|
+
const preflightHit = await preflightSetup(target, setupId);
|
|
3936
|
+
if (!preflightHit) {
|
|
3937
|
+
await applyDifferentialSetup(target, allArtifacts, installCommands);
|
|
3938
|
+
}
|
|
3823
3939
|
spawnCommand({
|
|
3824
3940
|
command: options.provider?.binary ?? "opencode",
|
|
3825
3941
|
args: [
|
|
@@ -3841,6 +3957,7 @@ async function ensureLocalOpenCodeServer(request) {
|
|
|
3841
3957
|
`http://127.0.0.1:${LOCAL_OPENCODE_PORT}/global/health`,
|
|
3842
3958
|
{ timeoutMs: LOCAL_OPENCODE_READY_TIMEOUT_MS }
|
|
3843
3959
|
);
|
|
3960
|
+
await markSetupComplete(target, setupId);
|
|
3844
3961
|
}
|
|
3845
3962
|
async function setupOpenCode(request) {
|
|
3846
3963
|
if (request.options.sandbox) {
|
|
@@ -3878,25 +3995,14 @@ var OpenCodeAgentAdapter = class {
|
|
|
3878
3995
|
"validateProviderUserInput",
|
|
3879
3996
|
() => validateProviderUserInput(request.provider, request.run.input)
|
|
3880
3997
|
);
|
|
3881
|
-
let pendingMessages = 0;
|
|
3882
|
-
let finalText = "";
|
|
3883
3998
|
let streamedTextFromSse = "";
|
|
3884
|
-
const
|
|
3999
|
+
const assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
4000
|
+
const announcedAssistantCompletions = /* @__PURE__ */ new Set();
|
|
3885
4001
|
let dispatchError;
|
|
3886
4002
|
let firstSseEventLogged = false;
|
|
3887
|
-
let resolveAllDone;
|
|
3888
|
-
const allDone = new Promise((resolve) => {
|
|
3889
|
-
resolveAllDone = resolve;
|
|
3890
|
-
});
|
|
3891
|
-
const checkDone = () => {
|
|
3892
|
-
if (pendingMessages === 0) {
|
|
3893
|
-
resolveAllDone();
|
|
3894
|
-
}
|
|
3895
|
-
};
|
|
3896
4003
|
let sendToSession;
|
|
3897
4004
|
const queuedParts = [];
|
|
3898
4005
|
sink.onMessage(async (content) => {
|
|
3899
|
-
pendingMessages++;
|
|
3900
4006
|
try {
|
|
3901
4007
|
const parts = await validateProviderUserInput(
|
|
3902
4008
|
request.provider,
|
|
@@ -3909,11 +4015,10 @@ var OpenCodeAgentAdapter = class {
|
|
|
3909
4015
|
queuedParts.push(mapped);
|
|
3910
4016
|
}
|
|
3911
4017
|
} catch (error) {
|
|
3912
|
-
pendingMessages--;
|
|
3913
4018
|
if (!dispatchError) {
|
|
3914
4019
|
dispatchError = error;
|
|
3915
4020
|
}
|
|
3916
|
-
|
|
4021
|
+
resolveSessionTerminal();
|
|
3917
4022
|
throw error;
|
|
3918
4023
|
}
|
|
3919
4024
|
});
|
|
@@ -3932,10 +4037,15 @@ var OpenCodeAgentAdapter = class {
|
|
|
3932
4037
|
const rawPayloads = [];
|
|
3933
4038
|
const sseAbort = new AbortController();
|
|
3934
4039
|
let sseTask;
|
|
3935
|
-
const dispatchAbort = new AbortController();
|
|
3936
4040
|
let capturedSessionId;
|
|
3937
4041
|
let sessionErrorFromSse;
|
|
3938
4042
|
let sessionAbortedFromSse = false;
|
|
4043
|
+
let sessionIdleFromSse = false;
|
|
4044
|
+
let resolveSessionTerminal;
|
|
4045
|
+
const sessionTerminal = new Promise((resolve) => {
|
|
4046
|
+
resolveSessionTerminal = resolve;
|
|
4047
|
+
});
|
|
4048
|
+
let lastSseActivityAt = Date.now();
|
|
3939
4049
|
let userAbortRequested = false;
|
|
3940
4050
|
sink.setAbort(async () => {
|
|
3941
4051
|
userAbortRequested = true;
|
|
@@ -3963,7 +4073,7 @@ var OpenCodeAgentAdapter = class {
|
|
|
3963
4073
|
} catch {
|
|
3964
4074
|
}
|
|
3965
4075
|
}
|
|
3966
|
-
|
|
4076
|
+
resolveSessionTerminal();
|
|
3967
4077
|
});
|
|
3968
4078
|
try {
|
|
3969
4079
|
const interactiveApproval = isInteractiveApproval(request.options);
|
|
@@ -4004,10 +4114,14 @@ var OpenCodeAgentAdapter = class {
|
|
|
4004
4114
|
const foreignMessageIds = /* @__PURE__ */ new Set();
|
|
4005
4115
|
sseTask = (async () => {
|
|
4006
4116
|
try {
|
|
4007
|
-
for await (const event of
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4117
|
+
for await (const event of streamSseResilient(
|
|
4118
|
+
`${runtime.baseUrl}/event`,
|
|
4119
|
+
{
|
|
4120
|
+
headers: runtime.previewHeaders,
|
|
4121
|
+
signal: sseAbort.signal
|
|
4122
|
+
}
|
|
4123
|
+
)) {
|
|
4124
|
+
lastSseActivityAt = Date.now();
|
|
4011
4125
|
if (!firstSseEventLogged) {
|
|
4012
4126
|
firstSseEventLogged = true;
|
|
4013
4127
|
debugOpencode(
|
|
@@ -4050,6 +4164,22 @@ var OpenCodeAgentAdapter = class {
|
|
|
4050
4164
|
{ messageId: info.id }
|
|
4051
4165
|
)
|
|
4052
4166
|
);
|
|
4167
|
+
} else if (info.role === "assistant" && !announcedAssistantCompletions.has(info.id)) {
|
|
4168
|
+
const time2 = info.time;
|
|
4169
|
+
if (typeof time2?.completed === "number") {
|
|
4170
|
+
announcedAssistantCompletions.add(info.id);
|
|
4171
|
+
sink.emitEvent(
|
|
4172
|
+
createNormalizedEvent(
|
|
4173
|
+
"message.completed",
|
|
4174
|
+
{
|
|
4175
|
+
provider: request.provider,
|
|
4176
|
+
runId: request.runId,
|
|
4177
|
+
raw
|
|
4178
|
+
},
|
|
4179
|
+
{ text: assistantTextByMessageId.get(info.id) ?? "" }
|
|
4180
|
+
)
|
|
4181
|
+
);
|
|
4182
|
+
}
|
|
4053
4183
|
}
|
|
4054
4184
|
}
|
|
4055
4185
|
}
|
|
@@ -4082,7 +4212,7 @@ var OpenCodeAgentAdapter = class {
|
|
|
4082
4212
|
continue;
|
|
4083
4213
|
}
|
|
4084
4214
|
const payloadRecord = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4085
|
-
if (
|
|
4215
|
+
if (payloadRecord?.type === "session.idle" || payloadRecord?.type === "session.error") {
|
|
4086
4216
|
const properties = payloadRecord.properties;
|
|
4087
4217
|
const eventSessionId = typeof properties?.sessionID === "string" ? properties.sessionID : void 0;
|
|
4088
4218
|
if (!eventSessionId || eventSessionId === sessionId) {
|
|
@@ -4094,13 +4224,28 @@ var OpenCodeAgentAdapter = class {
|
|
|
4094
4224
|
const errMsg = typeof errData?.data?.message === "string" ? errData.data.message : typeof errData?.message === "string" ? errData.message : "OpenCode session error";
|
|
4095
4225
|
sessionErrorFromSse = new Error(errMsg);
|
|
4096
4226
|
}
|
|
4227
|
+
} else {
|
|
4228
|
+
sessionIdleFromSse = true;
|
|
4097
4229
|
}
|
|
4098
4230
|
debugOpencode(
|
|
4099
|
-
"\u2605 %s for session=%s
|
|
4231
|
+
"\u2605 %s for session=%s",
|
|
4100
4232
|
payloadRecord.type,
|
|
4101
4233
|
sessionId
|
|
4102
4234
|
);
|
|
4103
|
-
|
|
4235
|
+
resolveSessionTerminal();
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
if (payloadRecord?.type === "session.status") {
|
|
4239
|
+
const properties = payloadRecord.properties;
|
|
4240
|
+
const status = properties?.status;
|
|
4241
|
+
const eventSessionId = typeof properties?.sessionID === "string" ? properties.sessionID : void 0;
|
|
4242
|
+
if ((!eventSessionId || eventSessionId === sessionId) && status?.type === "idle") {
|
|
4243
|
+
sessionIdleFromSse = true;
|
|
4244
|
+
debugOpencode(
|
|
4245
|
+
"\u2605 session.status{idle} for session=%s",
|
|
4246
|
+
sessionId
|
|
4247
|
+
);
|
|
4248
|
+
resolveSessionTerminal();
|
|
4104
4249
|
}
|
|
4105
4250
|
}
|
|
4106
4251
|
if (payloadRecord?.type === "message.part.delta") {
|
|
@@ -4111,12 +4256,15 @@ var OpenCodeAgentAdapter = class {
|
|
|
4111
4256
|
if (isForeignSession) {
|
|
4112
4257
|
continue;
|
|
4113
4258
|
}
|
|
4114
|
-
if (eventMessageId !== void 0 && settledMessageIds.has(eventMessageId)) {
|
|
4115
|
-
continue;
|
|
4116
|
-
}
|
|
4117
4259
|
const delta = typeof properties?.delta === "string" ? properties.delta : "";
|
|
4118
4260
|
if (delta && properties?.field === "text") {
|
|
4119
4261
|
streamedTextFromSse += delta;
|
|
4262
|
+
if (eventMessageId) {
|
|
4263
|
+
assistantTextByMessageId.set(
|
|
4264
|
+
eventMessageId,
|
|
4265
|
+
(assistantTextByMessageId.get(eventMessageId) ?? "") + delta
|
|
4266
|
+
);
|
|
4267
|
+
}
|
|
4120
4268
|
sink.emitEvent(
|
|
4121
4269
|
createNormalizedEvent(
|
|
4122
4270
|
"text.delta",
|
|
@@ -4169,126 +4317,131 @@ var OpenCodeAgentAdapter = class {
|
|
|
4169
4317
|
})
|
|
4170
4318
|
);
|
|
4171
4319
|
const agentSlug = openCodeAgentSlug(request.run.reasoning);
|
|
4172
|
-
const
|
|
4173
|
-
const
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4320
|
+
const dispatchPrompt = async (parts) => {
|
|
4321
|
+
const body = JSON.stringify({
|
|
4322
|
+
...request.run.model ? { model: toOpenCodeModel(request.run.model) } : {},
|
|
4323
|
+
// Per-message system prompt — keeps systemPrompt out of
|
|
4324
|
+
// the on-disk agent config so changing it doesn't
|
|
4325
|
+
// invalidate setupId. Sent on every dispatch (the field
|
|
4326
|
+
// is per-message, not session-sticky).
|
|
4327
|
+
...request.run.systemPrompt ? { system: request.run.systemPrompt } : {},
|
|
4328
|
+
agent: agentSlug,
|
|
4329
|
+
parts
|
|
4330
|
+
});
|
|
4331
|
+
const url = `${runtime.baseUrl}/session/${sessionId}/prompt_async`;
|
|
4332
|
+
const attempt = async () => {
|
|
4333
|
+
return fetch(url, {
|
|
4334
|
+
method: "POST",
|
|
4335
|
+
headers: {
|
|
4336
|
+
"content-type": "application/json",
|
|
4337
|
+
...runtime.previewHeaders
|
|
4338
|
+
},
|
|
4339
|
+
body
|
|
4340
|
+
});
|
|
4341
|
+
};
|
|
4342
|
+
let lastError;
|
|
4343
|
+
for (let i = 0; i < 2; i++) {
|
|
4344
|
+
try {
|
|
4345
|
+
const response = await attempt();
|
|
4346
|
+
if (response.ok || response.status === 204) {
|
|
4347
|
+
return;
|
|
4189
4348
|
}
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
request.runId,
|
|
4193
|
-
response,
|
|
4194
|
-
"message.response"
|
|
4195
|
-
);
|
|
4196
|
-
if (response && typeof response === "object" && !Array.isArray(response)) {
|
|
4197
|
-
rawPayloads.push(response);
|
|
4198
|
-
}
|
|
4199
|
-
sink.emitRaw(rawResponse);
|
|
4200
|
-
for (const event of normalizeRawAgentEvent(rawResponse)) {
|
|
4201
|
-
sink.emitEvent(event);
|
|
4202
|
-
}
|
|
4203
|
-
const reasoning = extractReasoning(response);
|
|
4204
|
-
if (reasoning) {
|
|
4205
|
-
sink.emitEvent(
|
|
4206
|
-
createNormalizedEvent(
|
|
4207
|
-
"reasoning.delta",
|
|
4208
|
-
{
|
|
4209
|
-
provider: request.provider,
|
|
4210
|
-
runId: request.runId,
|
|
4211
|
-
raw: rawResponse
|
|
4212
|
-
},
|
|
4213
|
-
{ delta: reasoning }
|
|
4214
|
-
)
|
|
4349
|
+
lastError = new Error(
|
|
4350
|
+
`POST ${url} returned ${response.status}`
|
|
4215
4351
|
);
|
|
4352
|
+
} catch (error) {
|
|
4353
|
+
lastError = error;
|
|
4216
4354
|
}
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4355
|
+
if (i === 0) {
|
|
4356
|
+
debugOpencode(
|
|
4357
|
+
"prompt_async dispatch attempt %d failed (%s); retrying once",
|
|
4358
|
+
i + 1,
|
|
4359
|
+
lastError?.message ?? String(lastError)
|
|
4222
4360
|
);
|
|
4223
|
-
|
|
4224
|
-
if (text.startsWith(sseTextForThisDispatch)) {
|
|
4225
|
-
missing = text.slice(sseTextForThisDispatch.length);
|
|
4226
|
-
} else if (sseTextForThisDispatch.length === 0) {
|
|
4227
|
-
missing = text;
|
|
4228
|
-
} else {
|
|
4229
|
-
missing = "";
|
|
4230
|
-
}
|
|
4231
|
-
if (missing.length > 0) {
|
|
4232
|
-
streamedTextFromSse += missing;
|
|
4233
|
-
sink.emitEvent(
|
|
4234
|
-
createNormalizedEvent(
|
|
4235
|
-
"text.delta",
|
|
4236
|
-
{
|
|
4237
|
-
provider: request.provider,
|
|
4238
|
-
runId: request.runId
|
|
4239
|
-
},
|
|
4240
|
-
{ delta: missing }
|
|
4241
|
-
)
|
|
4242
|
-
);
|
|
4243
|
-
}
|
|
4244
|
-
const assistantMessageId = extractAssistantMessageId(response);
|
|
4245
|
-
if (assistantMessageId) {
|
|
4246
|
-
settledMessageIds.add(assistantMessageId);
|
|
4247
|
-
}
|
|
4361
|
+
await sleep(500);
|
|
4248
4362
|
}
|
|
4249
|
-
} catch (error) {
|
|
4250
|
-
if (!dispatchError) {
|
|
4251
|
-
dispatchError = error;
|
|
4252
|
-
}
|
|
4253
|
-
} finally {
|
|
4254
|
-
pendingMessages--;
|
|
4255
|
-
checkDone();
|
|
4256
4363
|
}
|
|
4364
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
4257
4365
|
};
|
|
4258
4366
|
sendToSession = (parts) => {
|
|
4259
|
-
void
|
|
4367
|
+
void (async () => {
|
|
4368
|
+
try {
|
|
4369
|
+
await dispatchPrompt(parts);
|
|
4370
|
+
} catch (error) {
|
|
4371
|
+
if (!dispatchError) {
|
|
4372
|
+
dispatchError = error;
|
|
4373
|
+
}
|
|
4374
|
+
resolveSessionTerminal();
|
|
4375
|
+
}
|
|
4376
|
+
})();
|
|
4260
4377
|
};
|
|
4261
4378
|
for (const queued of queuedParts.splice(0)) {
|
|
4262
4379
|
sendToSession(queued);
|
|
4263
4380
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4381
|
+
try {
|
|
4382
|
+
await dispatchPrompt(mapToOpenCodeParts(inputParts));
|
|
4383
|
+
} catch (error) {
|
|
4384
|
+
if (!dispatchError) {
|
|
4385
|
+
dispatchError = error;
|
|
4386
|
+
}
|
|
4387
|
+
resolveSessionTerminal();
|
|
4388
|
+
}
|
|
4389
|
+
const SSE_SILENCE_THRESHOLD_MS = 18e4;
|
|
4390
|
+
const SSE_POLL_INTERVAL_MS = 5e3;
|
|
4391
|
+
lastSseActivityAt = Date.now();
|
|
4392
|
+
let sseSilent = false;
|
|
4393
|
+
while (!sessionIdleFromSse && !sessionErrorFromSse && !sessionAbortedFromSse && !userAbortRequested && !dispatchError) {
|
|
4394
|
+
const silence = Date.now() - lastSseActivityAt;
|
|
4395
|
+
if (silence > SSE_SILENCE_THRESHOLD_MS) {
|
|
4396
|
+
sseSilent = true;
|
|
4397
|
+
debugOpencode("SSE went silent (%dms) \u2014 giving up", silence);
|
|
4398
|
+
break;
|
|
4399
|
+
}
|
|
4400
|
+
await Promise.race([
|
|
4401
|
+
sessionTerminal,
|
|
4402
|
+
new Promise(
|
|
4403
|
+
(resolve) => setTimeout(resolve, SSE_POLL_INTERVAL_MS)
|
|
4404
|
+
)
|
|
4405
|
+
]);
|
|
4406
|
+
}
|
|
4407
|
+
sseAbort.abort();
|
|
4408
|
+
await sseTask;
|
|
4267
4409
|
if (userAbortRequested || sessionAbortedFromSse) {
|
|
4268
4410
|
debugOpencode(
|
|
4269
4411
|
"\u2605 run.cancelled (%dms since execute start)",
|
|
4270
4412
|
Date.now() - executeStartedAt
|
|
4271
4413
|
);
|
|
4272
|
-
sseAbort.abort();
|
|
4273
|
-
await sseTask;
|
|
4274
4414
|
sink.cancel({
|
|
4275
4415
|
text: streamedTextFromSse || void 0,
|
|
4276
4416
|
costData: extractOpenCodeCostData(rawPayloads)
|
|
4277
4417
|
});
|
|
4278
4418
|
} else if (sessionErrorFromSse) {
|
|
4279
|
-
sseAbort.abort();
|
|
4280
|
-
await sseTask;
|
|
4281
4419
|
sink.fail(sessionErrorFromSse);
|
|
4282
|
-
} else if (dispatchError
|
|
4283
|
-
sseAbort.abort();
|
|
4284
|
-
await sseTask;
|
|
4420
|
+
} else if (dispatchError) {
|
|
4285
4421
|
sink.fail(dispatchError);
|
|
4286
|
-
} else {
|
|
4422
|
+
} else if (sessionIdleFromSse) {
|
|
4287
4423
|
debugOpencode(
|
|
4288
4424
|
"\u2605 run.completed (%dms since execute start) chars=%d",
|
|
4289
4425
|
Date.now() - executeStartedAt,
|
|
4290
|
-
|
|
4426
|
+
streamedTextFromSse.length
|
|
4291
4427
|
);
|
|
4428
|
+
let lastAssistantText = "";
|
|
4429
|
+
for (const [messageId, text] of assistantTextByMessageId) {
|
|
4430
|
+
lastAssistantText = text;
|
|
4431
|
+
if (!announcedAssistantCompletions.has(messageId)) {
|
|
4432
|
+
announcedAssistantCompletions.add(messageId);
|
|
4433
|
+
sink.emitEvent(
|
|
4434
|
+
createNormalizedEvent(
|
|
4435
|
+
"message.completed",
|
|
4436
|
+
{
|
|
4437
|
+
provider: request.provider,
|
|
4438
|
+
runId: request.runId
|
|
4439
|
+
},
|
|
4440
|
+
{ text }
|
|
4441
|
+
)
|
|
4442
|
+
);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4292
4445
|
sink.emitEvent(
|
|
4293
4446
|
createNormalizedEvent(
|
|
4294
4447
|
"run.completed",
|
|
@@ -4296,15 +4449,23 @@ var OpenCodeAgentAdapter = class {
|
|
|
4296
4449
|
provider: request.provider,
|
|
4297
4450
|
runId: request.runId
|
|
4298
4451
|
},
|
|
4299
|
-
{ text:
|
|
4452
|
+
{ text: lastAssistantText }
|
|
4300
4453
|
)
|
|
4301
4454
|
);
|
|
4302
|
-
sseAbort.abort();
|
|
4303
|
-
await sseTask;
|
|
4304
4455
|
sink.complete({
|
|
4305
|
-
text:
|
|
4456
|
+
text: lastAssistantText,
|
|
4306
4457
|
costData: extractOpenCodeCostData(rawPayloads)
|
|
4307
4458
|
});
|
|
4459
|
+
} else if (sseSilent) {
|
|
4460
|
+
sink.fail(
|
|
4461
|
+
new Error(
|
|
4462
|
+
"opencode SSE went silent before the session reached idle"
|
|
4463
|
+
)
|
|
4464
|
+
);
|
|
4465
|
+
} else {
|
|
4466
|
+
sink.fail(
|
|
4467
|
+
new Error("opencode run ended without a terminal signal")
|
|
4468
|
+
);
|
|
4308
4469
|
}
|
|
4309
4470
|
} finally {
|
|
4310
4471
|
sseAbort.abort();
|
|
@@ -4350,10 +4511,10 @@ var OpenCodeAgentAdapter = class {
|
|
|
4350
4511
|
}
|
|
4351
4512
|
}
|
|
4352
4513
|
/**
|
|
4353
|
-
* Stateless message injection. POST
|
|
4354
|
-
* `/session/:id/
|
|
4355
|
-
*
|
|
4356
|
-
*
|
|
4514
|
+
* Stateless message injection. Fire-and-forget POST to
|
|
4515
|
+
* `/session/:id/prompt_async` (returns 204) — opencode appends the
|
|
4516
|
+
* message to the running session and the originating instance picks
|
|
4517
|
+
* up the new turn through its existing SSE stream.
|
|
4357
4518
|
*/
|
|
4358
4519
|
async attachSendMessage(request, content) {
|
|
4359
4520
|
if (!request.sessionId) {
|
|
@@ -4367,20 +4528,21 @@ var OpenCodeAgentAdapter = class {
|
|
|
4367
4528
|
content
|
|
4368
4529
|
);
|
|
4369
4530
|
const parts = mapToOpenCodeParts(inputParts);
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4531
|
+
const url = `${baseUrl}/session/${request.sessionId}/prompt_async`;
|
|
4532
|
+
const response = await fetch(url, {
|
|
4533
|
+
method: "POST",
|
|
4534
|
+
headers: {
|
|
4535
|
+
"content-type": "application/json",
|
|
4536
|
+
...request.sandbox.previewHeaders
|
|
4537
|
+
},
|
|
4538
|
+
body: JSON.stringify({
|
|
4539
|
+
agent: openCodeAgentSlug(void 0),
|
|
4540
|
+
parts
|
|
4541
|
+
})
|
|
4542
|
+
});
|
|
4543
|
+
if (!response.ok && response.status !== 204) {
|
|
4544
|
+
throw new Error(`POST ${url} returned ${response.status}`);
|
|
4545
|
+
}
|
|
4384
4546
|
}
|
|
4385
4547
|
};
|
|
4386
4548
|
|
|
@@ -4463,6 +4625,7 @@ var AgentRunController = class {
|
|
|
4463
4625
|
pendingPermissions = /* @__PURE__ */ new Map();
|
|
4464
4626
|
messageHandler;
|
|
4465
4627
|
text = "";
|
|
4628
|
+
observedMessageCompleted = false;
|
|
4466
4629
|
costData = null;
|
|
4467
4630
|
settled = false;
|
|
4468
4631
|
finished;
|
|
@@ -4513,6 +4676,9 @@ var AgentRunController = class {
|
|
|
4513
4676
|
this.text += event.delta;
|
|
4514
4677
|
} else if ((event.type === "message.completed" || event.type === "run.completed") && event.text) {
|
|
4515
4678
|
this.text = event.text;
|
|
4679
|
+
if (event.type === "message.completed") {
|
|
4680
|
+
this.observedMessageCompleted = true;
|
|
4681
|
+
}
|
|
4516
4682
|
}
|
|
4517
4683
|
this.eventQueue.push(event);
|
|
4518
4684
|
}
|
|
@@ -4610,7 +4776,7 @@ var AgentRunController = class {
|
|
|
4610
4776
|
"Agent run completed before pending permission requests resolved."
|
|
4611
4777
|
)
|
|
4612
4778
|
);
|
|
4613
|
-
if (result?.text) {
|
|
4779
|
+
if (result?.text && !this.observedMessageCompleted) {
|
|
4614
4780
|
this.text = result.text;
|
|
4615
4781
|
}
|
|
4616
4782
|
if (result && "costData" in result) {
|
|
@@ -4651,9 +4817,11 @@ var AgentRunController = class {
|
|
|
4651
4817
|
}
|
|
4652
4818
|
this.settled = true;
|
|
4653
4819
|
this.clearPendingPermissions(
|
|
4654
|
-
new Error(
|
|
4820
|
+
new Error(
|
|
4821
|
+
"Agent run was cancelled before pending permission requests resolved."
|
|
4822
|
+
)
|
|
4655
4823
|
);
|
|
4656
|
-
if (result?.text) {
|
|
4824
|
+
if (result?.text && !this.observedMessageCompleted) {
|
|
4657
4825
|
this.text = result.text;
|
|
4658
4826
|
}
|
|
4659
4827
|
if (result && "costData" in result) {
|
|
@@ -4768,7 +4936,7 @@ var Agent = class {
|
|
|
4768
4936
|
* second `setup()` against the same sandbox does ~one round-trip of
|
|
4769
4937
|
* work.
|
|
4770
4938
|
*/
|
|
4771
|
-
async setup(
|
|
4939
|
+
async setup() {
|
|
4772
4940
|
if (this.setupPromise) {
|
|
4773
4941
|
await this.setupPromise;
|
|
4774
4942
|
return;
|
|
@@ -4778,8 +4946,7 @@ var Agent = class {
|
|
|
4778
4946
|
this.setupPromise = (async () => {
|
|
4779
4947
|
await this.adapter.setup({
|
|
4780
4948
|
provider: this.provider,
|
|
4781
|
-
options: this.options
|
|
4782
|
-
config
|
|
4949
|
+
options: this.options
|
|
4783
4950
|
});
|
|
4784
4951
|
debugAgent(
|
|
4785
4952
|
"setup() returned provider=%s after %dms",
|
|
@@ -4801,14 +4968,10 @@ var Agent = class {
|
|
|
4801
4968
|
);
|
|
4802
4969
|
}
|
|
4803
4970
|
if (runConfig.forkSessionId && !runConfig.forkAtMessageId) {
|
|
4804
|
-
throw new Error(
|
|
4805
|
-
"AgentRunConfig.forkSessionId requires forkAtMessageId."
|
|
4806
|
-
);
|
|
4971
|
+
throw new Error("AgentRunConfig.forkSessionId requires forkAtMessageId.");
|
|
4807
4972
|
}
|
|
4808
4973
|
if (runConfig.forkAtMessageId && !runConfig.forkSessionId) {
|
|
4809
|
-
throw new Error(
|
|
4810
|
-
"AgentRunConfig.forkAtMessageId requires forkSessionId."
|
|
4811
|
-
);
|
|
4974
|
+
throw new Error("AgentRunConfig.forkAtMessageId requires forkSessionId.");
|
|
4812
4975
|
}
|
|
4813
4976
|
const runId = runConfig.runId ?? randomUUID2();
|
|
4814
4977
|
const streamCalledAt = Date.now();
|