@cabane/companion 0.6.168 → 0.6.170
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 +355 -166
- package/dist/runtime.js +326 -152
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -3056,9 +3056,15 @@ var resolvedMcpServerSchema = z8.union([
|
|
|
3056
3056
|
var resolvedMcpServersSchema = z8.record(z8.string(), resolvedMcpServerSchema);
|
|
3057
3057
|
var hostInjectedServersSchema = z8.record(z8.string(), z8.unknown());
|
|
3058
3058
|
var turnRequestSchema = z8.object({
|
|
3059
|
-
// Server-composed
|
|
3060
|
-
// charter)
|
|
3059
|
+
// Server-composed standing instructions: the core segment (body, tool naming, identity, agent
|
|
3060
|
+
// charter) and, after it, the conversation segment (workspace and room charters, skill indexes,
|
|
3061
|
+
// roster). One string to the adapter, current as of this dispatch.
|
|
3061
3062
|
systemPrompt: z8.string(),
|
|
3063
|
+
// The same text as its two ordered segments, so an adapter that writes standing text into the
|
|
3064
|
+
// conversation (Codex's input preamble) can tell which half changed. `systemPrompt` is
|
|
3065
|
+
// `joinStanding(standing)`. Optional: absent from an API older than the split, where
|
|
3066
|
+
// `systemPrompt` is the core alone and the conversation material rides `prompt`.
|
|
3067
|
+
standing: z8.object({ core: z8.string(), conversation: z8.string() }).optional(),
|
|
3062
3068
|
// Server-composed per-turn user text (anchor reminder + the triggering message).
|
|
3063
3069
|
prompt: z8.string(),
|
|
3064
3070
|
// The multi-block user-message body (text + vision).
|
|
@@ -3106,6 +3112,16 @@ var turnRequestSchema = z8.object({
|
|
|
3106
3112
|
})
|
|
3107
3113
|
});
|
|
3108
3114
|
|
|
3115
|
+
// packages/agent-runtime/src/standing.ts
|
|
3116
|
+
var STANDING_SEPARATOR = "\n\n---\n\n";
|
|
3117
|
+
function joinStanding(segments) {
|
|
3118
|
+
const conversation = segments.conversation.trim();
|
|
3119
|
+
return conversation.length > 0 ? `${segments.core}${STANDING_SEPARATOR}${conversation}` : segments.core;
|
|
3120
|
+
}
|
|
3121
|
+
function standingSegments(req) {
|
|
3122
|
+
return req.standing ?? { core: req.systemPrompt, conversation: "" };
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3109
3125
|
// packages/agent-runtime/src/conformance.ts
|
|
3110
3126
|
import { z as z9 } from "zod";
|
|
3111
3127
|
var conformanceFixtureSchema = z9.object({
|
|
@@ -4988,7 +5004,6 @@ async function* decodeOpencodeStream(events, ctx) {
|
|
|
4988
5004
|
let reason;
|
|
4989
5005
|
let settled = false;
|
|
4990
5006
|
const userMessageIds = /* @__PURE__ */ new Set();
|
|
4991
|
-
if (ctx.skipMessageId) userMessageIds.add(ctx.skipMessageId);
|
|
4992
5007
|
for await (const ev of events) {
|
|
4993
5008
|
if (ctx.signal.aborted) return;
|
|
4994
5009
|
if (ev.type === "message.updated") {
|
|
@@ -5148,8 +5163,6 @@ function buildRunSpec(req, resumeSessionId) {
|
|
|
5148
5163
|
parts: buildParts(req),
|
|
5149
5164
|
tools,
|
|
5150
5165
|
allowAllHostTools,
|
|
5151
|
-
// Fresh session → mint + send a client id; resumed → let opencode mint it.
|
|
5152
|
-
sendClientMessageId: resumeSessionId == null,
|
|
5153
5166
|
...dialect.agent ? { agent: dialect.agent } : {},
|
|
5154
5167
|
mcp: buildMcp(req),
|
|
5155
5168
|
// Every server this turn installs with a credential resolved into it: a remote one's
|
|
@@ -5308,14 +5321,12 @@ function createHttpOpencodeTransport(opts) {
|
|
|
5308
5321
|
created = true;
|
|
5309
5322
|
}
|
|
5310
5323
|
await installMcp(doFetch, base, spec, signal);
|
|
5311
|
-
const userMessageId = spec.sendClientMessageId ? newOpencodeMessageId() : null;
|
|
5312
5324
|
const promptRes = await doFetch(
|
|
5313
5325
|
`${base}/session/${encodeURIComponent(sessionId)}/prompt_async`,
|
|
5314
5326
|
{
|
|
5315
5327
|
method: "POST",
|
|
5316
5328
|
headers: { "content-type": "application/json" },
|
|
5317
5329
|
body: JSON.stringify({
|
|
5318
|
-
...userMessageId ? { messageID: userMessageId } : {},
|
|
5319
5330
|
...spec.model ? { model: spec.model } : {},
|
|
5320
5331
|
system: spec.systemPrompt,
|
|
5321
5332
|
parts: spec.parts,
|
|
@@ -5341,7 +5352,6 @@ function createHttpOpencodeTransport(opts) {
|
|
|
5341
5352
|
sessionId,
|
|
5342
5353
|
created,
|
|
5343
5354
|
directory: spec.directory,
|
|
5344
|
-
userMessageId,
|
|
5345
5355
|
// The lock is released when this stream finishes draining.
|
|
5346
5356
|
events: releaseAfter(parseSseStream(eventRes.body, sessionId, signal), release)
|
|
5347
5357
|
};
|
|
@@ -5449,9 +5459,6 @@ function belongsToSession(ev, sessionId) {
|
|
|
5449
5459
|
if (typeof infoSid === "string") return infoSid === sessionId;
|
|
5450
5460
|
return true;
|
|
5451
5461
|
}
|
|
5452
|
-
function newOpencodeMessageId() {
|
|
5453
|
-
return `msg_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
5454
|
-
}
|
|
5455
5462
|
function trimSlash(s) {
|
|
5456
5463
|
return s.endsWith("/") ? s.slice(0, -1) : s;
|
|
5457
5464
|
}
|
|
@@ -5503,8 +5510,7 @@ function createOpencodeAdapter(deps = {}) {
|
|
|
5503
5510
|
}
|
|
5504
5511
|
yield* decodeOpencodeStream(result.events, {
|
|
5505
5512
|
signal,
|
|
5506
|
-
sessionId: result.sessionId
|
|
5507
|
-
skipMessageId: result.userMessageId
|
|
5513
|
+
sessionId: result.sessionId
|
|
5508
5514
|
});
|
|
5509
5515
|
}
|
|
5510
5516
|
};
|
|
@@ -5903,8 +5909,7 @@ var OPENCODE_CONFORMANCE_FIXTURES = [
|
|
|
5903
5909
|
]
|
|
5904
5910
|
},
|
|
5905
5911
|
{
|
|
5906
|
-
// On a
|
|
5907
|
-
// decoder gets no `skipMessageId`. It learns the user-message id from the stream — the
|
|
5912
|
+
// On resume, as on a fresh turn, the decoder learns the user-message id from the stream:
|
|
5908
5913
|
// `message.updated` user frame precedes the echoed part — and drops the echo, so the injected
|
|
5909
5914
|
// per-turn context can't leak into Steps. Only the reply surfaces; no session event.
|
|
5910
5915
|
name: "session resume \u2014 prompt echo filtered via stream-derived user id",
|
|
@@ -6125,7 +6130,14 @@ function fingerprintPrompt(prompt) {
|
|
|
6125
6130
|
return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
|
|
6126
6131
|
}
|
|
6127
6132
|
function encodeSession3(state) {
|
|
6128
|
-
return JSON.stringify(
|
|
6133
|
+
return JSON.stringify({
|
|
6134
|
+
threadId: state.threadId,
|
|
6135
|
+
cwd: state.cwd,
|
|
6136
|
+
promptFingerprint: state.promptFingerprint,
|
|
6137
|
+
conversationFingerprint: state.conversationFingerprint ?? null,
|
|
6138
|
+
compactions: state.compactions ?? null,
|
|
6139
|
+
usageBaseline: state.usageBaseline
|
|
6140
|
+
});
|
|
6129
6141
|
}
|
|
6130
6142
|
function decideResume3(stored, currentCwd) {
|
|
6131
6143
|
if (!stored) return { fresh: true, reason: "no_session" };
|
|
@@ -6146,6 +6158,8 @@ function decideResume3(stored, currentCwd) {
|
|
|
6146
6158
|
return {
|
|
6147
6159
|
resume: state.threadId,
|
|
6148
6160
|
promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
|
|
6161
|
+
conversationFingerprint: typeof state.conversationFingerprint === "string" ? state.conversationFingerprint : null,
|
|
6162
|
+
compactions: typeof state.compactions === "number" && Number.isInteger(state.compactions) && state.compactions >= 0 ? state.compactions : null,
|
|
6149
6163
|
usageBaseline: readUsageBaseline(state.usageBaseline)
|
|
6150
6164
|
};
|
|
6151
6165
|
}
|
|
@@ -6178,6 +6192,7 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6178
6192
|
let usage;
|
|
6179
6193
|
let cumulativeUsage;
|
|
6180
6194
|
let threadIdForSession = ctx.resumedThreadId;
|
|
6195
|
+
let lastState = ctx.storedState ?? null;
|
|
6181
6196
|
let settled = false;
|
|
6182
6197
|
function* flushInterim() {
|
|
6183
6198
|
const sealed = sealHeld2(held, false);
|
|
@@ -6193,14 +6208,14 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6193
6208
|
const state = encodeSession3({
|
|
6194
6209
|
threadId,
|
|
6195
6210
|
cwd: ctx.cwd ?? "",
|
|
6196
|
-
promptFingerprint: ctx.promptFingerprint ?? null,
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
// closing write records the new baseline.
|
|
6211
|
+
promptFingerprint: ctx.heldStanding?.promptFingerprint ?? null,
|
|
6212
|
+
conversationFingerprint: ctx.heldStanding?.conversationFingerprint ?? null,
|
|
6213
|
+
compactions: ctx.heldStanding?.compactions ?? null,
|
|
6200
6214
|
usageBaseline: ctx.usageBaseline ?? null
|
|
6201
6215
|
});
|
|
6202
6216
|
if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
|
|
6203
6217
|
sessionEmitted = true;
|
|
6218
|
+
lastState = state;
|
|
6204
6219
|
yield {
|
|
6205
6220
|
type: "session",
|
|
6206
6221
|
state,
|
|
@@ -6299,14 +6314,16 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6299
6314
|
if (sealed) yield sealed;
|
|
6300
6315
|
reason = "no_terminal";
|
|
6301
6316
|
}
|
|
6302
|
-
if (ok &&
|
|
6317
|
+
if (ok && threadIdForSession) {
|
|
6303
6318
|
const closing = encodeSession3({
|
|
6304
6319
|
threadId: threadIdForSession,
|
|
6305
6320
|
cwd: ctx.cwd ?? "",
|
|
6306
6321
|
promptFingerprint: ctx.promptFingerprint ?? null,
|
|
6307
|
-
|
|
6322
|
+
conversationFingerprint: ctx.conversationFingerprint ?? null,
|
|
6323
|
+
compactions: ctx.compactions ?? null,
|
|
6324
|
+
usageBaseline: cumulativeUsage ?? ctx.usageBaseline ?? null
|
|
6308
6325
|
});
|
|
6309
|
-
yield { type: "session", state: closing };
|
|
6326
|
+
if (closing !== lastState) yield { type: "session", state: closing };
|
|
6310
6327
|
}
|
|
6311
6328
|
const resolvedConfig = ctx.resolvedReasoningEffort ? { reasoningEffort: ctx.resolvedReasoningEffort } : void 0;
|
|
6312
6329
|
yield {
|
|
@@ -6376,15 +6393,27 @@ var ENV_ENVELOPE_KEYS = [
|
|
|
6376
6393
|
"CABANE_CONVERSATION_TITLE",
|
|
6377
6394
|
"CABANE_HOST_ACCESS"
|
|
6378
6395
|
];
|
|
6379
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null,
|
|
6396
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
|
|
6380
6397
|
const { policy, config } = req;
|
|
6381
6398
|
const directory = req.local.cwd ?? "";
|
|
6382
6399
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
6383
6400
|
const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
|
|
6384
6401
|
const model = config.model ? parseCodexModel(config.model) : null;
|
|
6385
|
-
const
|
|
6386
|
-
const
|
|
6387
|
-
const
|
|
6402
|
+
const segments = standingSegments(req);
|
|
6403
|
+
const conversationText = segments.conversation.trim();
|
|
6404
|
+
const fingerprints = {
|
|
6405
|
+
core: fingerprintPrompt(segments.core),
|
|
6406
|
+
conversation: conversationText.length > 0 ? fingerprintPrompt(conversationText) : null
|
|
6407
|
+
};
|
|
6408
|
+
const resumed = resumeThreadId !== null;
|
|
6409
|
+
const held = resumed ? threadStanding : null;
|
|
6410
|
+
const viaInput = baseInstructionsFile === null;
|
|
6411
|
+
const preamble = {
|
|
6412
|
+
core: viaInput && (held?.core ?? null) !== fingerprints.core,
|
|
6413
|
+
// Compared both ways: a held segment that is now empty differs from it, and nothing held
|
|
6414
|
+
// equals nothing to send.
|
|
6415
|
+
conversation: viaInput && (held?.conversation ?? null) !== fingerprints.conversation
|
|
6416
|
+
};
|
|
6388
6417
|
return {
|
|
6389
6418
|
resumeThreadId,
|
|
6390
6419
|
directory,
|
|
@@ -6393,9 +6422,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
6393
6422
|
skipGitRepoCheck: true,
|
|
6394
6423
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
6395
6424
|
baseInstructionsFile,
|
|
6396
|
-
input: withImages(buildInput(req,
|
|
6397
|
-
|
|
6398
|
-
|
|
6425
|
+
input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
|
|
6426
|
+
preamble,
|
|
6427
|
+
fingerprints,
|
|
6399
6428
|
config: buildConfig(req, baseInstructionsFile),
|
|
6400
6429
|
secretEnv: codexSecretEnv(req)
|
|
6401
6430
|
};
|
|
@@ -6411,12 +6440,26 @@ function codexSecretEnv(req) {
|
|
|
6411
6440
|
}
|
|
6412
6441
|
return Object.keys(env).length > 0 ? env : null;
|
|
6413
6442
|
}
|
|
6414
|
-
|
|
6443
|
+
var CORE_REPLACES = "Your standing instructions changed since an earlier copy in this conversation, or that copy may have been compacted away. This copy replaces every earlier one: where they differ, this one holds, and anything an earlier copy said that this one does not no longer applies.";
|
|
6444
|
+
var CONVERSATION_REPLACES = "This conversation's standing context \u2014 its charters, skill indexes and roster \u2014 changed since an earlier copy in this conversation, or that copy may have been compacted away. This copy replaces every earlier one, including charters and skills an earlier turn carried in its own text: where they differ, this one holds, and anything an earlier copy said that this one does not no longer applies.";
|
|
6445
|
+
function buildInput(req, segments, preamble, resumed) {
|
|
6415
6446
|
const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
|
|
6416
6447
|
const body = userText.trim().length > 0 ? userText : req.prompt;
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6448
|
+
const standing = [];
|
|
6449
|
+
if (!resumed) {
|
|
6450
|
+
if (preamble.core) standing.push(joinStanding(segments).trim());
|
|
6451
|
+
} else {
|
|
6452
|
+
if (preamble.core) standing.push(`${CORE_REPLACES}
|
|
6453
|
+
|
|
6454
|
+
${segments.core.trim()}`);
|
|
6455
|
+
if (preamble.conversation) {
|
|
6456
|
+
standing.push(`${CONVERSATION_REPLACES}
|
|
6457
|
+
|
|
6458
|
+
${segments.conversation.trim()}`.trim());
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
const lead = standing.filter((s) => s.length > 0);
|
|
6462
|
+
return lead.length > 0 ? `${lead.join("\n\n---\n\n")}
|
|
6420
6463
|
|
|
6421
6464
|
${body}` : body;
|
|
6422
6465
|
}
|
|
@@ -6628,7 +6671,6 @@ function createCodexAdapter(deps = {}) {
|
|
|
6628
6671
|
const directory = req.local.cwd ?? "";
|
|
6629
6672
|
const decision = decideResume3(req.session, directory);
|
|
6630
6673
|
const resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
6631
|
-
const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
|
|
6632
6674
|
const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
6633
6675
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
6634
6676
|
deps.onWarn?.(
|
|
@@ -6645,6 +6687,32 @@ function createCodexAdapter(deps = {}) {
|
|
|
6645
6687
|
degraded = false;
|
|
6646
6688
|
}
|
|
6647
6689
|
}
|
|
6690
|
+
let compactions = 0;
|
|
6691
|
+
let threadStanding = null;
|
|
6692
|
+
if ("resume" in decision) {
|
|
6693
|
+
compactions = deps.countCompactions ? await deps.countCompactions(decision.resume) : null;
|
|
6694
|
+
if (signal.aborted) return;
|
|
6695
|
+
if (compactions === null) {
|
|
6696
|
+
deps.onWarn?.(
|
|
6697
|
+
"codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions (ENG210)",
|
|
6698
|
+
{ threadId: decision.resume }
|
|
6699
|
+
);
|
|
6700
|
+
} else if (decision.compactions === compactions) {
|
|
6701
|
+
threadStanding = {
|
|
6702
|
+
core: decision.promptFingerprint,
|
|
6703
|
+
conversation: decision.conversationFingerprint
|
|
6704
|
+
};
|
|
6705
|
+
} else if (decision.compactions !== null && compactions > decision.compactions) {
|
|
6706
|
+
deps.onWarn?.(
|
|
6707
|
+
"codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions (ENG210)",
|
|
6708
|
+
{
|
|
6709
|
+
threadId: decision.resume,
|
|
6710
|
+
compactions,
|
|
6711
|
+
compactionsSince: compactions - decision.compactions
|
|
6712
|
+
}
|
|
6713
|
+
);
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6648
6716
|
let instructions = null;
|
|
6649
6717
|
if (!req.policy.hostFs) {
|
|
6650
6718
|
if (!deps.writeInstructionsFile) {
|
|
@@ -6689,7 +6757,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
6689
6757
|
req,
|
|
6690
6758
|
resumeThreadId,
|
|
6691
6759
|
instructions?.path ?? null,
|
|
6692
|
-
|
|
6760
|
+
threadStanding,
|
|
6693
6761
|
images?.paths ?? []
|
|
6694
6762
|
);
|
|
6695
6763
|
if (spec.model === null) {
|
|
@@ -6704,10 +6772,18 @@ function createCodexAdapter(deps = {}) {
|
|
|
6704
6772
|
cwd: req.local.cwd,
|
|
6705
6773
|
resumedThreadId: resumeThreadId,
|
|
6706
6774
|
degraded,
|
|
6707
|
-
// Which
|
|
6708
|
-
// later copy is what the model reads.
|
|
6709
|
-
//
|
|
6710
|
-
promptFingerprint: spec.
|
|
6775
|
+
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6776
|
+
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6777
|
+
// the spec, so the record can't disagree with what was sent.
|
|
6778
|
+
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6779
|
+
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6780
|
+
compactions,
|
|
6781
|
+
// What the thread held before this turn, for the write at `thread.started`.
|
|
6782
|
+
heldStanding: {
|
|
6783
|
+
promptFingerprint: threadStanding?.core ?? null,
|
|
6784
|
+
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6785
|
+
compactions: threadStanding ? compactions : null
|
|
6786
|
+
},
|
|
6711
6787
|
storedState: req.session,
|
|
6712
6788
|
usageBaseline: threadUsageBaseline,
|
|
6713
6789
|
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
@@ -6807,11 +6883,21 @@ var OP_FAILURE = {
|
|
|
6807
6883
|
message: "files.read returned more than the result ceiling"
|
|
6808
6884
|
}
|
|
6809
6885
|
};
|
|
6810
|
-
var
|
|
6886
|
+
var startedSession = (threadId, cwd = DIR2, degraded = false) => ({
|
|
6811
6887
|
type: "session",
|
|
6812
|
-
state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
|
|
6888
|
+
state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
|
|
6813
6889
|
...degraded ? { degraded: true } : {}
|
|
6814
6890
|
});
|
|
6891
|
+
var closingSession = (threadId, usageBaseline = null) => ({
|
|
6892
|
+
type: "session",
|
|
6893
|
+
state: encodeSession3({
|
|
6894
|
+
threadId,
|
|
6895
|
+
cwd: DIR2,
|
|
6896
|
+
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
6897
|
+
usageBaseline,
|
|
6898
|
+
compactions: 0
|
|
6899
|
+
})
|
|
6900
|
+
});
|
|
6815
6901
|
var CODEX_CONFORMANCE_FIXTURES = [
|
|
6816
6902
|
{
|
|
6817
6903
|
// A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
|
|
@@ -6834,11 +6920,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6834
6920
|
})
|
|
6835
6921
|
],
|
|
6836
6922
|
expected: [
|
|
6837
|
-
|
|
6923
|
+
startedSession(NEW_THREAD_ID),
|
|
6838
6924
|
{ type: "text", body: "Hello there.", terminal: true },
|
|
6839
6925
|
// The closing session write — the thread's ending totals, recorded as the baseline the next turn
|
|
6840
6926
|
// subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
|
|
6841
|
-
|
|
6927
|
+
closingSession(NEW_THREAD_ID, {
|
|
6842
6928
|
inputTokens: 100,
|
|
6843
6929
|
outputTokens: 50,
|
|
6844
6930
|
cacheReadTokens: 20,
|
|
@@ -6885,7 +6971,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6885
6971
|
turnCompleted()
|
|
6886
6972
|
],
|
|
6887
6973
|
expected: [
|
|
6888
|
-
|
|
6974
|
+
startedSession(NEW_THREAD_ID),
|
|
6889
6975
|
{ type: "text", body: "Reading your notes.", terminal: false },
|
|
6890
6976
|
{
|
|
6891
6977
|
type: "tool",
|
|
@@ -6905,6 +6991,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6905
6991
|
result: [{ type: "text", text: "sleep early" }]
|
|
6906
6992
|
},
|
|
6907
6993
|
{ type: "text", body: "Here's the summary.", terminal: true },
|
|
6994
|
+
closingSession(NEW_THREAD_ID),
|
|
6908
6995
|
{ type: "result", ok: true }
|
|
6909
6996
|
]
|
|
6910
6997
|
},
|
|
@@ -6938,7 +7025,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6938
7025
|
turnCompleted()
|
|
6939
7026
|
],
|
|
6940
7027
|
expected: [
|
|
6941
|
-
|
|
7028
|
+
startedSession(NEW_THREAD_ID),
|
|
6942
7029
|
{
|
|
6943
7030
|
type: "tool",
|
|
6944
7031
|
id: "query-success",
|
|
@@ -6956,6 +7043,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6956
7043
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6957
7044
|
result: OP_SUCCESS
|
|
6958
7045
|
},
|
|
7046
|
+
closingSession(NEW_THREAD_ID),
|
|
6959
7047
|
{ type: "result", ok: true }
|
|
6960
7048
|
]
|
|
6961
7049
|
},
|
|
@@ -6991,7 +7079,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6991
7079
|
turnCompleted()
|
|
6992
7080
|
],
|
|
6993
7081
|
expected: [
|
|
6994
|
-
|
|
7082
|
+
startedSession(NEW_THREAD_ID),
|
|
6995
7083
|
{
|
|
6996
7084
|
type: "tool",
|
|
6997
7085
|
id: "query-failed",
|
|
@@ -7009,6 +7097,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7009
7097
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
7010
7098
|
result: OP_FAILURE
|
|
7011
7099
|
},
|
|
7100
|
+
closingSession(NEW_THREAD_ID),
|
|
7012
7101
|
{ type: "result", ok: true }
|
|
7013
7102
|
]
|
|
7014
7103
|
},
|
|
@@ -7032,7 +7121,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7032
7121
|
turnCompleted()
|
|
7033
7122
|
],
|
|
7034
7123
|
expected: [
|
|
7035
|
-
|
|
7124
|
+
startedSession(NEW_THREAD_ID),
|
|
7036
7125
|
{
|
|
7037
7126
|
type: "tool",
|
|
7038
7127
|
id: "mcp-failed",
|
|
@@ -7050,6 +7139,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7050
7139
|
input: { q: "receipt" },
|
|
7051
7140
|
result: "connection closed"
|
|
7052
7141
|
},
|
|
7142
|
+
closingSession(NEW_THREAD_ID),
|
|
7053
7143
|
{ type: "result", ok: true }
|
|
7054
7144
|
]
|
|
7055
7145
|
},
|
|
@@ -7075,7 +7165,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7075
7165
|
turnCompleted()
|
|
7076
7166
|
],
|
|
7077
7167
|
expected: [
|
|
7078
|
-
|
|
7168
|
+
startedSession(NEW_THREAD_ID),
|
|
7079
7169
|
{
|
|
7080
7170
|
type: "tool",
|
|
7081
7171
|
id: "mcp-image",
|
|
@@ -7093,6 +7183,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7093
7183
|
input: {},
|
|
7094
7184
|
result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
|
|
7095
7185
|
},
|
|
7186
|
+
closingSession(NEW_THREAD_ID),
|
|
7096
7187
|
{ type: "result", ok: true }
|
|
7097
7188
|
]
|
|
7098
7189
|
},
|
|
@@ -7133,7 +7224,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7133
7224
|
turnCompleted()
|
|
7134
7225
|
],
|
|
7135
7226
|
expected: [
|
|
7136
|
-
|
|
7227
|
+
startedSession(NEW_THREAD_ID),
|
|
7137
7228
|
{ type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
|
|
7138
7229
|
{
|
|
7139
7230
|
type: "tool",
|
|
@@ -7144,6 +7235,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7144
7235
|
input: { path: "/product/tasks" },
|
|
7145
7236
|
result: [{ type: "text", text: "CT481.md" }]
|
|
7146
7237
|
},
|
|
7238
|
+
closingSession(NEW_THREAD_ID),
|
|
7147
7239
|
{ type: "result", ok: true }
|
|
7148
7240
|
]
|
|
7149
7241
|
},
|
|
@@ -7161,7 +7253,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7161
7253
|
turnCompleted()
|
|
7162
7254
|
],
|
|
7163
7255
|
expected: [
|
|
7164
|
-
|
|
7256
|
+
startedSession(NEW_THREAD_ID),
|
|
7165
7257
|
{
|
|
7166
7258
|
type: "tool",
|
|
7167
7259
|
id: "ws1",
|
|
@@ -7178,6 +7270,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7178
7270
|
summary: "best pizza in nyc",
|
|
7179
7271
|
input: { query: "best pizza in nyc" }
|
|
7180
7272
|
},
|
|
7273
|
+
closingSession(NEW_THREAD_ID),
|
|
7181
7274
|
{ type: "result", ok: true }
|
|
7182
7275
|
]
|
|
7183
7276
|
},
|
|
@@ -7193,7 +7286,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7193
7286
|
turnCompleted()
|
|
7194
7287
|
],
|
|
7195
7288
|
expected: [
|
|
7196
|
-
|
|
7289
|
+
startedSession(NEW_THREAD_ID),
|
|
7197
7290
|
{
|
|
7198
7291
|
type: "tool",
|
|
7199
7292
|
id: "ws2",
|
|
@@ -7210,6 +7303,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7210
7303
|
summary: "",
|
|
7211
7304
|
input: { query: "" }
|
|
7212
7305
|
},
|
|
7306
|
+
closingSession(NEW_THREAD_ID),
|
|
7213
7307
|
{ type: "result", ok: true }
|
|
7214
7308
|
]
|
|
7215
7309
|
},
|
|
@@ -7240,7 +7334,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7240
7334
|
turnCompleted()
|
|
7241
7335
|
],
|
|
7242
7336
|
expected: [
|
|
7243
|
-
|
|
7337
|
+
startedSession(NEW_THREAD_ID),
|
|
7244
7338
|
{ type: "text", body: "Working on it.", terminal: false },
|
|
7245
7339
|
{
|
|
7246
7340
|
type: "tool",
|
|
@@ -7272,7 +7366,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7272
7366
|
turnFailed("provider exploded")
|
|
7273
7367
|
],
|
|
7274
7368
|
expected: [
|
|
7275
|
-
|
|
7369
|
+
startedSession(NEW_THREAD_ID),
|
|
7276
7370
|
{ type: "text", body: "Partial work.", terminal: false },
|
|
7277
7371
|
{ type: "runtime_notice", body: "provider exploded" },
|
|
7278
7372
|
{ type: "result", ok: false, reason: "error:provider exploded" }
|
|
@@ -7289,7 +7383,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7289
7383
|
fatalError("stream died")
|
|
7290
7384
|
],
|
|
7291
7385
|
expected: [
|
|
7292
|
-
|
|
7386
|
+
startedSession(NEW_THREAD_ID),
|
|
7293
7387
|
{ type: "text", body: "Starting.", terminal: false },
|
|
7294
7388
|
{ type: "runtime_notice", body: "stream died" },
|
|
7295
7389
|
{ type: "result", ok: false, reason: "error:stream died" }
|
|
@@ -7302,7 +7396,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7302
7396
|
request: makeRequest3(),
|
|
7303
7397
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
|
|
7304
7398
|
expected: [
|
|
7305
|
-
|
|
7399
|
+
startedSession(NEW_THREAD_ID),
|
|
7306
7400
|
{ type: "runtime_notice", body: "429 Too Many Requests" },
|
|
7307
7401
|
{ type: "result", ok: false, reason: "rate_limited" }
|
|
7308
7402
|
]
|
|
@@ -7312,7 +7406,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7312
7406
|
request: makeRequest3(),
|
|
7313
7407
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
|
|
7314
7408
|
expected: [
|
|
7315
|
-
|
|
7409
|
+
startedSession(NEW_THREAD_ID),
|
|
7316
7410
|
{ type: "runtime_notice", body: "503 Service Unavailable" },
|
|
7317
7411
|
{ type: "result", ok: false, reason: "server_error" }
|
|
7318
7412
|
]
|
|
@@ -7325,7 +7419,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7325
7419
|
turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
|
|
7326
7420
|
],
|
|
7327
7421
|
expected: [
|
|
7328
|
-
|
|
7422
|
+
startedSession(NEW_THREAD_ID),
|
|
7329
7423
|
{
|
|
7330
7424
|
type: "runtime_notice",
|
|
7331
7425
|
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
@@ -7338,7 +7432,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7338
7432
|
request: makeRequest3(),
|
|
7339
7433
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
|
|
7340
7434
|
expected: [
|
|
7341
|
-
|
|
7435
|
+
startedSession(NEW_THREAD_ID),
|
|
7342
7436
|
{ type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
|
|
7343
7437
|
{ type: "result", ok: false, reason: "auth_expired" }
|
|
7344
7438
|
]
|
|
@@ -7350,7 +7444,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7350
7444
|
request: makeRequest3(),
|
|
7351
7445
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
|
|
7352
7446
|
expected: [
|
|
7353
|
-
|
|
7447
|
+
startedSession(NEW_THREAD_ID),
|
|
7354
7448
|
{ type: "runtime_notice", body: "something local broke" },
|
|
7355
7449
|
{ type: "result", ok: false, reason: "error:something local broke" }
|
|
7356
7450
|
]
|
|
@@ -7363,7 +7457,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7363
7457
|
turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
|
|
7364
7458
|
],
|
|
7365
7459
|
expected: [
|
|
7366
|
-
|
|
7460
|
+
startedSession(NEW_THREAD_ID),
|
|
7367
7461
|
{ type: "runtime_notice", body: BOUNDED_NOTICE2 },
|
|
7368
7462
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
7369
7463
|
]
|
|
@@ -7374,26 +7468,32 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7374
7468
|
name: "empty failure text emits no runtime notice",
|
|
7375
7469
|
request: makeRequest3(),
|
|
7376
7470
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
|
|
7377
|
-
expected: [
|
|
7471
|
+
expected: [
|
|
7472
|
+
startedSession(NEW_THREAD_ID),
|
|
7473
|
+
{ type: "result", ok: false, reason: "error:unknown" }
|
|
7474
|
+
]
|
|
7378
7475
|
},
|
|
7379
7476
|
{
|
|
7380
7477
|
name: "whitespace-only failure text emits no runtime notice",
|
|
7381
7478
|
request: makeRequest3(),
|
|
7382
7479
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
|
|
7383
|
-
expected: [
|
|
7480
|
+
expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
|
|
7384
7481
|
},
|
|
7385
7482
|
{
|
|
7386
7483
|
name: "bare status token emits no runtime notice",
|
|
7387
7484
|
request: makeRequest3(),
|
|
7388
7485
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
|
|
7389
|
-
expected: [
|
|
7486
|
+
expected: [
|
|
7487
|
+
startedSession(NEW_THREAD_ID),
|
|
7488
|
+
{ type: "result", ok: false, reason: "rate_limited" }
|
|
7489
|
+
]
|
|
7390
7490
|
},
|
|
7391
7491
|
{
|
|
7392
7492
|
name: "single-word provider sentence emits a runtime notice",
|
|
7393
7493
|
request: makeRequest3(),
|
|
7394
7494
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
|
|
7395
7495
|
expected: [
|
|
7396
|
-
|
|
7496
|
+
startedSession(NEW_THREAD_ID),
|
|
7397
7497
|
{ type: "runtime_notice", body: "Unavailable." },
|
|
7398
7498
|
{ type: "result", ok: false, reason: "error:Unavailable." }
|
|
7399
7499
|
]
|
|
@@ -7421,7 +7521,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7421
7521
|
turnCompleted()
|
|
7422
7522
|
],
|
|
7423
7523
|
expected: [
|
|
7424
|
-
|
|
7524
|
+
startedSession(NEW_THREAD_ID),
|
|
7425
7525
|
{
|
|
7426
7526
|
type: "result",
|
|
7427
7527
|
ok: false,
|
|
@@ -7441,8 +7541,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7441
7541
|
turnCompleted()
|
|
7442
7542
|
],
|
|
7443
7543
|
expected: [
|
|
7444
|
-
|
|
7544
|
+
startedSession(NEW_THREAD_ID),
|
|
7445
7545
|
{ type: "text", body: "All good now.", terminal: true },
|
|
7546
|
+
closingSession(NEW_THREAD_ID),
|
|
7446
7547
|
{ type: "result", ok: true }
|
|
7447
7548
|
]
|
|
7448
7549
|
},
|
|
@@ -7454,7 +7555,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7454
7555
|
name: "zero-content success (empty runtime result)",
|
|
7455
7556
|
request: makeRequest3(),
|
|
7456
7557
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
|
|
7457
|
-
expected: [
|
|
7558
|
+
expected: [
|
|
7559
|
+
startedSession(NEW_THREAD_ID),
|
|
7560
|
+
closingSession(NEW_THREAD_ID),
|
|
7561
|
+
{ type: "result", ok: true }
|
|
7562
|
+
]
|
|
7458
7563
|
},
|
|
7459
7564
|
{
|
|
7460
7565
|
// Empty-final: a clean turn that ended on a tool call with no closing message.
|
|
@@ -7479,7 +7584,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7479
7584
|
turnCompleted()
|
|
7480
7585
|
],
|
|
7481
7586
|
expected: [
|
|
7482
|
-
|
|
7587
|
+
startedSession(NEW_THREAD_ID),
|
|
7483
7588
|
{
|
|
7484
7589
|
type: "tool",
|
|
7485
7590
|
id: "t1",
|
|
@@ -7497,6 +7602,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7497
7602
|
input: { changes: [{ path: "a.md", kind: "add" }] },
|
|
7498
7603
|
result: [{ path: "a.md", kind: "add" }]
|
|
7499
7604
|
},
|
|
7605
|
+
closingSession(NEW_THREAD_ID),
|
|
7500
7606
|
{ type: "result", ok: true }
|
|
7501
7607
|
]
|
|
7502
7608
|
},
|
|
@@ -7512,9 +7618,10 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7512
7618
|
turnCompleted()
|
|
7513
7619
|
],
|
|
7514
7620
|
expected: [
|
|
7515
|
-
|
|
7621
|
+
startedSession(NEW_THREAD_ID),
|
|
7516
7622
|
{ type: "thinking", text: "Let me check the notes." },
|
|
7517
7623
|
{ type: "text", body: "All set.", terminal: true },
|
|
7624
|
+
closingSession(NEW_THREAD_ID),
|
|
7518
7625
|
{ type: "result", ok: true }
|
|
7519
7626
|
]
|
|
7520
7627
|
},
|
|
@@ -7532,8 +7639,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7532
7639
|
}),
|
|
7533
7640
|
nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
|
|
7534
7641
|
expected: [
|
|
7535
|
-
|
|
7642
|
+
startedSession(NEW_THREAD_ID),
|
|
7536
7643
|
{ type: "text", body: "Done.", terminal: true },
|
|
7644
|
+
closingSession(NEW_THREAD_ID),
|
|
7537
7645
|
{
|
|
7538
7646
|
type: "result",
|
|
7539
7647
|
ok: true,
|
|
@@ -7555,7 +7663,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7555
7663
|
threadId: "th1",
|
|
7556
7664
|
cwd: DIR2,
|
|
7557
7665
|
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
7558
|
-
usageBaseline: null
|
|
7666
|
+
usageBaseline: null,
|
|
7667
|
+
compactions: 0
|
|
7559
7668
|
})
|
|
7560
7669
|
}),
|
|
7561
7670
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
@@ -7577,8 +7686,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7577
7686
|
}),
|
|
7578
7687
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
7579
7688
|
expected: [
|
|
7580
|
-
|
|
7689
|
+
startedSession("th1"),
|
|
7581
7690
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7691
|
+
closingSession("th1"),
|
|
7582
7692
|
{ type: "result", ok: true }
|
|
7583
7693
|
]
|
|
7584
7694
|
},
|
|
@@ -7600,8 +7710,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7600
7710
|
}),
|
|
7601
7711
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
7602
7712
|
expected: [
|
|
7603
|
-
|
|
7713
|
+
startedSession("th1"),
|
|
7604
7714
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7715
|
+
closingSession("th1"),
|
|
7605
7716
|
{ type: "result", ok: true }
|
|
7606
7717
|
]
|
|
7607
7718
|
},
|
|
@@ -7627,8 +7738,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7627
7738
|
turnCompleted()
|
|
7628
7739
|
],
|
|
7629
7740
|
expected: [
|
|
7630
|
-
|
|
7741
|
+
startedSession(NEW_THREAD_ID, DIR2, true),
|
|
7631
7742
|
{ type: "text", body: "Fresh start.", terminal: true },
|
|
7743
|
+
closingSession(NEW_THREAD_ID),
|
|
7632
7744
|
{ type: "result", ok: true }
|
|
7633
7745
|
]
|
|
7634
7746
|
}
|
|
@@ -7710,7 +7822,7 @@ var ConnectorHealthStore = class {
|
|
|
7710
7822
|
// src/dispatcher.ts
|
|
7711
7823
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7712
7824
|
import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
7713
|
-
import { join as
|
|
7825
|
+
import { join as join19 } from "path";
|
|
7714
7826
|
|
|
7715
7827
|
// src/runner/launch.ts
|
|
7716
7828
|
import { createInterface } from "readline";
|
|
@@ -9108,6 +9220,7 @@ function buildCompanionTurnRequest(params) {
|
|
|
9108
9220
|
const { turnContext: t } = params;
|
|
9109
9221
|
return {
|
|
9110
9222
|
systemPrompt: t.systemPrompt,
|
|
9223
|
+
...t.standing ? { standing: t.standing } : {},
|
|
9111
9224
|
prompt: t.prompt,
|
|
9112
9225
|
content: t.content,
|
|
9113
9226
|
config: t.config,
|
|
@@ -9219,6 +9332,62 @@ function message(err) {
|
|
|
9219
9332
|
return err instanceof Error ? err.message : String(err);
|
|
9220
9333
|
}
|
|
9221
9334
|
|
|
9335
|
+
// src/codex-compactions.ts
|
|
9336
|
+
import { createReadStream } from "fs";
|
|
9337
|
+
import { readdir } from "fs/promises";
|
|
9338
|
+
import { homedir as homedir6 } from "os";
|
|
9339
|
+
import { join as join16 } from "path";
|
|
9340
|
+
import { createInterface as createInterface2 } from "readline";
|
|
9341
|
+
var DAY_MS = 864e5;
|
|
9342
|
+
var UUID_V7 = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9343
|
+
var RECORD_HEAD_CHARS = 200;
|
|
9344
|
+
function createCodexCompactionCounter(codexHome = process.env.CODEX_HOME || join16(homedir6(), ".codex")) {
|
|
9345
|
+
return async (threadId) => {
|
|
9346
|
+
try {
|
|
9347
|
+
const rollout = await findRollout(codexHome, threadId);
|
|
9348
|
+
return rollout ? await countCompactions(rollout) : null;
|
|
9349
|
+
} catch {
|
|
9350
|
+
return null;
|
|
9351
|
+
}
|
|
9352
|
+
};
|
|
9353
|
+
}
|
|
9354
|
+
async function findRollout(codexHome, threadId) {
|
|
9355
|
+
const id = UUID_V7.exec(threadId);
|
|
9356
|
+
if (!id) return null;
|
|
9357
|
+
const createdAt = parseInt(`${id[1]}${id[2]}`, 16);
|
|
9358
|
+
const suffix = `-${threadId}.jsonl`;
|
|
9359
|
+
for (const offset of [0, -DAY_MS, DAY_MS]) {
|
|
9360
|
+
const day = new Date(createdAt + offset);
|
|
9361
|
+
const dir2 = join16(
|
|
9362
|
+
codexHome,
|
|
9363
|
+
"sessions",
|
|
9364
|
+
String(day.getUTCFullYear()),
|
|
9365
|
+
String(day.getUTCMonth() + 1).padStart(2, "0"),
|
|
9366
|
+
String(day.getUTCDate()).padStart(2, "0")
|
|
9367
|
+
);
|
|
9368
|
+
let names;
|
|
9369
|
+
try {
|
|
9370
|
+
names = await readdir(dir2);
|
|
9371
|
+
} catch {
|
|
9372
|
+
continue;
|
|
9373
|
+
}
|
|
9374
|
+
const name = names.find((n) => n.startsWith("rollout-") && n.endsWith(suffix));
|
|
9375
|
+
if (name) return join16(dir2, name);
|
|
9376
|
+
}
|
|
9377
|
+
return null;
|
|
9378
|
+
}
|
|
9379
|
+
async function countCompactions(rollout) {
|
|
9380
|
+
const lines2 = createInterface2({
|
|
9381
|
+
input: createReadStream(rollout, { encoding: "utf8" }),
|
|
9382
|
+
crlfDelay: Infinity
|
|
9383
|
+
});
|
|
9384
|
+
let count = 0;
|
|
9385
|
+
for await (const line of lines2) {
|
|
9386
|
+
if (line.slice(0, RECORD_HEAD_CHARS).includes('"type":"compacted"')) count += 1;
|
|
9387
|
+
}
|
|
9388
|
+
return count;
|
|
9389
|
+
}
|
|
9390
|
+
|
|
9222
9391
|
// src/turn-seq-floor.ts
|
|
9223
9392
|
var SeqFloorUnavailable = class extends Error {
|
|
9224
9393
|
constructor(detail) {
|
|
@@ -9316,15 +9485,15 @@ function reportFailure(config, body) {
|
|
|
9316
9485
|
|
|
9317
9486
|
// src/prepared.ts
|
|
9318
9487
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "fs";
|
|
9319
|
-
import { join as
|
|
9488
|
+
import { join as join17 } from "path";
|
|
9320
9489
|
function dirFor(workspaceId) {
|
|
9321
|
-
return
|
|
9490
|
+
return join17(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
9322
9491
|
}
|
|
9323
9492
|
function conversationDir(workspaceId, conversationId) {
|
|
9324
|
-
return
|
|
9493
|
+
return join17(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
9325
9494
|
}
|
|
9326
9495
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
9327
|
-
return
|
|
9496
|
+
return join17(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
9328
9497
|
}
|
|
9329
9498
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
9330
9499
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -9356,9 +9525,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
|
|
|
9356
9525
|
|
|
9357
9526
|
// src/transcript-writer.ts
|
|
9358
9527
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
9359
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
9528
|
+
import { basename as basename3, dirname as dirname7, join as join18 } from "path";
|
|
9360
9529
|
function transcriptsDir() {
|
|
9361
|
-
return
|
|
9530
|
+
return join18(runnerStateDir(), "transcripts");
|
|
9362
9531
|
}
|
|
9363
9532
|
var RETAIN = 200;
|
|
9364
9533
|
var ANOMALY_RETAIN = 50;
|
|
@@ -9368,7 +9537,7 @@ var TranscriptWriter = class {
|
|
|
9368
9537
|
onWarn;
|
|
9369
9538
|
constructor(dir2, meta, onWarn) {
|
|
9370
9539
|
this.onWarn = onWarn;
|
|
9371
|
-
this.path =
|
|
9540
|
+
this.path = join18(dir2, fileName(meta));
|
|
9372
9541
|
try {
|
|
9373
9542
|
mkdirSync8(dir2, { recursive: true });
|
|
9374
9543
|
try {
|
|
@@ -9397,10 +9566,10 @@ var TranscriptWriter = class {
|
|
|
9397
9566
|
preserveAnomaly() {
|
|
9398
9567
|
if (this.broken) return;
|
|
9399
9568
|
try {
|
|
9400
|
-
const dir2 =
|
|
9569
|
+
const dir2 = join18(dirname7(this.path), "anomalies");
|
|
9401
9570
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
9402
9571
|
chmodSync4(dir2, 448);
|
|
9403
|
-
const target =
|
|
9572
|
+
const target = join18(dir2, basename3(this.path));
|
|
9404
9573
|
copyFileSync(this.path, target);
|
|
9405
9574
|
chmodSync4(target, 384);
|
|
9406
9575
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9446,7 +9615,7 @@ function pruneOld(dir2, retain) {
|
|
|
9446
9615
|
const drop = files.sort().slice(0, files.length - retain);
|
|
9447
9616
|
for (const f of drop) {
|
|
9448
9617
|
try {
|
|
9449
|
-
rmSync6(
|
|
9618
|
+
rmSync6(join18(dir2, f), { force: true });
|
|
9450
9619
|
} catch {
|
|
9451
9620
|
}
|
|
9452
9621
|
}
|
|
@@ -10355,6 +10524,8 @@ var TurnExecution = class {
|
|
|
10355
10524
|
turnId: this.turnId,
|
|
10356
10525
|
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
10357
10526
|
}),
|
|
10527
|
+
// Whether a resumed thread compacted since its standing preamble was sent.
|
|
10528
|
+
countCompactions: createCodexCompactionCounter(),
|
|
10358
10529
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
10359
10530
|
})
|
|
10360
10531
|
);
|
|
@@ -10415,6 +10586,9 @@ var TurnExecution = class {
|
|
|
10415
10586
|
);
|
|
10416
10587
|
return {
|
|
10417
10588
|
systemPrompt: context.systemPrompt,
|
|
10589
|
+
// Replaced even when absent, so the segments can't outlive the
|
|
10590
|
+
// `systemPrompt` they were split from.
|
|
10591
|
+
standing: context.standing,
|
|
10418
10592
|
prompt: context.prompt,
|
|
10419
10593
|
content: context.content
|
|
10420
10594
|
};
|
|
@@ -10826,7 +11000,7 @@ function checkoutState(cwd) {
|
|
|
10826
11000
|
if (entries.length === 0) {
|
|
10827
11001
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10828
11002
|
}
|
|
10829
|
-
const gitPath =
|
|
11003
|
+
const gitPath = join19(cwd, ".git");
|
|
10830
11004
|
if (!existsSync12(gitPath)) return { ok: true, reason: "usable" };
|
|
10831
11005
|
let stat;
|
|
10832
11006
|
try {
|
|
@@ -10834,7 +11008,7 @@ function checkoutState(cwd) {
|
|
|
10834
11008
|
} catch (error) {
|
|
10835
11009
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10836
11010
|
}
|
|
10837
|
-
if (stat.isDirectory() && !existsSync12(
|
|
11011
|
+
if (stat.isDirectory() && !existsSync12(join19(gitPath, "HEAD")))
|
|
10838
11012
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10839
11013
|
return { ok: true, reason: "usable" };
|
|
10840
11014
|
}
|
|
@@ -11041,7 +11215,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
11041
11215
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
11042
11216
|
import { constants as constants3, tmpdir as tmpdir3 } from "os";
|
|
11043
11217
|
import {
|
|
11044
|
-
createReadStream,
|
|
11218
|
+
createReadStream as createReadStream2,
|
|
11045
11219
|
createWriteStream as createWriteStream2,
|
|
11046
11220
|
existsSync as existsSync15,
|
|
11047
11221
|
mkdirSync as mkdirSync10,
|
|
@@ -11052,7 +11226,7 @@ import {
|
|
|
11052
11226
|
statSync as statSync4,
|
|
11053
11227
|
writeFileSync as writeFileSync6
|
|
11054
11228
|
} from "fs";
|
|
11055
|
-
import { join as
|
|
11229
|
+
import { join as join22 } from "path";
|
|
11056
11230
|
import { Writable } from "stream";
|
|
11057
11231
|
|
|
11058
11232
|
// src/providers/docker-cli.ts
|
|
@@ -11246,7 +11420,7 @@ import {
|
|
|
11246
11420
|
renameSync as renameSync5,
|
|
11247
11421
|
rmSync as rmSync8
|
|
11248
11422
|
} from "fs";
|
|
11249
|
-
import { join as
|
|
11423
|
+
import { join as join20 } from "path";
|
|
11250
11424
|
function routes(t) {
|
|
11251
11425
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
11252
11426
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -11292,7 +11466,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11292
11466
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
11293
11467
|
let entry = {};
|
|
11294
11468
|
try {
|
|
11295
|
-
entry = JSON.parse(readFileSync9(
|
|
11469
|
+
entry = JSON.parse(readFileSync9(join20(from, name), "utf8"));
|
|
11296
11470
|
} catch {
|
|
11297
11471
|
}
|
|
11298
11472
|
return { name, entry };
|
|
@@ -11305,7 +11479,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11305
11479
|
let published = 0;
|
|
11306
11480
|
for (const { name } of staged) {
|
|
11307
11481
|
try {
|
|
11308
|
-
move(
|
|
11482
|
+
move(join20(from, name), join20(to, name));
|
|
11309
11483
|
} catch {
|
|
11310
11484
|
return { published, complete: false };
|
|
11311
11485
|
}
|
|
@@ -11318,7 +11492,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11318
11492
|
// src/providers/runner-base.ts
|
|
11319
11493
|
import { createHash as createHash3 } from "crypto";
|
|
11320
11494
|
import { existsSync as existsSync14, realpathSync as realpathSync4 } from "fs";
|
|
11321
|
-
import { dirname as dirname8, join as
|
|
11495
|
+
import { dirname as dirname8, join as join21, sep as sep3 } from "path";
|
|
11322
11496
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11323
11497
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
11324
11498
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -11366,11 +11540,11 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
11366
11540
|
function ownInstall(moduleFile = fileURLToPath5(import.meta.url)) {
|
|
11367
11541
|
let dir2 = dirname8(moduleFile);
|
|
11368
11542
|
for (let i = 0; i < 4; i++) {
|
|
11369
|
-
if (existsSync14(
|
|
11543
|
+
if (existsSync14(join21(dir2, "dist", "cli.js")) && existsSync14(join21(dir2, "package.json"))) break;
|
|
11370
11544
|
dir2 = dirname8(dir2);
|
|
11371
11545
|
}
|
|
11372
|
-
if (!existsSync14(
|
|
11373
|
-
const sdk =
|
|
11546
|
+
if (!existsSync14(join21(dir2, "dist", "cli.js"))) return null;
|
|
11547
|
+
const sdk = join21(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
11374
11548
|
if (!existsSync14(sdk)) return null;
|
|
11375
11549
|
const root = realpathSync4(dir2);
|
|
11376
11550
|
return realpathSync4(sdk).startsWith(root + sep3) ? root : null;
|
|
@@ -11509,39 +11683,39 @@ function networkCreateError(stderr) {
|
|
|
11509
11683
|
}
|
|
11510
11684
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11511
11685
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11512
|
-
return
|
|
11686
|
+
return join22(deviceDir, "attempts", attemptId, "session.tar");
|
|
11513
11687
|
}
|
|
11514
11688
|
function pruneOtherAttempts(deviceDir, pair, keep) {
|
|
11515
|
-
const root =
|
|
11689
|
+
const root = join22(deviceDir, "attempts");
|
|
11516
11690
|
if (!existsSync15(root)) return;
|
|
11517
11691
|
for (const id of readdirSync4(root)) {
|
|
11518
11692
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
11519
11693
|
let pairOf = "";
|
|
11520
11694
|
try {
|
|
11521
|
-
pairOf = readFileSync10(
|
|
11695
|
+
pairOf = readFileSync10(join22(root, id, "pair"), "utf8");
|
|
11522
11696
|
} catch {
|
|
11523
11697
|
continue;
|
|
11524
11698
|
}
|
|
11525
11699
|
if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
|
|
11526
|
-
rmSync9(
|
|
11700
|
+
rmSync9(join22(root, id), { recursive: true, force: true });
|
|
11527
11701
|
}
|
|
11528
11702
|
}
|
|
11529
11703
|
}
|
|
11530
11704
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
11531
11705
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
11532
|
-
const root =
|
|
11706
|
+
const root = join22(deviceDir, "attempts");
|
|
11533
11707
|
if (!existsSync15(root)) return [];
|
|
11534
11708
|
const removed = [];
|
|
11535
11709
|
for (const id of readdirSync4(root)) {
|
|
11536
11710
|
if (!UUID_RE.test(id)) continue;
|
|
11537
11711
|
let kept;
|
|
11538
11712
|
try {
|
|
11539
|
-
kept = statSync4(
|
|
11713
|
+
kept = statSync4(join22(root, id)).mtimeMs;
|
|
11540
11714
|
} catch {
|
|
11541
11715
|
continue;
|
|
11542
11716
|
}
|
|
11543
11717
|
if (now - kept < maxAgeMs) continue;
|
|
11544
|
-
rmSync9(
|
|
11718
|
+
rmSync9(join22(root, id), { recursive: true, force: true });
|
|
11545
11719
|
removed.push(id);
|
|
11546
11720
|
}
|
|
11547
11721
|
return removed;
|
|
@@ -11742,11 +11916,11 @@ function createDockerProvider(opts) {
|
|
|
11742
11916
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
11743
11917
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
11744
11918
|
};
|
|
11745
|
-
const socketRoot = opts.socketRoot ??
|
|
11919
|
+
const socketRoot = opts.socketRoot ?? join22(
|
|
11746
11920
|
process.env.XDG_RUNTIME_DIR && existsSync15(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
11747
11921
|
`cabane-runners-${opts.deviceKey}`
|
|
11748
11922
|
);
|
|
11749
|
-
const socketDir = (name) =>
|
|
11923
|
+
const socketDir = (name) => join22(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
11750
11924
|
const create = async (name, attempt) => {
|
|
11751
11925
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
11752
11926
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -11888,7 +12062,7 @@ function createDockerProvider(opts) {
|
|
|
11888
12062
|
}
|
|
11889
12063
|
};
|
|
11890
12064
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
11891
|
-
const pendingPath = (name) =>
|
|
12065
|
+
const pendingPath = (name) => join22(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
11892
12066
|
const readPending = (name) => {
|
|
11893
12067
|
try {
|
|
11894
12068
|
const ids = JSON.parse(readFileSync10(pendingPath(name), "utf8"));
|
|
@@ -11902,12 +12076,12 @@ function createDockerProvider(opts) {
|
|
|
11902
12076
|
rmSync9(pendingPath(name), { force: true });
|
|
11903
12077
|
return;
|
|
11904
12078
|
}
|
|
11905
|
-
mkdirSync10(
|
|
12079
|
+
mkdirSync10(join22(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
11906
12080
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
11907
12081
|
};
|
|
11908
12082
|
const keepSession = async (name, attempt, attemptId) => {
|
|
11909
12083
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
11910
|
-
mkdirSync10(
|
|
12084
|
+
mkdirSync10(join22(archive, ".."), { recursive: true });
|
|
11911
12085
|
const partial = `${archive}.${process.pid}.partial`;
|
|
11912
12086
|
const file = new CappedFile(partial, sessionCap);
|
|
11913
12087
|
const r = await execIn(
|
|
@@ -11924,7 +12098,7 @@ function createDockerProvider(opts) {
|
|
|
11924
12098
|
);
|
|
11925
12099
|
if (r.code === 0 && !file.over) {
|
|
11926
12100
|
renameSync6(partial, archive);
|
|
11927
|
-
writeFileSync6(
|
|
12101
|
+
writeFileSync6(join22(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
11928
12102
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
11929
12103
|
await writeMarker(name, attemptId);
|
|
11930
12104
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -11938,7 +12112,7 @@ function createDockerProvider(opts) {
|
|
|
11938
12112
|
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"
|
|
11939
12113
|
);
|
|
11940
12114
|
};
|
|
11941
|
-
const stagingDir = (name) =>
|
|
12115
|
+
const stagingDir = (name) => join22(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
11942
12116
|
const ensureRunning = async (name) => {
|
|
11943
12117
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
11944
12118
|
if (seen.code !== 0) return false;
|
|
@@ -11946,7 +12120,7 @@ function createDockerProvider(opts) {
|
|
|
11946
12120
|
return (await docker.run(["start", name])).code === 0;
|
|
11947
12121
|
};
|
|
11948
12122
|
const dropArchive = (attemptId) => {
|
|
11949
|
-
rmSync9(
|
|
12123
|
+
rmSync9(join22(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
11950
12124
|
recursive: true,
|
|
11951
12125
|
force: true
|
|
11952
12126
|
});
|
|
@@ -12015,7 +12189,7 @@ function createDockerProvider(opts) {
|
|
|
12015
12189
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
12016
12190
|
await must(["start", name], "starting the container");
|
|
12017
12191
|
const git = await serveGitCredentials({
|
|
12018
|
-
socketPath:
|
|
12192
|
+
socketPath: join22(socketDir(name), "git.sock"),
|
|
12019
12193
|
hosts: opts.gitHosts,
|
|
12020
12194
|
fill: opts.gitFill,
|
|
12021
12195
|
log
|
|
@@ -12051,7 +12225,7 @@ function createDockerProvider(opts) {
|
|
|
12051
12225
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
12052
12226
|
if (existsSync15(archive)) {
|
|
12053
12227
|
const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
|
|
12054
|
-
input:
|
|
12228
|
+
input: createReadStream2(archive)
|
|
12055
12229
|
});
|
|
12056
12230
|
if (r.code !== 0) {
|
|
12057
12231
|
log.warn(
|
|
@@ -12164,15 +12338,15 @@ function createDockerProvider(opts) {
|
|
|
12164
12338
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
12165
12339
|
if (transcripts) {
|
|
12166
12340
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
12167
|
-
if (kept.length) mkdirSync10(
|
|
12168
|
-
for (const f of kept) writeFileSync6(
|
|
12341
|
+
if (kept.length) mkdirSync10(join22(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
12342
|
+
for (const f of kept) writeFileSync6(join22(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
12169
12343
|
await removeIn(
|
|
12170
12344
|
name,
|
|
12171
12345
|
`${CONTAINER_STATE}/transcripts`,
|
|
12172
12346
|
kept.map((f) => f.name)
|
|
12173
12347
|
);
|
|
12174
12348
|
}
|
|
12175
|
-
const outboxRel =
|
|
12349
|
+
const outboxRel = join22("outbox", encodeURIComponent(attempt.agentId));
|
|
12176
12350
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
12177
12351
|
let unpublished = false;
|
|
12178
12352
|
if (outbox) {
|
|
@@ -12198,7 +12372,7 @@ function createDockerProvider(opts) {
|
|
|
12198
12372
|
const staged = stagingDir(name);
|
|
12199
12373
|
mkdirSync10(staged, { recursive: true });
|
|
12200
12374
|
writeFileSync6(
|
|
12201
|
-
|
|
12375
|
+
join22(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
12202
12376
|
`${JSON.stringify(entry)}
|
|
12203
12377
|
`
|
|
12204
12378
|
);
|
|
@@ -12211,7 +12385,7 @@ function createDockerProvider(opts) {
|
|
|
12211
12385
|
if (outbox.complete) {
|
|
12212
12386
|
const published = publishStaged(
|
|
12213
12387
|
stagingDir(name),
|
|
12214
|
-
|
|
12388
|
+
join22(opts.hostStateDir, outboxRel),
|
|
12215
12389
|
opts.moveFile
|
|
12216
12390
|
);
|
|
12217
12391
|
queued = published.published;
|
|
@@ -12426,8 +12600,8 @@ import {
|
|
|
12426
12600
|
} from "fs";
|
|
12427
12601
|
import { createRequire as createRequire2 } from "module";
|
|
12428
12602
|
import { isIP } from "net";
|
|
12429
|
-
import { homedir as
|
|
12430
|
-
import { dirname as dirname9, join as
|
|
12603
|
+
import { homedir as homedir7, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
|
|
12604
|
+
import { dirname as dirname9, join as join23, sep as sep4 } from "path";
|
|
12431
12605
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
12432
12606
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
12433
12607
|
function accountHome() {
|
|
@@ -12524,7 +12698,7 @@ function companionStoreWatch(log) {
|
|
|
12524
12698
|
return new CompanionStoreWatch({
|
|
12525
12699
|
log,
|
|
12526
12700
|
roots: () => {
|
|
12527
|
-
const home =
|
|
12701
|
+
const home = homedir7();
|
|
12528
12702
|
return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
|
|
12529
12703
|
}
|
|
12530
12704
|
});
|
|
@@ -12623,8 +12797,8 @@ function emptyHarnessVersions() {
|
|
|
12623
12797
|
}
|
|
12624
12798
|
|
|
12625
12799
|
// src/harness-copy.ts
|
|
12626
|
-
import { homedir as
|
|
12627
|
-
import { join as
|
|
12800
|
+
import { homedir as homedir8 } from "os";
|
|
12801
|
+
import { join as join24 } from "path";
|
|
12628
12802
|
var HARNESS_LABELS = {
|
|
12629
12803
|
"claude-code": "Claude Code",
|
|
12630
12804
|
codex: "Codex",
|
|
@@ -12638,7 +12812,7 @@ function failureText(runtime, reason) {
|
|
|
12638
12812
|
case "spawn":
|
|
12639
12813
|
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
12640
12814
|
case "config":
|
|
12641
|
-
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(
|
|
12815
|
+
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(join24(homedir8(), ".codex", "config.toml"))})` : ""}. Fix it, then try again.`;
|
|
12642
12816
|
case "unreadable":
|
|
12643
12817
|
return `Couldn't check whether ${label} is signed in: it answered in a way Cabane couldn't read. Try again, and if it keeps happening, update Cabane.`;
|
|
12644
12818
|
}
|
|
@@ -12978,7 +13152,7 @@ import {
|
|
|
12978
13152
|
rmSync as rmSync11,
|
|
12979
13153
|
writeFileSync as writeFileSync8
|
|
12980
13154
|
} from "fs";
|
|
12981
|
-
import { join as
|
|
13155
|
+
import { join as join25 } from "path";
|
|
12982
13156
|
var MAX_ENTRIES = 2e3;
|
|
12983
13157
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12984
13158
|
var Outbox = class {
|
|
@@ -12991,10 +13165,10 @@ var Outbox = class {
|
|
|
12991
13165
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
12992
13166
|
// cases route writes at the right tmpdir.
|
|
12993
13167
|
dir() {
|
|
12994
|
-
return
|
|
13168
|
+
return join25(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
12995
13169
|
}
|
|
12996
13170
|
fileFor(turnId, seq) {
|
|
12997
|
-
return
|
|
13171
|
+
return join25(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
12998
13172
|
}
|
|
12999
13173
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
13000
13174
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -13036,7 +13210,7 @@ var Outbox = class {
|
|
|
13036
13210
|
const entries = [];
|
|
13037
13211
|
for (const name of names) {
|
|
13038
13212
|
if (!name.endsWith(".json")) continue;
|
|
13039
|
-
const full =
|
|
13213
|
+
const full = join25(dir2, name);
|
|
13040
13214
|
try {
|
|
13041
13215
|
const parsed = JSON.parse(readFileSync12(full, "utf8"));
|
|
13042
13216
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -13260,7 +13434,7 @@ var SseSubscriber = class {
|
|
|
13260
13434
|
// src/updater.ts
|
|
13261
13435
|
import { spawn as spawn8 } from "child_process";
|
|
13262
13436
|
import { copyFileSync as copyFileSync3, existsSync as existsSync18, mkdirSync as mkdirSync16, renameSync as renameSync11, rmSync as rmSync14 } from "fs";
|
|
13263
|
-
import { dirname as dirname13, join as
|
|
13437
|
+
import { dirname as dirname13, join as join29 } from "path";
|
|
13264
13438
|
|
|
13265
13439
|
// src/boot-rule.ts
|
|
13266
13440
|
import {
|
|
@@ -13272,7 +13446,7 @@ import {
|
|
|
13272
13446
|
symlinkSync,
|
|
13273
13447
|
writeFileSync as writeFileSync9
|
|
13274
13448
|
} from "fs";
|
|
13275
|
-
import { dirname as dirname10, join as
|
|
13449
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
13276
13450
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
13277
13451
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
13278
13452
|
function superseded(pending, ...running) {
|
|
@@ -13305,26 +13479,26 @@ import {
|
|
|
13305
13479
|
rmSync as rmSync13,
|
|
13306
13480
|
symlinkSync as symlinkSync2
|
|
13307
13481
|
} from "fs";
|
|
13308
|
-
import { dirname as dirname11, join as
|
|
13482
|
+
import { dirname as dirname11, join as join27 } from "path";
|
|
13309
13483
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
13310
13484
|
function versionsDir() {
|
|
13311
|
-
return
|
|
13485
|
+
return join27(cabaneDir(), "versions");
|
|
13312
13486
|
}
|
|
13313
13487
|
function currentLink() {
|
|
13314
|
-
return
|
|
13488
|
+
return join27(cabaneDir(), "current");
|
|
13315
13489
|
}
|
|
13316
13490
|
function bootGuardPath() {
|
|
13317
|
-
return
|
|
13491
|
+
return join27(cabaneDir(), "boot-guard.mjs");
|
|
13318
13492
|
}
|
|
13319
13493
|
function versionPackageRoot(version) {
|
|
13320
|
-
return
|
|
13494
|
+
return join27(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
13321
13495
|
}
|
|
13322
13496
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
13323
13497
|
return dirname11(dirname11(fileURLToPath7(moduleUrl)));
|
|
13324
13498
|
}
|
|
13325
13499
|
function packageVersion(root) {
|
|
13326
13500
|
try {
|
|
13327
|
-
const pkg2 = JSON.parse(readFileSync14(
|
|
13501
|
+
const pkg2 = JSON.parse(readFileSync14(join27(root, "package.json"), "utf8"));
|
|
13328
13502
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
13329
13503
|
} catch {
|
|
13330
13504
|
return null;
|
|
@@ -13333,7 +13507,7 @@ function packageVersion(root) {
|
|
|
13333
13507
|
function currentRoot() {
|
|
13334
13508
|
try {
|
|
13335
13509
|
const root = realpathSync7(currentLink());
|
|
13336
|
-
return existsSync17(
|
|
13510
|
+
return existsSync17(join27(root, "dist", "cli.js")) ? root : null;
|
|
13337
13511
|
} catch {
|
|
13338
13512
|
return null;
|
|
13339
13513
|
}
|
|
@@ -13356,7 +13530,7 @@ function pruneVersions(keep) {
|
|
|
13356
13530
|
const removed = [];
|
|
13357
13531
|
for (const name of readdirSync6(dir2)) {
|
|
13358
13532
|
if (keep.includes(name)) continue;
|
|
13359
|
-
rmSync13(
|
|
13533
|
+
rmSync13(join27(dir2, name), { recursive: true, force: true });
|
|
13360
13534
|
removed.push(name);
|
|
13361
13535
|
}
|
|
13362
13536
|
return removed;
|
|
@@ -13375,7 +13549,7 @@ function compareVersions(a, b) {
|
|
|
13375
13549
|
|
|
13376
13550
|
// src/update-state.ts
|
|
13377
13551
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync15, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
13378
|
-
import { dirname as dirname12, join as
|
|
13552
|
+
import { dirname as dirname12, join as join28 } from "path";
|
|
13379
13553
|
import { z as z18 } from "zod";
|
|
13380
13554
|
var resultSchema = z18.object({
|
|
13381
13555
|
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -13399,7 +13573,7 @@ var stateSchema = z18.object({
|
|
|
13399
13573
|
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
13400
13574
|
});
|
|
13401
13575
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13402
|
-
return
|
|
13576
|
+
return join28(dir2, "update.json");
|
|
13403
13577
|
}
|
|
13404
13578
|
function readUpdateState(path = updateStatePath()) {
|
|
13405
13579
|
try {
|
|
@@ -13448,7 +13622,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
13448
13622
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
13449
13623
|
}
|
|
13450
13624
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
13451
|
-
const built =
|
|
13625
|
+
const built = join29(root, "dist", "boot-guard.js");
|
|
13452
13626
|
if (!existsSync18(built)) return false;
|
|
13453
13627
|
const target = bootGuardPath();
|
|
13454
13628
|
mkdirSync16(dirname13(target), { recursive: true });
|
|
@@ -13488,17 +13662,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
13488
13662
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
13489
13663
|
const run = deps.run ?? runProcess;
|
|
13490
13664
|
const node = deps.execPath ?? process.execPath;
|
|
13491
|
-
const target =
|
|
13665
|
+
const target = join29(versionsDir(), version);
|
|
13492
13666
|
const placed = versionPackageRoot(version);
|
|
13493
13667
|
if (existsSync18(placed)) {
|
|
13494
13668
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
13495
13669
|
return { ok: true, root: placed };
|
|
13496
13670
|
}
|
|
13497
|
-
const npm =
|
|
13671
|
+
const npm = join29(dirname13(node), "npm");
|
|
13498
13672
|
if (!existsSync18(npm))
|
|
13499
13673
|
return { ok: false, reason: `there is no npm beside Node at ${dirname13(node)}` };
|
|
13500
13674
|
mkdirSync16(versionsDir(), { recursive: true });
|
|
13501
|
-
const staging =
|
|
13675
|
+
const staging = join29(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
13502
13676
|
rmSync14(staging, { recursive: true, force: true });
|
|
13503
13677
|
const installed = await run(
|
|
13504
13678
|
npm,
|
|
@@ -13527,7 +13701,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13527
13701
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
13528
13702
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
13529
13703
|
}
|
|
13530
|
-
const root =
|
|
13704
|
+
const root = join29(staging, "node_modules", "@cabane", "companion");
|
|
13531
13705
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
13532
13706
|
if (verdict) {
|
|
13533
13707
|
rmSync14(staging, { recursive: true, force: true });
|
|
@@ -13539,7 +13713,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13539
13713
|
}
|
|
13540
13714
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
13541
13715
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
13542
|
-
const answered = await deps.run(deps.execPath, [
|
|
13716
|
+
const answered = await deps.run(deps.execPath, [join29(root, "dist", "bin.js"), "--version"], {
|
|
13543
13717
|
timeoutMs: 6e4,
|
|
13544
13718
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
13545
13719
|
});
|
|
@@ -13547,7 +13721,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
13547
13721
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
13548
13722
|
}
|
|
13549
13723
|
if (harnesses.length === 0) return null;
|
|
13550
|
-
const probed = await deps.run(deps.execPath, [
|
|
13724
|
+
const probed = await deps.run(deps.execPath, [join29(root, "dist", "postinstall.js"), "--json"], {
|
|
13551
13725
|
timeoutMs: 6e4
|
|
13552
13726
|
});
|
|
13553
13727
|
let resolutions = {};
|
|
@@ -15348,9 +15522,9 @@ function handleUncaught(log, err, origin) {
|
|
|
15348
15522
|
|
|
15349
15523
|
// src/crash-marker.ts
|
|
15350
15524
|
import { existsSync as existsSync19, mkdirSync as mkdirSync17, readFileSync as readFileSync16, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "fs";
|
|
15351
|
-
import { join as
|
|
15525
|
+
import { join as join30 } from "path";
|
|
15352
15526
|
function crashMarkerPath() {
|
|
15353
|
-
return
|
|
15527
|
+
return join30(cabaneDir(), "last-error.json");
|
|
15354
15528
|
}
|
|
15355
15529
|
function recordCrash(rec) {
|
|
15356
15530
|
try {
|
|
@@ -15370,7 +15544,7 @@ function clearCrash() {
|
|
|
15370
15544
|
// src/stray-companion.ts
|
|
15371
15545
|
import { spawnSync } from "child_process";
|
|
15372
15546
|
import { readFileSync as readFileSync17 } from "fs";
|
|
15373
|
-
import { join as
|
|
15547
|
+
import { join as join31 } from "path";
|
|
15374
15548
|
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
15375
15549
|
var START = /\sstart(\s|$)/;
|
|
15376
15550
|
function companionStarts(psOutput) {
|
|
@@ -15387,7 +15561,7 @@ function companionStarts(psOutput) {
|
|
|
15387
15561
|
}
|
|
15388
15562
|
function stateDirOf(env) {
|
|
15389
15563
|
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
15390
|
-
return home ?
|
|
15564
|
+
return home ? join31(home, ".cabane") : null;
|
|
15391
15565
|
}
|
|
15392
15566
|
function envFromPsE(output, command) {
|
|
15393
15567
|
const line = output.replace(/\n$/, "");
|