@cabane/companion 0.6.141 → 0.6.143
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/cli.js +276 -165
- package/dist/runtime.js +230 -126
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -17,14 +17,14 @@ import { z as z2 } from "zod";
|
|
|
17
17
|
|
|
18
18
|
// src/errors.ts
|
|
19
19
|
var CompanionError = class extends Error {
|
|
20
|
-
constructor(
|
|
21
|
-
super(
|
|
20
|
+
constructor(message2) {
|
|
21
|
+
super(message2);
|
|
22
22
|
this.name = "CompanionError";
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
var ApiError = class extends CompanionError {
|
|
26
|
-
constructor(status,
|
|
27
|
-
super(
|
|
26
|
+
constructor(status, message2, body) {
|
|
27
|
+
super(message2);
|
|
28
28
|
this.status = status;
|
|
29
29
|
this.body = body;
|
|
30
30
|
this.name = "ApiError";
|
|
@@ -33,14 +33,14 @@ var ApiError = class extends CompanionError {
|
|
|
33
33
|
body;
|
|
34
34
|
};
|
|
35
35
|
var ConfigError = class extends CompanionError {
|
|
36
|
-
constructor(
|
|
37
|
-
super(
|
|
36
|
+
constructor(message2) {
|
|
37
|
+
super(message2);
|
|
38
38
|
this.name = "ConfigError";
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
var PrepareHookError = class extends CompanionError {
|
|
42
|
-
constructor(
|
|
43
|
-
super(
|
|
42
|
+
constructor(message2) {
|
|
43
|
+
super(message2);
|
|
44
44
|
this.name = "PrepareHookError";
|
|
45
45
|
}
|
|
46
46
|
};
|
|
@@ -1202,8 +1202,8 @@ function formatHumanLine(record) {
|
|
|
1202
1202
|
let line = `${time} ${word ? `${word} ` : ""}${context ? `${context} ` : ""}${humanText(record.msg)}`;
|
|
1203
1203
|
const err = record.err;
|
|
1204
1204
|
const errorRecord = err !== null && typeof err === "object" ? err : null;
|
|
1205
|
-
const
|
|
1206
|
-
const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) ||
|
|
1205
|
+
const message2 = humanText(errorRecord && "message" in errorRecord ? errorRecord.message : err);
|
|
1206
|
+
const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) || message2}` : message2;
|
|
1207
1207
|
if (detail && !line.includes(detail)) line += ` (${detail})`;
|
|
1208
1208
|
if (level < 30) {
|
|
1209
1209
|
const hidden = /* @__PURE__ */ new Set([
|
|
@@ -1337,9 +1337,9 @@ var CompanionStateHub = class {
|
|
|
1337
1337
|
if (info.deviceLabel !== void 0) this.deviceLabel = info.deviceLabel;
|
|
1338
1338
|
this.emitStatus();
|
|
1339
1339
|
}
|
|
1340
|
-
setDeviceError(
|
|
1341
|
-
if (this.deviceError ===
|
|
1342
|
-
this.deviceError =
|
|
1340
|
+
setDeviceError(message2) {
|
|
1341
|
+
if (this.deviceError === message2) return;
|
|
1342
|
+
this.deviceError = message2;
|
|
1343
1343
|
this.emitStatus();
|
|
1344
1344
|
}
|
|
1345
1345
|
clearDeviceError() {
|
|
@@ -1960,7 +1960,7 @@ async function warnAboutHarnessReadiness(cfg, deps = {}) {
|
|
|
1960
1960
|
const probeClaude = deps.probeClaude ?? claudeOnPath;
|
|
1961
1961
|
const probeCodex = deps.probeCodex ?? codexOnPath;
|
|
1962
1962
|
const resolveBundled = deps.resolveBundled ?? ((runtime) => resolveBundledBinary(runtime));
|
|
1963
|
-
const warn = deps.warn ?? ((
|
|
1963
|
+
const warn = deps.warn ?? ((message2) => process.stderr.write(`${message2}
|
|
1964
1964
|
`));
|
|
1965
1965
|
const connected = [
|
|
1966
1966
|
...isClaudeCodeConnected(cfg) ? ["Claude Code"] : [],
|
|
@@ -2305,8 +2305,8 @@ function apiErrorCode(err) {
|
|
|
2305
2305
|
function apiErrorMessage(err) {
|
|
2306
2306
|
const body = err instanceof ApiError ? err.body : void 0;
|
|
2307
2307
|
if (body !== null && typeof body === "object") {
|
|
2308
|
-
const
|
|
2309
|
-
if (typeof
|
|
2308
|
+
const message2 = body.message;
|
|
2309
|
+
if (typeof message2 === "string" && message2.length > 0) return message2;
|
|
2310
2310
|
}
|
|
2311
2311
|
return err instanceof Error ? err.message : String(err);
|
|
2312
2312
|
}
|
|
@@ -2341,9 +2341,9 @@ function describeErrorBody(body) {
|
|
|
2341
2341
|
}
|
|
2342
2342
|
var CAUSE_TEXT_CAP = 200;
|
|
2343
2343
|
function describeError(err) {
|
|
2344
|
-
const
|
|
2344
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
2345
2345
|
const cause = describeCause(err);
|
|
2346
|
-
return cause === void 0 ?
|
|
2346
|
+
return cause === void 0 ? message2 : `${message2}: ${cause}`;
|
|
2347
2347
|
}
|
|
2348
2348
|
function describeCause(err) {
|
|
2349
2349
|
if (!(err instanceof Error)) return void 0;
|
|
@@ -3990,8 +3990,7 @@ function turnControlPromptGuidance(prefix) {
|
|
|
3990
3990
|
var CLAUDE_CODE_ADDENDUM = [
|
|
3991
3991
|
"On this harness the Cabane tools register under MCP prefixes. The workspace tools are",
|
|
3992
3992
|
"`mcp__cabane__query`, `mcp__cabane__mutate` and `mcp__cabane__describe`; the ancillary",
|
|
3993
|
-
"tools are `
|
|
3994
|
-
"`mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
3993
|
+
"tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
3995
3994
|
"`mcp__cabane__finalize_upload`, and `mcp__cabane__mint_render_token`.",
|
|
3996
3995
|
turnControlPromptGuidance("mcp__cabane_companion__"),
|
|
3997
3996
|
"Where this prompt names a tool by its bare verb, call",
|
|
@@ -4379,15 +4378,15 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4379
4378
|
}
|
|
4380
4379
|
} catch (err) {
|
|
4381
4380
|
if (ctx.signal.aborted) throw err;
|
|
4382
|
-
const
|
|
4383
|
-
const failure = mcpInventory.initReceived ? classifyErrorText(
|
|
4381
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
4382
|
+
const failure = mcpInventory.initReceived ? classifyErrorText(message2) : classifyFailure(signals, { text: message2 });
|
|
4384
4383
|
if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
|
|
4385
4384
|
ok = false;
|
|
4386
4385
|
if (failure) {
|
|
4387
4386
|
resultReason = encodeFailureReason(failure);
|
|
4388
4387
|
} else {
|
|
4389
4388
|
resultReason = "session_start_failed";
|
|
4390
|
-
if (ctx.startEvidence) ctx.startEvidence.thrown =
|
|
4389
|
+
if (ctx.startEvidence) ctx.startEvidence.thrown = message2.slice(0, 200);
|
|
4391
4390
|
}
|
|
4392
4391
|
sawResult = true;
|
|
4393
4392
|
}
|
|
@@ -5228,7 +5227,7 @@ var OPENCODE_ADDENDUM = [
|
|
|
5228
5227
|
"Every tool here goes by its plain name: the workspace tools are `query`, `mutate` and",
|
|
5229
5228
|
"`describe`.",
|
|
5230
5229
|
turnControlPromptGuidance(""),
|
|
5231
|
-
"The ancillary tools are `
|
|
5230
|
+
"The ancillary tools are `read_binary`, `upload`, `begin_upload`,",
|
|
5232
5231
|
"`finalize_upload`, `mint_render_token`; and the host tools are plain verbs too (`bash`,",
|
|
5233
5232
|
"`read`, `edit`). Mind the collision: a bare `read` or `edit` is the HOST tool, acting on the",
|
|
5234
5233
|
"local machine \u2014 a workspace read is `query` with the operation name `files.read`. The",
|
|
@@ -5316,9 +5315,9 @@ function readSessionError(properties) {
|
|
|
5316
5315
|
if (typeof err === "string") return err;
|
|
5317
5316
|
const rec = asRecord(err);
|
|
5318
5317
|
if (!rec) return "unknown";
|
|
5319
|
-
const { name, message } = deepestError(rec);
|
|
5320
|
-
if (
|
|
5321
|
-
if (
|
|
5318
|
+
const { name, message: message2 } = deepestError(rec);
|
|
5319
|
+
if (message2 && name && !isGenericErrorName(name)) return `${name}: ${message2}`;
|
|
5320
|
+
if (message2) return message2;
|
|
5322
5321
|
if (name) return name;
|
|
5323
5322
|
if (typeof rec.name === "string") return rec.name;
|
|
5324
5323
|
return "unknown";
|
|
@@ -5331,10 +5330,10 @@ function deepestError(node) {
|
|
|
5331
5330
|
const best = {};
|
|
5332
5331
|
let current = node;
|
|
5333
5332
|
for (let depth = 0; current && depth < 8; depth++) {
|
|
5334
|
-
const
|
|
5333
|
+
const message2 = typeof current.message === "string" ? current.message : void 0;
|
|
5335
5334
|
const name = typeof current.name === "string" && !isGenericErrorName(current.name) ? current.name : void 0;
|
|
5336
|
-
if (
|
|
5337
|
-
best.message =
|
|
5335
|
+
if (message2) {
|
|
5336
|
+
best.message = message2;
|
|
5338
5337
|
if (name) best.name = name;
|
|
5339
5338
|
} else if (name && !best.name) {
|
|
5340
5339
|
best.name = name;
|
|
@@ -5925,9 +5924,9 @@ var echoedUserPart = (partId, messageID, text) => ({
|
|
|
5925
5924
|
properties: { part: { type: "text", id: partId, messageID, text } }
|
|
5926
5925
|
});
|
|
5927
5926
|
var idle = () => ({ type: "session.idle", properties: {} });
|
|
5928
|
-
var errored = (
|
|
5927
|
+
var errored = (message2) => ({
|
|
5929
5928
|
type: "session.error",
|
|
5930
|
-
properties: { error: { message } }
|
|
5929
|
+
properties: { error: { message: message2 } }
|
|
5931
5930
|
});
|
|
5932
5931
|
var sessionEvent2 = (sessionId, directory = DIR, degraded = false) => ({
|
|
5933
5932
|
type: "session",
|
|
@@ -6333,8 +6332,8 @@ var CODEX_ADDENDUM = [
|
|
|
6333
6332
|
// in sat two lines under the instruction to invoke the exact qualified names,
|
|
6334
6333
|
// so the sentence contradicted itself. The bare `query` / `mutate` below stay:
|
|
6335
6334
|
// that sentence is about operation names, after the qualified forms are given.
|
|
6336
|
-
"The ancillary tools are `
|
|
6337
|
-
"`
|
|
6335
|
+
"The ancillary tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`,",
|
|
6336
|
+
"`mcp__cabane__begin_upload`, `mcp__cabane__finalize_upload` and",
|
|
6338
6337
|
"`mcp__cabane__mint_render_token` \u2014 all possibly deferred too.",
|
|
6339
6338
|
"There is no Cabane `read`/`write`/`search` tool \u2014 those are",
|
|
6340
6339
|
"operation NAMES you pass to `query` / `mutate`. One more harness fact: you can interleave",
|
|
@@ -6362,8 +6361,8 @@ function readItemText(item) {
|
|
|
6362
6361
|
function readItemMessage(item) {
|
|
6363
6362
|
return str(item.message) ?? "";
|
|
6364
6363
|
}
|
|
6365
|
-
function isModelMetadataError(
|
|
6366
|
-
return
|
|
6364
|
+
function isModelMetadataError(message2) {
|
|
6365
|
+
return message2.includes("Defaulting to fallback metadata");
|
|
6367
6366
|
}
|
|
6368
6367
|
function readToolItem(item, eventType) {
|
|
6369
6368
|
const type = str(item.type);
|
|
@@ -6598,11 +6597,11 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6598
6597
|
if (!item) continue;
|
|
6599
6598
|
const itemType = readItemType(item);
|
|
6600
6599
|
if (itemType === "error") {
|
|
6601
|
-
const
|
|
6602
|
-
if (isModelMetadataError(
|
|
6600
|
+
const message2 = readItemMessage(item);
|
|
6601
|
+
if (isModelMetadataError(message2)) {
|
|
6603
6602
|
yield* flushInterim();
|
|
6604
6603
|
ok = false;
|
|
6605
|
-
reason = `model_unavailable:${
|
|
6604
|
+
reason = `model_unavailable:${message2.slice(0, 200)}`;
|
|
6606
6605
|
settled = true;
|
|
6607
6606
|
break;
|
|
6608
6607
|
}
|
|
@@ -6759,7 +6758,7 @@ var ENV_ENVELOPE_KEYS = [
|
|
|
6759
6758
|
"CABANE_CONVERSATION_TITLE",
|
|
6760
6759
|
"CABANE_HOST_ACCESS"
|
|
6761
6760
|
];
|
|
6762
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null) {
|
|
6761
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
|
|
6763
6762
|
const { policy, config } = req;
|
|
6764
6763
|
const directory = req.local.cwd ?? "";
|
|
6765
6764
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
@@ -6776,7 +6775,7 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
6776
6775
|
skipGitRepoCheck: true,
|
|
6777
6776
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
6778
6777
|
baseInstructionsFile,
|
|
6779
|
-
input: buildInput(req, promptRidesInput),
|
|
6778
|
+
input: withImages(buildInput(req, promptRidesInput), imagePaths),
|
|
6780
6779
|
promptRidesInput,
|
|
6781
6780
|
promptFingerprint,
|
|
6782
6781
|
config: buildConfig(req, baseInstructionsFile),
|
|
@@ -6792,6 +6791,13 @@ function buildInput(req, promptRidesInput) {
|
|
|
6792
6791
|
|
|
6793
6792
|
${body}` : body;
|
|
6794
6793
|
}
|
|
6794
|
+
function withImages(text, imagePaths) {
|
|
6795
|
+
if (imagePaths.length === 0) return text;
|
|
6796
|
+
return [
|
|
6797
|
+
{ type: "text", text },
|
|
6798
|
+
...imagePaths.map((path) => ({ type: "local_image", path }))
|
|
6799
|
+
];
|
|
6800
|
+
}
|
|
6795
6801
|
function buildConfig(req, baseInstructionsFile) {
|
|
6796
6802
|
const mcp_servers = {};
|
|
6797
6803
|
for (const [name, server] of Object.entries(req.local.mcpServers ?? {})) {
|
|
@@ -7033,12 +7039,35 @@ function createCodexAdapter(deps = {}) {
|
|
|
7033
7039
|
}
|
|
7034
7040
|
}
|
|
7035
7041
|
}
|
|
7042
|
+
let images = null;
|
|
7036
7043
|
try {
|
|
7044
|
+
const imageUrls = req.content.flatMap((b) => b.type === "image" ? [b.source.url] : []);
|
|
7045
|
+
if (imageUrls.length > 0) {
|
|
7046
|
+
if (!deps.prepareImages) {
|
|
7047
|
+
deps.onWarn?.(
|
|
7048
|
+
"codex adapter: no image preparer wired on this host \u2014 the turn runs without its images (CT1757)",
|
|
7049
|
+
{ images: imageUrls.length }
|
|
7050
|
+
);
|
|
7051
|
+
} else {
|
|
7052
|
+
try {
|
|
7053
|
+
images = await deps.prepareImages(imageUrls, signal);
|
|
7054
|
+
} catch (err) {
|
|
7055
|
+
if (!signal.aborted) {
|
|
7056
|
+
deps.onWarn?.(
|
|
7057
|
+
"codex adapter: preparing the images failed \u2014 the turn runs without them (CT1757)",
|
|
7058
|
+
{ err: err instanceof Error ? err.message : String(err) }
|
|
7059
|
+
);
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
if (signal.aborted) return;
|
|
7064
|
+
}
|
|
7037
7065
|
const spec = buildRunSpec2(
|
|
7038
7066
|
req,
|
|
7039
7067
|
resumeThreadId,
|
|
7040
7068
|
instructions?.path ?? null,
|
|
7041
|
-
threadPromptFingerprint
|
|
7069
|
+
threadPromptFingerprint,
|
|
7070
|
+
images?.paths ?? []
|
|
7042
7071
|
);
|
|
7043
7072
|
if (spec.model === null) {
|
|
7044
7073
|
deps.onWarn?.("Codex is using its default model because no model was resolved", {
|
|
@@ -7067,6 +7096,15 @@ function createCodexAdapter(deps = {}) {
|
|
|
7067
7096
|
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
7068
7097
|
});
|
|
7069
7098
|
} finally {
|
|
7099
|
+
if (images) {
|
|
7100
|
+
try {
|
|
7101
|
+
await images.cleanup();
|
|
7102
|
+
} catch (err) {
|
|
7103
|
+
deps.onWarn?.("codex adapter: image cleanup failed (CT1757)", {
|
|
7104
|
+
err: err instanceof Error ? err.message : String(err)
|
|
7105
|
+
});
|
|
7106
|
+
}
|
|
7107
|
+
}
|
|
7070
7108
|
if (instructions) {
|
|
7071
7109
|
try {
|
|
7072
7110
|
await instructions.cleanup();
|
|
@@ -7133,14 +7171,14 @@ var turnCompleted = (usage) => ({
|
|
|
7133
7171
|
type: "turn.completed",
|
|
7134
7172
|
...usage ? { usage } : {}
|
|
7135
7173
|
});
|
|
7136
|
-
var turnFailed = (
|
|
7174
|
+
var turnFailed = (message2) => ({
|
|
7137
7175
|
type: "turn.failed",
|
|
7138
|
-
error: { message }
|
|
7176
|
+
error: { message: message2 }
|
|
7139
7177
|
});
|
|
7140
|
-
var fatalError = (
|
|
7141
|
-
var errorItem = (id,
|
|
7178
|
+
var fatalError = (message2) => ({ type: "error", message: message2 });
|
|
7179
|
+
var errorItem = (id, message2) => ({
|
|
7142
7180
|
type: "item.completed",
|
|
7143
|
-
item: { id, type: "error", message }
|
|
7181
|
+
item: { id, type: "error", message: message2 }
|
|
7144
7182
|
});
|
|
7145
7183
|
var OP_SUCCESS = { ok: true, result: { source: "native" } };
|
|
7146
7184
|
var OP_FAILURE = {
|
|
@@ -8059,7 +8097,7 @@ var ConnectorHealthStore = class {
|
|
|
8059
8097
|
// src/dispatcher.ts
|
|
8060
8098
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8061
8099
|
import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
8062
|
-
import { join as
|
|
8100
|
+
import { join as join15 } from "path";
|
|
8063
8101
|
|
|
8064
8102
|
// src/runner/launch.ts
|
|
8065
8103
|
import { createInterface } from "readline";
|
|
@@ -8125,8 +8163,8 @@ function unenforceable(policy) {
|
|
|
8125
8163
|
deniedDomains: as === "deniedDomains" ? [host] : []
|
|
8126
8164
|
});
|
|
8127
8165
|
if (!check.success) {
|
|
8128
|
-
const
|
|
8129
|
-
return `runners.policy.${field}.${i}: ${
|
|
8166
|
+
const message2 = check.error.issues[0]?.message ?? "not a host pattern the sandbox accepts";
|
|
8167
|
+
return `runners.policy.${field}.${i}: ${message2}`;
|
|
8130
8168
|
}
|
|
8131
8169
|
}
|
|
8132
8170
|
}
|
|
@@ -8140,8 +8178,8 @@ function unenforceable(policy) {
|
|
|
8140
8178
|
try {
|
|
8141
8179
|
new RegExp(globToRegex(path));
|
|
8142
8180
|
} catch (err) {
|
|
8143
|
-
const
|
|
8144
|
-
return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${
|
|
8181
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8182
|
+
return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${message2})`;
|
|
8145
8183
|
}
|
|
8146
8184
|
}
|
|
8147
8185
|
}
|
|
@@ -8260,8 +8298,8 @@ async function reapTurnScopes(turnId) {
|
|
|
8260
8298
|
});
|
|
8261
8299
|
return { failures: [] };
|
|
8262
8300
|
} catch (err) {
|
|
8263
|
-
const
|
|
8264
|
-
return classifyStopError(
|
|
8301
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8302
|
+
return classifyStopError(message2) === "absent" ? { failures: [] } : { failures: [message2.slice(0, 200)] };
|
|
8265
8303
|
}
|
|
8266
8304
|
}
|
|
8267
8305
|
function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
|
|
@@ -8277,13 +8315,13 @@ function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
|
|
|
8277
8315
|
return false;
|
|
8278
8316
|
}
|
|
8279
8317
|
}
|
|
8280
|
-
function classifyStopError(
|
|
8318
|
+
function classifyStopError(message2) {
|
|
8281
8319
|
if (/failed to connect to bus|failed to get d-?bus connection|connection refused|refusing to operate|spawn systemctl|systemctl.*ENOENT|ENOENT.*systemctl/i.test(
|
|
8282
|
-
|
|
8320
|
+
message2
|
|
8283
8321
|
)) {
|
|
8284
8322
|
return "failure";
|
|
8285
8323
|
}
|
|
8286
|
-
if (/not loaded|could not be found|no such unit|unit .* not found/i.test(
|
|
8324
|
+
if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message2))
|
|
8287
8325
|
return "absent";
|
|
8288
8326
|
return "failure";
|
|
8289
8327
|
}
|
|
@@ -8395,9 +8433,9 @@ function createTurnContainment(turnId, deps = {}) {
|
|
|
8395
8433
|
timeout: (CONTAINMENT_STOP_GRACE_SEC + 10) * 1e3
|
|
8396
8434
|
});
|
|
8397
8435
|
} catch (err) {
|
|
8398
|
-
const
|
|
8399
|
-
if (classifyStopError(
|
|
8400
|
-
result.failures.push({ target: unit, error:
|
|
8436
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8437
|
+
if (classifyStopError(message2) === "failure") {
|
|
8438
|
+
result.failures.push({ target: unit, error: message2.slice(0, 200) });
|
|
8401
8439
|
continue;
|
|
8402
8440
|
}
|
|
8403
8441
|
result.reaped.push(unit);
|
|
@@ -8802,8 +8840,8 @@ function runnerStdin(input) {
|
|
|
8802
8840
|
})}
|
|
8803
8841
|
`;
|
|
8804
8842
|
}
|
|
8805
|
-
function encodeLauncherMessage(
|
|
8806
|
-
return `${JSON.stringify(
|
|
8843
|
+
function encodeLauncherMessage(message2) {
|
|
8844
|
+
return `${JSON.stringify(message2)}
|
|
8807
8845
|
`;
|
|
8808
8846
|
}
|
|
8809
8847
|
function decodeRunnerEvent(line) {
|
|
@@ -9535,6 +9573,68 @@ async function writeCodexInstructionsFile(contents) {
|
|
|
9535
9573
|
};
|
|
9536
9574
|
}
|
|
9537
9575
|
|
|
9576
|
+
// src/codex-images.ts
|
|
9577
|
+
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
9578
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
9579
|
+
import { join as join12 } from "path";
|
|
9580
|
+
var FETCH_TIMEOUT_MS = 3e4;
|
|
9581
|
+
var EXTENSIONS = {
|
|
9582
|
+
"image/png": "png",
|
|
9583
|
+
"image/jpeg": "jpg",
|
|
9584
|
+
"image/gif": "gif",
|
|
9585
|
+
"image/webp": "webp"
|
|
9586
|
+
};
|
|
9587
|
+
function createCodexImagePreparer(opts) {
|
|
9588
|
+
const doFetch = opts.fetch ?? fetch;
|
|
9589
|
+
const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
9590
|
+
return async (urls, signal) => {
|
|
9591
|
+
signal.throwIfAborted();
|
|
9592
|
+
const dir2 = await mkdtemp2(join12(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
|
|
9593
|
+
const cleanup = async () => {
|
|
9594
|
+
try {
|
|
9595
|
+
await rm2(dir2, { recursive: true, force: true });
|
|
9596
|
+
} catch (err) {
|
|
9597
|
+
opts.warn(
|
|
9598
|
+
{ dir: dir2, err: message(err) },
|
|
9599
|
+
"codex images: could not remove the scratch directory"
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9602
|
+
};
|
|
9603
|
+
const prepareOne = async (url, index) => {
|
|
9604
|
+
try {
|
|
9605
|
+
const res = await doFetch(url, {
|
|
9606
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)])
|
|
9607
|
+
});
|
|
9608
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9609
|
+
const type = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
9610
|
+
const ext = EXTENSIONS[type];
|
|
9611
|
+
if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
|
|
9612
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
9613
|
+
const path = join12(dir2, `image-${index + 1}.${ext}`);
|
|
9614
|
+
await writeFile2(path, bytes, { mode: 384 });
|
|
9615
|
+
return path;
|
|
9616
|
+
} catch (err) {
|
|
9617
|
+
if (!signal.aborted) {
|
|
9618
|
+
opts.warn(
|
|
9619
|
+
{ image: index + 1, of: urls.length, err: message(err) },
|
|
9620
|
+
"codex images: an image could not be prepared; the turn runs without it"
|
|
9621
|
+
);
|
|
9622
|
+
}
|
|
9623
|
+
return null;
|
|
9624
|
+
}
|
|
9625
|
+
};
|
|
9626
|
+
const paths = await Promise.all(urls.map(prepareOne));
|
|
9627
|
+
if (signal.aborted) {
|
|
9628
|
+
await cleanup();
|
|
9629
|
+
signal.throwIfAborted();
|
|
9630
|
+
}
|
|
9631
|
+
return { paths: paths.filter((p) => p !== null), cleanup };
|
|
9632
|
+
};
|
|
9633
|
+
}
|
|
9634
|
+
function message(err) {
|
|
9635
|
+
return err instanceof Error ? err.message : String(err);
|
|
9636
|
+
}
|
|
9637
|
+
|
|
9538
9638
|
// src/turn-seq-floor.ts
|
|
9539
9639
|
var SeqFloorUnavailable = class extends Error {
|
|
9540
9640
|
constructor(detail) {
|
|
@@ -9572,15 +9672,15 @@ function readOutboxFloor(read, turnId, log) {
|
|
|
9572
9672
|
import { spawn as spawn5 } from "child_process";
|
|
9573
9673
|
import { hostname } from "os";
|
|
9574
9674
|
function prepareFailureCause(error) {
|
|
9575
|
-
const
|
|
9576
|
-
if (/timed out/i.test(
|
|
9577
|
-
if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(
|
|
9675
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
9676
|
+
if (/timed out/i.test(message2)) return "prepare hook timed out";
|
|
9677
|
+
if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(message2))
|
|
9578
9678
|
return "prepare hook module missing; check the configured command and its installation for this device";
|
|
9579
|
-
if (/not valid JSON|JSON must carry|produced no output/.test(
|
|
9679
|
+
if (/not valid JSON|JSON must carry|produced no output/.test(message2))
|
|
9580
9680
|
return "prepare hook returned malformed output; expected a cwd path or JSON with a non-empty cwd";
|
|
9581
|
-
if (/ENOENT/.test(
|
|
9582
|
-
if (/EACCES/.test(
|
|
9583
|
-
const exit =
|
|
9681
|
+
if (/ENOENT/.test(message2)) return "prepare hook executable missing (ENOENT)";
|
|
9682
|
+
if (/EACCES/.test(message2)) return "prepare hook executable is not accessible (EACCES)";
|
|
9683
|
+
const exit = message2.match(/exited with code (\d+)/)?.[1];
|
|
9584
9684
|
return exit ? `prepare hook exited with code ${exit}` : "prepare hook failed; inspect the device-local hook log";
|
|
9585
9685
|
}
|
|
9586
9686
|
async function runPrepareWithReporting(runner, hook, input, context, reportError) {
|
|
@@ -9632,15 +9732,15 @@ function reportFailure(config, body) {
|
|
|
9632
9732
|
|
|
9633
9733
|
// src/prepared.ts
|
|
9634
9734
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "fs";
|
|
9635
|
-
import { join as
|
|
9735
|
+
import { join as join13 } from "path";
|
|
9636
9736
|
function dirFor(workspaceId) {
|
|
9637
|
-
return
|
|
9737
|
+
return join13(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
9638
9738
|
}
|
|
9639
9739
|
function conversationDir(workspaceId, conversationId) {
|
|
9640
|
-
return
|
|
9740
|
+
return join13(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
9641
9741
|
}
|
|
9642
9742
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
9643
|
-
return
|
|
9743
|
+
return join13(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
9644
9744
|
}
|
|
9645
9745
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
9646
9746
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -9672,9 +9772,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
|
|
|
9672
9772
|
|
|
9673
9773
|
// src/transcript-writer.ts
|
|
9674
9774
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
9675
|
-
import { basename, dirname as dirname6, join as
|
|
9775
|
+
import { basename, dirname as dirname6, join as join14 } from "path";
|
|
9676
9776
|
function transcriptsDir() {
|
|
9677
|
-
return
|
|
9777
|
+
return join14(runnerStateDir(), "transcripts");
|
|
9678
9778
|
}
|
|
9679
9779
|
var RETAIN = 200;
|
|
9680
9780
|
var ANOMALY_RETAIN = 50;
|
|
@@ -9684,7 +9784,7 @@ var TranscriptWriter = class {
|
|
|
9684
9784
|
onWarn;
|
|
9685
9785
|
constructor(dir2, meta, onWarn) {
|
|
9686
9786
|
this.onWarn = onWarn;
|
|
9687
|
-
this.path =
|
|
9787
|
+
this.path = join14(dir2, fileName(meta));
|
|
9688
9788
|
try {
|
|
9689
9789
|
mkdirSync8(dir2, { recursive: true });
|
|
9690
9790
|
try {
|
|
@@ -9713,10 +9813,10 @@ var TranscriptWriter = class {
|
|
|
9713
9813
|
preserveAnomaly() {
|
|
9714
9814
|
if (this.broken) return;
|
|
9715
9815
|
try {
|
|
9716
|
-
const dir2 =
|
|
9816
|
+
const dir2 = join14(dirname6(this.path), "anomalies");
|
|
9717
9817
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
9718
9818
|
chmodSync4(dir2, 448);
|
|
9719
|
-
const target =
|
|
9819
|
+
const target = join14(dir2, basename(this.path));
|
|
9720
9820
|
copyFileSync(this.path, target);
|
|
9721
9821
|
chmodSync4(target, 384);
|
|
9722
9822
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9762,7 +9862,7 @@ function pruneOld(dir2, retain) {
|
|
|
9762
9862
|
const drop = files.sort().slice(0, files.length - retain);
|
|
9763
9863
|
for (const f of drop) {
|
|
9764
9864
|
try {
|
|
9765
|
-
rmSync6(
|
|
9865
|
+
rmSync6(join14(dir2, f), { force: true });
|
|
9766
9866
|
} catch {
|
|
9767
9867
|
}
|
|
9768
9868
|
}
|
|
@@ -10333,16 +10433,16 @@ var TurnExecution = class {
|
|
|
10333
10433
|
}
|
|
10334
10434
|
gateTrigger() {
|
|
10335
10435
|
const { payload, turnLog } = this;
|
|
10336
|
-
const
|
|
10337
|
-
const isDispatchableTrigger =
|
|
10436
|
+
const message2 = this.turnContext.message;
|
|
10437
|
+
const isDispatchableTrigger = message2.role === "user" || message2.role === "agent" || message2.role === "system";
|
|
10338
10438
|
if (!isDispatchableTrigger) {
|
|
10339
|
-
turnLog.debug({ role:
|
|
10439
|
+
turnLog.debug({ role: message2.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
|
|
10340
10440
|
throw this.concluded("unexpected_role", UNEXPECTED_ROLE_REASON);
|
|
10341
10441
|
}
|
|
10342
10442
|
this.supervisor.notifyStart({
|
|
10343
10443
|
id: this.dispatchId,
|
|
10344
10444
|
conversationId: payload.conversationId,
|
|
10345
|
-
message:
|
|
10445
|
+
message: message2.body
|
|
10346
10446
|
});
|
|
10347
10447
|
}
|
|
10348
10448
|
async resolveSecrets() {
|
|
@@ -10693,6 +10793,10 @@ var TurnExecution = class {
|
|
|
10693
10793
|
enabled: true,
|
|
10694
10794
|
onWarn,
|
|
10695
10795
|
writeInstructionsFile: writeCodexInstructionsFile,
|
|
10796
|
+
prepareImages: createCodexImagePreparer({
|
|
10797
|
+
turnId: this.turnId,
|
|
10798
|
+
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
10799
|
+
}),
|
|
10696
10800
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
10697
10801
|
})
|
|
10698
10802
|
);
|
|
@@ -11172,7 +11276,7 @@ function checkoutState(cwd) {
|
|
|
11172
11276
|
if (entries.length === 0) {
|
|
11173
11277
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
11174
11278
|
}
|
|
11175
|
-
const gitPath =
|
|
11279
|
+
const gitPath = join15(cwd, ".git");
|
|
11176
11280
|
if (!existsSync10(gitPath)) return { ok: true, reason: "usable" };
|
|
11177
11281
|
let stat;
|
|
11178
11282
|
try {
|
|
@@ -11180,7 +11284,7 @@ function checkoutState(cwd) {
|
|
|
11180
11284
|
} catch (error) {
|
|
11181
11285
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
11182
11286
|
}
|
|
11183
|
-
if (stat.isDirectory() && !existsSync10(
|
|
11287
|
+
if (stat.isDirectory() && !existsSync10(join15(gitPath, "HEAD")))
|
|
11184
11288
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
11185
11289
|
return { ok: true, reason: "usable" };
|
|
11186
11290
|
}
|
|
@@ -11373,7 +11477,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
11373
11477
|
|
|
11374
11478
|
// src/providers/docker.ts
|
|
11375
11479
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
11376
|
-
import { constants as constants2, tmpdir as
|
|
11480
|
+
import { constants as constants2, tmpdir as tmpdir3 } from "os";
|
|
11377
11481
|
import {
|
|
11378
11482
|
createReadStream,
|
|
11379
11483
|
createWriteStream as createWriteStream2,
|
|
@@ -11386,7 +11490,7 @@ import {
|
|
|
11386
11490
|
statSync as statSync3,
|
|
11387
11491
|
writeFileSync as writeFileSync6
|
|
11388
11492
|
} from "fs";
|
|
11389
|
-
import { join as
|
|
11493
|
+
import { join as join18 } from "path";
|
|
11390
11494
|
import { Writable } from "stream";
|
|
11391
11495
|
|
|
11392
11496
|
// src/providers/docker-cli.ts
|
|
@@ -11580,7 +11684,7 @@ import {
|
|
|
11580
11684
|
renameSync as renameSync4,
|
|
11581
11685
|
rmSync as rmSync8
|
|
11582
11686
|
} from "fs";
|
|
11583
|
-
import { join as
|
|
11687
|
+
import { join as join16 } from "path";
|
|
11584
11688
|
function routes(t) {
|
|
11585
11689
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
11586
11690
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -11626,7 +11730,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11626
11730
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
11627
11731
|
let entry = {};
|
|
11628
11732
|
try {
|
|
11629
|
-
entry = JSON.parse(readFileSync8(
|
|
11733
|
+
entry = JSON.parse(readFileSync8(join16(from, name), "utf8"));
|
|
11630
11734
|
} catch {
|
|
11631
11735
|
}
|
|
11632
11736
|
return { name, entry };
|
|
@@ -11639,7 +11743,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11639
11743
|
let published = 0;
|
|
11640
11744
|
for (const { name } of staged) {
|
|
11641
11745
|
try {
|
|
11642
|
-
move(
|
|
11746
|
+
move(join16(from, name), join16(to, name));
|
|
11643
11747
|
} catch {
|
|
11644
11748
|
return { published, complete: false };
|
|
11645
11749
|
}
|
|
@@ -11652,7 +11756,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11652
11756
|
// src/providers/runner-base.ts
|
|
11653
11757
|
import { createHash as createHash3 } from "crypto";
|
|
11654
11758
|
import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
|
|
11655
|
-
import { dirname as dirname7, join as
|
|
11759
|
+
import { dirname as dirname7, join as join17, sep } from "path";
|
|
11656
11760
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11657
11761
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
11658
11762
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -11700,11 +11804,11 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
11700
11804
|
function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
|
|
11701
11805
|
let dir2 = dirname7(moduleFile);
|
|
11702
11806
|
for (let i = 0; i < 4; i++) {
|
|
11703
|
-
if (existsSync12(
|
|
11807
|
+
if (existsSync12(join17(dir2, "dist", "cli.js")) && existsSync12(join17(dir2, "package.json"))) break;
|
|
11704
11808
|
dir2 = dirname7(dir2);
|
|
11705
11809
|
}
|
|
11706
|
-
if (!existsSync12(
|
|
11707
|
-
const sdk =
|
|
11810
|
+
if (!existsSync12(join17(dir2, "dist", "cli.js"))) return null;
|
|
11811
|
+
const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
11708
11812
|
if (!existsSync12(sdk)) return null;
|
|
11709
11813
|
const root = realpathSync2(dir2);
|
|
11710
11814
|
return realpathSync2(sdk).startsWith(root + sep) ? root : null;
|
|
@@ -11843,21 +11947,21 @@ function networkCreateError(stderr) {
|
|
|
11843
11947
|
}
|
|
11844
11948
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11845
11949
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11846
|
-
return
|
|
11950
|
+
return join18(deviceDir, "attempts", attemptId, "session.tar");
|
|
11847
11951
|
}
|
|
11848
11952
|
function pruneOtherAttempts(deviceDir, pair, keep) {
|
|
11849
|
-
const root =
|
|
11953
|
+
const root = join18(deviceDir, "attempts");
|
|
11850
11954
|
if (!existsSync13(root)) return;
|
|
11851
11955
|
for (const id of readdirSync4(root)) {
|
|
11852
11956
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
11853
11957
|
let pairOf = "";
|
|
11854
11958
|
try {
|
|
11855
|
-
pairOf = readFileSync9(
|
|
11959
|
+
pairOf = readFileSync9(join18(root, id, "pair"), "utf8");
|
|
11856
11960
|
} catch {
|
|
11857
11961
|
continue;
|
|
11858
11962
|
}
|
|
11859
11963
|
if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
|
|
11860
|
-
rmSync9(
|
|
11964
|
+
rmSync9(join18(root, id), { recursive: true, force: true });
|
|
11861
11965
|
}
|
|
11862
11966
|
}
|
|
11863
11967
|
}
|
|
@@ -12057,11 +12161,11 @@ function createDockerProvider(opts) {
|
|
|
12057
12161
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
12058
12162
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
12059
12163
|
};
|
|
12060
|
-
const socketRoot = opts.socketRoot ??
|
|
12061
|
-
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR :
|
|
12164
|
+
const socketRoot = opts.socketRoot ?? join18(
|
|
12165
|
+
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
12062
12166
|
`cabane-runners-${opts.deviceKey}`
|
|
12063
12167
|
);
|
|
12064
|
-
const socketDir = (name) =>
|
|
12168
|
+
const socketDir = (name) => join18(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
12065
12169
|
const create = async (name, attempt) => {
|
|
12066
12170
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
12067
12171
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -12203,7 +12307,7 @@ function createDockerProvider(opts) {
|
|
|
12203
12307
|
}
|
|
12204
12308
|
};
|
|
12205
12309
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
12206
|
-
const pendingPath = (name) =>
|
|
12310
|
+
const pendingPath = (name) => join18(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
12207
12311
|
const readPending = (name) => {
|
|
12208
12312
|
try {
|
|
12209
12313
|
const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
|
|
@@ -12217,12 +12321,12 @@ function createDockerProvider(opts) {
|
|
|
12217
12321
|
rmSync9(pendingPath(name), { force: true });
|
|
12218
12322
|
return;
|
|
12219
12323
|
}
|
|
12220
|
-
mkdirSync10(
|
|
12324
|
+
mkdirSync10(join18(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
12221
12325
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
12222
12326
|
};
|
|
12223
12327
|
const keepSession = async (name, attempt, attemptId) => {
|
|
12224
12328
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
12225
|
-
mkdirSync10(
|
|
12329
|
+
mkdirSync10(join18(archive, ".."), { recursive: true });
|
|
12226
12330
|
const partial = `${archive}.${process.pid}.partial`;
|
|
12227
12331
|
const file = new CappedFile(partial, sessionCap);
|
|
12228
12332
|
const r = await execIn(
|
|
@@ -12239,7 +12343,7 @@ function createDockerProvider(opts) {
|
|
|
12239
12343
|
);
|
|
12240
12344
|
if (r.code === 0 && !file.over) {
|
|
12241
12345
|
renameSync5(partial, archive);
|
|
12242
|
-
writeFileSync6(
|
|
12346
|
+
writeFileSync6(join18(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
12243
12347
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
12244
12348
|
await writeMarker(name, attemptId);
|
|
12245
12349
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -12253,7 +12357,7 @@ function createDockerProvider(opts) {
|
|
|
12253
12357
|
file.over ? "runners: the session files are over the size cap and weren't kept; the next new container starts the session over" : "runners: couldn't keep the session files; the next new container starts the session over"
|
|
12254
12358
|
);
|
|
12255
12359
|
};
|
|
12256
|
-
const stagingDir = (name) =>
|
|
12360
|
+
const stagingDir = (name) => join18(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
12257
12361
|
const ensureRunning = async (name) => {
|
|
12258
12362
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
12259
12363
|
if (seen.code !== 0) return false;
|
|
@@ -12261,7 +12365,7 @@ function createDockerProvider(opts) {
|
|
|
12261
12365
|
return (await docker.run(["start", name])).code === 0;
|
|
12262
12366
|
};
|
|
12263
12367
|
const dropArchive = (attemptId) => {
|
|
12264
|
-
rmSync9(
|
|
12368
|
+
rmSync9(join18(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
12265
12369
|
recursive: true,
|
|
12266
12370
|
force: true
|
|
12267
12371
|
});
|
|
@@ -12330,7 +12434,7 @@ function createDockerProvider(opts) {
|
|
|
12330
12434
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
12331
12435
|
await must(["start", name], "starting the container");
|
|
12332
12436
|
const git = await serveGitCredentials({
|
|
12333
|
-
socketPath:
|
|
12437
|
+
socketPath: join18(socketDir(name), "git.sock"),
|
|
12334
12438
|
hosts: opts.gitHosts,
|
|
12335
12439
|
fill: opts.gitFill,
|
|
12336
12440
|
log
|
|
@@ -12479,15 +12583,15 @@ function createDockerProvider(opts) {
|
|
|
12479
12583
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
12480
12584
|
if (transcripts) {
|
|
12481
12585
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
12482
|
-
if (kept.length) mkdirSync10(
|
|
12483
|
-
for (const f of kept) writeFileSync6(
|
|
12586
|
+
if (kept.length) mkdirSync10(join18(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
12587
|
+
for (const f of kept) writeFileSync6(join18(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
12484
12588
|
await removeIn(
|
|
12485
12589
|
name,
|
|
12486
12590
|
`${CONTAINER_STATE}/transcripts`,
|
|
12487
12591
|
kept.map((f) => f.name)
|
|
12488
12592
|
);
|
|
12489
12593
|
}
|
|
12490
|
-
const outboxRel =
|
|
12594
|
+
const outboxRel = join18("outbox", encodeURIComponent(attempt.agentId));
|
|
12491
12595
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
12492
12596
|
let unpublished = false;
|
|
12493
12597
|
if (outbox) {
|
|
@@ -12513,7 +12617,7 @@ function createDockerProvider(opts) {
|
|
|
12513
12617
|
const staged = stagingDir(name);
|
|
12514
12618
|
mkdirSync10(staged, { recursive: true });
|
|
12515
12619
|
writeFileSync6(
|
|
12516
|
-
|
|
12620
|
+
join18(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
12517
12621
|
`${JSON.stringify(entry)}
|
|
12518
12622
|
`
|
|
12519
12623
|
);
|
|
@@ -12526,7 +12630,7 @@ function createDockerProvider(opts) {
|
|
|
12526
12630
|
if (outbox.complete) {
|
|
12527
12631
|
const published = publishStaged(
|
|
12528
12632
|
stagingDir(name),
|
|
12529
|
-
|
|
12633
|
+
join18(opts.hostStateDir, outboxRel),
|
|
12530
12634
|
opts.moveFile
|
|
12531
12635
|
);
|
|
12532
12636
|
queued = published.published;
|
|
@@ -12741,8 +12845,8 @@ import {
|
|
|
12741
12845
|
} from "fs";
|
|
12742
12846
|
import { createRequire as createRequire2 } from "module";
|
|
12743
12847
|
import { isIP } from "net";
|
|
12744
|
-
import { homedir as homedir3, networkInterfaces, tmpdir as
|
|
12745
|
-
import { dirname as dirname8, join as
|
|
12848
|
+
import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
|
|
12849
|
+
import { dirname as dirname8, join as join19, sep as sep2 } from "path";
|
|
12746
12850
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12747
12851
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
12748
12852
|
function accountHome() {
|
|
@@ -12952,7 +13056,7 @@ import {
|
|
|
12952
13056
|
rmSync as rmSync11,
|
|
12953
13057
|
writeFileSync as writeFileSync8
|
|
12954
13058
|
} from "fs";
|
|
12955
|
-
import { join as
|
|
13059
|
+
import { join as join20 } from "path";
|
|
12956
13060
|
var MAX_ENTRIES = 2e3;
|
|
12957
13061
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12958
13062
|
var Outbox = class {
|
|
@@ -12965,10 +13069,10 @@ var Outbox = class {
|
|
|
12965
13069
|
// Resolved lazily (per call, like cursor.ts) so tests that swap HOME between
|
|
12966
13070
|
// cases route writes at the right tmpdir.
|
|
12967
13071
|
dir() {
|
|
12968
|
-
return
|
|
13072
|
+
return join20(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
12969
13073
|
}
|
|
12970
13074
|
fileFor(turnId, seq) {
|
|
12971
|
-
return
|
|
13075
|
+
return join20(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
12972
13076
|
}
|
|
12973
13077
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
12974
13078
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -13010,7 +13114,7 @@ var Outbox = class {
|
|
|
13010
13114
|
const entries = [];
|
|
13011
13115
|
for (const name of names) {
|
|
13012
13116
|
if (!name.endsWith(".json")) continue;
|
|
13013
|
-
const full =
|
|
13117
|
+
const full = join20(dir2, name);
|
|
13014
13118
|
try {
|
|
13015
13119
|
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
13016
13120
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -14651,8 +14755,8 @@ function errorCode(err) {
|
|
|
14651
14755
|
function isRecoverableSocketError(err) {
|
|
14652
14756
|
const code = errorCode(err);
|
|
14653
14757
|
if (code && RECOVERABLE_CODES.has(code)) return true;
|
|
14654
|
-
const
|
|
14655
|
-
return /\bEPIPE\b|\bECONNRESET\b/.test(
|
|
14758
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
14759
|
+
return /\bEPIPE\b|\bECONNRESET\b/.test(message2);
|
|
14656
14760
|
}
|
|
14657
14761
|
function installProcessSafetyNet(log, opts = {}) {
|
|
14658
14762
|
const proc = opts.proc ?? process;
|
|
@@ -14667,26 +14771,26 @@ function installProcessSafetyNet(log, opts = {}) {
|
|
|
14667
14771
|
);
|
|
14668
14772
|
}
|
|
14669
14773
|
function handleUncaught(log, err, origin) {
|
|
14670
|
-
const
|
|
14774
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
14671
14775
|
const code = errorCode(err);
|
|
14672
14776
|
if (isRecoverableSocketError(err)) {
|
|
14673
14777
|
log.debug(
|
|
14674
|
-
{ origin, code, err:
|
|
14778
|
+
{ origin, code, err: message2 },
|
|
14675
14779
|
"companion: recovered from a socket error (kept running \u2014 a broken pipe never kills the companion)"
|
|
14676
14780
|
);
|
|
14677
14781
|
return;
|
|
14678
14782
|
}
|
|
14679
14783
|
log.error(
|
|
14680
|
-
{ origin, code, err:
|
|
14784
|
+
{ origin, code, err: message2, stack: err instanceof Error ? err.stack : void 0 },
|
|
14681
14785
|
"An unexpected error occurred; the companion is still running."
|
|
14682
14786
|
);
|
|
14683
14787
|
}
|
|
14684
14788
|
|
|
14685
14789
|
// src/crash-marker.ts
|
|
14686
14790
|
import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync12, rmSync as rmSync12, writeFileSync as writeFileSync9 } from "fs";
|
|
14687
|
-
import { join as
|
|
14791
|
+
import { join as join21 } from "path";
|
|
14688
14792
|
function crashMarkerPath() {
|
|
14689
|
-
return
|
|
14793
|
+
return join21(cabaneDir(), "last-error.json");
|
|
14690
14794
|
}
|
|
14691
14795
|
function recordCrash(rec) {
|
|
14692
14796
|
try {
|