@cabane/companion 0.6.169 → 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 +353 -153
- package/dist/runtime.js +324 -139
- 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({
|
|
@@ -6114,7 +6130,14 @@ function fingerprintPrompt(prompt) {
|
|
|
6114
6130
|
return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
|
|
6115
6131
|
}
|
|
6116
6132
|
function encodeSession3(state) {
|
|
6117
|
-
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
|
+
});
|
|
6118
6141
|
}
|
|
6119
6142
|
function decideResume3(stored, currentCwd) {
|
|
6120
6143
|
if (!stored) return { fresh: true, reason: "no_session" };
|
|
@@ -6135,6 +6158,8 @@ function decideResume3(stored, currentCwd) {
|
|
|
6135
6158
|
return {
|
|
6136
6159
|
resume: state.threadId,
|
|
6137
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,
|
|
6138
6163
|
usageBaseline: readUsageBaseline(state.usageBaseline)
|
|
6139
6164
|
};
|
|
6140
6165
|
}
|
|
@@ -6167,6 +6192,7 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6167
6192
|
let usage;
|
|
6168
6193
|
let cumulativeUsage;
|
|
6169
6194
|
let threadIdForSession = ctx.resumedThreadId;
|
|
6195
|
+
let lastState = ctx.storedState ?? null;
|
|
6170
6196
|
let settled = false;
|
|
6171
6197
|
function* flushInterim() {
|
|
6172
6198
|
const sealed = sealHeld2(held, false);
|
|
@@ -6182,14 +6208,14 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6182
6208
|
const state = encodeSession3({
|
|
6183
6209
|
threadId,
|
|
6184
6210
|
cwd: ctx.cwd ?? "",
|
|
6185
|
-
promptFingerprint: ctx.promptFingerprint ?? null,
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
// closing write records the new baseline.
|
|
6211
|
+
promptFingerprint: ctx.heldStanding?.promptFingerprint ?? null,
|
|
6212
|
+
conversationFingerprint: ctx.heldStanding?.conversationFingerprint ?? null,
|
|
6213
|
+
compactions: ctx.heldStanding?.compactions ?? null,
|
|
6189
6214
|
usageBaseline: ctx.usageBaseline ?? null
|
|
6190
6215
|
});
|
|
6191
6216
|
if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
|
|
6192
6217
|
sessionEmitted = true;
|
|
6218
|
+
lastState = state;
|
|
6193
6219
|
yield {
|
|
6194
6220
|
type: "session",
|
|
6195
6221
|
state,
|
|
@@ -6288,14 +6314,16 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6288
6314
|
if (sealed) yield sealed;
|
|
6289
6315
|
reason = "no_terminal";
|
|
6290
6316
|
}
|
|
6291
|
-
if (ok &&
|
|
6317
|
+
if (ok && threadIdForSession) {
|
|
6292
6318
|
const closing = encodeSession3({
|
|
6293
6319
|
threadId: threadIdForSession,
|
|
6294
6320
|
cwd: ctx.cwd ?? "",
|
|
6295
6321
|
promptFingerprint: ctx.promptFingerprint ?? null,
|
|
6296
|
-
|
|
6322
|
+
conversationFingerprint: ctx.conversationFingerprint ?? null,
|
|
6323
|
+
compactions: ctx.compactions ?? null,
|
|
6324
|
+
usageBaseline: cumulativeUsage ?? ctx.usageBaseline ?? null
|
|
6297
6325
|
});
|
|
6298
|
-
yield { type: "session", state: closing };
|
|
6326
|
+
if (closing !== lastState) yield { type: "session", state: closing };
|
|
6299
6327
|
}
|
|
6300
6328
|
const resolvedConfig = ctx.resolvedReasoningEffort ? { reasoningEffort: ctx.resolvedReasoningEffort } : void 0;
|
|
6301
6329
|
yield {
|
|
@@ -6365,15 +6393,27 @@ var ENV_ENVELOPE_KEYS = [
|
|
|
6365
6393
|
"CABANE_CONVERSATION_TITLE",
|
|
6366
6394
|
"CABANE_HOST_ACCESS"
|
|
6367
6395
|
];
|
|
6368
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null,
|
|
6396
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
|
|
6369
6397
|
const { policy, config } = req;
|
|
6370
6398
|
const directory = req.local.cwd ?? "";
|
|
6371
6399
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
6372
6400
|
const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
|
|
6373
6401
|
const model = config.model ? parseCodexModel(config.model) : null;
|
|
6374
|
-
const
|
|
6375
|
-
const
|
|
6376
|
-
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
|
+
};
|
|
6377
6417
|
return {
|
|
6378
6418
|
resumeThreadId,
|
|
6379
6419
|
directory,
|
|
@@ -6382,9 +6422,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
6382
6422
|
skipGitRepoCheck: true,
|
|
6383
6423
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
6384
6424
|
baseInstructionsFile,
|
|
6385
|
-
input: withImages(buildInput(req,
|
|
6386
|
-
|
|
6387
|
-
|
|
6425
|
+
input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
|
|
6426
|
+
preamble,
|
|
6427
|
+
fingerprints,
|
|
6388
6428
|
config: buildConfig(req, baseInstructionsFile),
|
|
6389
6429
|
secretEnv: codexSecretEnv(req)
|
|
6390
6430
|
};
|
|
@@ -6400,12 +6440,26 @@ function codexSecretEnv(req) {
|
|
|
6400
6440
|
}
|
|
6401
6441
|
return Object.keys(env).length > 0 ? env : null;
|
|
6402
6442
|
}
|
|
6403
|
-
|
|
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) {
|
|
6404
6446
|
const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
|
|
6405
6447
|
const body = userText.trim().length > 0 ? userText : req.prompt;
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
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")}
|
|
6409
6463
|
|
|
6410
6464
|
${body}` : body;
|
|
6411
6465
|
}
|
|
@@ -6617,7 +6671,6 @@ function createCodexAdapter(deps = {}) {
|
|
|
6617
6671
|
const directory = req.local.cwd ?? "";
|
|
6618
6672
|
const decision = decideResume3(req.session, directory);
|
|
6619
6673
|
const resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
6620
|
-
const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
|
|
6621
6674
|
const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
6622
6675
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
6623
6676
|
deps.onWarn?.(
|
|
@@ -6634,6 +6687,32 @@ function createCodexAdapter(deps = {}) {
|
|
|
6634
6687
|
degraded = false;
|
|
6635
6688
|
}
|
|
6636
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
|
+
}
|
|
6637
6716
|
let instructions = null;
|
|
6638
6717
|
if (!req.policy.hostFs) {
|
|
6639
6718
|
if (!deps.writeInstructionsFile) {
|
|
@@ -6678,7 +6757,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
6678
6757
|
req,
|
|
6679
6758
|
resumeThreadId,
|
|
6680
6759
|
instructions?.path ?? null,
|
|
6681
|
-
|
|
6760
|
+
threadStanding,
|
|
6682
6761
|
images?.paths ?? []
|
|
6683
6762
|
);
|
|
6684
6763
|
if (spec.model === null) {
|
|
@@ -6693,10 +6772,18 @@ function createCodexAdapter(deps = {}) {
|
|
|
6693
6772
|
cwd: req.local.cwd,
|
|
6694
6773
|
resumedThreadId: resumeThreadId,
|
|
6695
6774
|
degraded,
|
|
6696
|
-
// Which
|
|
6697
|
-
// later copy is what the model reads.
|
|
6698
|
-
//
|
|
6699
|
-
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
|
+
},
|
|
6700
6787
|
storedState: req.session,
|
|
6701
6788
|
usageBaseline: threadUsageBaseline,
|
|
6702
6789
|
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
@@ -6796,11 +6883,21 @@ var OP_FAILURE = {
|
|
|
6796
6883
|
message: "files.read returned more than the result ceiling"
|
|
6797
6884
|
}
|
|
6798
6885
|
};
|
|
6799
|
-
var
|
|
6886
|
+
var startedSession = (threadId, cwd = DIR2, degraded = false) => ({
|
|
6800
6887
|
type: "session",
|
|
6801
|
-
state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
|
|
6888
|
+
state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
|
|
6802
6889
|
...degraded ? { degraded: true } : {}
|
|
6803
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
|
+
});
|
|
6804
6901
|
var CODEX_CONFORMANCE_FIXTURES = [
|
|
6805
6902
|
{
|
|
6806
6903
|
// A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
|
|
@@ -6823,11 +6920,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6823
6920
|
})
|
|
6824
6921
|
],
|
|
6825
6922
|
expected: [
|
|
6826
|
-
|
|
6923
|
+
startedSession(NEW_THREAD_ID),
|
|
6827
6924
|
{ type: "text", body: "Hello there.", terminal: true },
|
|
6828
6925
|
// The closing session write — the thread's ending totals, recorded as the baseline the next turn
|
|
6829
6926
|
// subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
|
|
6830
|
-
|
|
6927
|
+
closingSession(NEW_THREAD_ID, {
|
|
6831
6928
|
inputTokens: 100,
|
|
6832
6929
|
outputTokens: 50,
|
|
6833
6930
|
cacheReadTokens: 20,
|
|
@@ -6874,7 +6971,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6874
6971
|
turnCompleted()
|
|
6875
6972
|
],
|
|
6876
6973
|
expected: [
|
|
6877
|
-
|
|
6974
|
+
startedSession(NEW_THREAD_ID),
|
|
6878
6975
|
{ type: "text", body: "Reading your notes.", terminal: false },
|
|
6879
6976
|
{
|
|
6880
6977
|
type: "tool",
|
|
@@ -6894,6 +6991,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6894
6991
|
result: [{ type: "text", text: "sleep early" }]
|
|
6895
6992
|
},
|
|
6896
6993
|
{ type: "text", body: "Here's the summary.", terminal: true },
|
|
6994
|
+
closingSession(NEW_THREAD_ID),
|
|
6897
6995
|
{ type: "result", ok: true }
|
|
6898
6996
|
]
|
|
6899
6997
|
},
|
|
@@ -6927,7 +7025,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6927
7025
|
turnCompleted()
|
|
6928
7026
|
],
|
|
6929
7027
|
expected: [
|
|
6930
|
-
|
|
7028
|
+
startedSession(NEW_THREAD_ID),
|
|
6931
7029
|
{
|
|
6932
7030
|
type: "tool",
|
|
6933
7031
|
id: "query-success",
|
|
@@ -6945,6 +7043,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6945
7043
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6946
7044
|
result: OP_SUCCESS
|
|
6947
7045
|
},
|
|
7046
|
+
closingSession(NEW_THREAD_ID),
|
|
6948
7047
|
{ type: "result", ok: true }
|
|
6949
7048
|
]
|
|
6950
7049
|
},
|
|
@@ -6980,7 +7079,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6980
7079
|
turnCompleted()
|
|
6981
7080
|
],
|
|
6982
7081
|
expected: [
|
|
6983
|
-
|
|
7082
|
+
startedSession(NEW_THREAD_ID),
|
|
6984
7083
|
{
|
|
6985
7084
|
type: "tool",
|
|
6986
7085
|
id: "query-failed",
|
|
@@ -6998,6 +7097,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
6998
7097
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6999
7098
|
result: OP_FAILURE
|
|
7000
7099
|
},
|
|
7100
|
+
closingSession(NEW_THREAD_ID),
|
|
7001
7101
|
{ type: "result", ok: true }
|
|
7002
7102
|
]
|
|
7003
7103
|
},
|
|
@@ -7021,7 +7121,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7021
7121
|
turnCompleted()
|
|
7022
7122
|
],
|
|
7023
7123
|
expected: [
|
|
7024
|
-
|
|
7124
|
+
startedSession(NEW_THREAD_ID),
|
|
7025
7125
|
{
|
|
7026
7126
|
type: "tool",
|
|
7027
7127
|
id: "mcp-failed",
|
|
@@ -7039,6 +7139,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7039
7139
|
input: { q: "receipt" },
|
|
7040
7140
|
result: "connection closed"
|
|
7041
7141
|
},
|
|
7142
|
+
closingSession(NEW_THREAD_ID),
|
|
7042
7143
|
{ type: "result", ok: true }
|
|
7043
7144
|
]
|
|
7044
7145
|
},
|
|
@@ -7064,7 +7165,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7064
7165
|
turnCompleted()
|
|
7065
7166
|
],
|
|
7066
7167
|
expected: [
|
|
7067
|
-
|
|
7168
|
+
startedSession(NEW_THREAD_ID),
|
|
7068
7169
|
{
|
|
7069
7170
|
type: "tool",
|
|
7070
7171
|
id: "mcp-image",
|
|
@@ -7082,6 +7183,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7082
7183
|
input: {},
|
|
7083
7184
|
result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
|
|
7084
7185
|
},
|
|
7186
|
+
closingSession(NEW_THREAD_ID),
|
|
7085
7187
|
{ type: "result", ok: true }
|
|
7086
7188
|
]
|
|
7087
7189
|
},
|
|
@@ -7122,7 +7224,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7122
7224
|
turnCompleted()
|
|
7123
7225
|
],
|
|
7124
7226
|
expected: [
|
|
7125
|
-
|
|
7227
|
+
startedSession(NEW_THREAD_ID),
|
|
7126
7228
|
{ type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
|
|
7127
7229
|
{
|
|
7128
7230
|
type: "tool",
|
|
@@ -7133,6 +7235,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7133
7235
|
input: { path: "/product/tasks" },
|
|
7134
7236
|
result: [{ type: "text", text: "CT481.md" }]
|
|
7135
7237
|
},
|
|
7238
|
+
closingSession(NEW_THREAD_ID),
|
|
7136
7239
|
{ type: "result", ok: true }
|
|
7137
7240
|
]
|
|
7138
7241
|
},
|
|
@@ -7150,7 +7253,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7150
7253
|
turnCompleted()
|
|
7151
7254
|
],
|
|
7152
7255
|
expected: [
|
|
7153
|
-
|
|
7256
|
+
startedSession(NEW_THREAD_ID),
|
|
7154
7257
|
{
|
|
7155
7258
|
type: "tool",
|
|
7156
7259
|
id: "ws1",
|
|
@@ -7167,6 +7270,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7167
7270
|
summary: "best pizza in nyc",
|
|
7168
7271
|
input: { query: "best pizza in nyc" }
|
|
7169
7272
|
},
|
|
7273
|
+
closingSession(NEW_THREAD_ID),
|
|
7170
7274
|
{ type: "result", ok: true }
|
|
7171
7275
|
]
|
|
7172
7276
|
},
|
|
@@ -7182,7 +7286,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7182
7286
|
turnCompleted()
|
|
7183
7287
|
],
|
|
7184
7288
|
expected: [
|
|
7185
|
-
|
|
7289
|
+
startedSession(NEW_THREAD_ID),
|
|
7186
7290
|
{
|
|
7187
7291
|
type: "tool",
|
|
7188
7292
|
id: "ws2",
|
|
@@ -7199,6 +7303,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7199
7303
|
summary: "",
|
|
7200
7304
|
input: { query: "" }
|
|
7201
7305
|
},
|
|
7306
|
+
closingSession(NEW_THREAD_ID),
|
|
7202
7307
|
{ type: "result", ok: true }
|
|
7203
7308
|
]
|
|
7204
7309
|
},
|
|
@@ -7229,7 +7334,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7229
7334
|
turnCompleted()
|
|
7230
7335
|
],
|
|
7231
7336
|
expected: [
|
|
7232
|
-
|
|
7337
|
+
startedSession(NEW_THREAD_ID),
|
|
7233
7338
|
{ type: "text", body: "Working on it.", terminal: false },
|
|
7234
7339
|
{
|
|
7235
7340
|
type: "tool",
|
|
@@ -7261,7 +7366,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7261
7366
|
turnFailed("provider exploded")
|
|
7262
7367
|
],
|
|
7263
7368
|
expected: [
|
|
7264
|
-
|
|
7369
|
+
startedSession(NEW_THREAD_ID),
|
|
7265
7370
|
{ type: "text", body: "Partial work.", terminal: false },
|
|
7266
7371
|
{ type: "runtime_notice", body: "provider exploded" },
|
|
7267
7372
|
{ type: "result", ok: false, reason: "error:provider exploded" }
|
|
@@ -7278,7 +7383,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7278
7383
|
fatalError("stream died")
|
|
7279
7384
|
],
|
|
7280
7385
|
expected: [
|
|
7281
|
-
|
|
7386
|
+
startedSession(NEW_THREAD_ID),
|
|
7282
7387
|
{ type: "text", body: "Starting.", terminal: false },
|
|
7283
7388
|
{ type: "runtime_notice", body: "stream died" },
|
|
7284
7389
|
{ type: "result", ok: false, reason: "error:stream died" }
|
|
@@ -7291,7 +7396,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7291
7396
|
request: makeRequest3(),
|
|
7292
7397
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
|
|
7293
7398
|
expected: [
|
|
7294
|
-
|
|
7399
|
+
startedSession(NEW_THREAD_ID),
|
|
7295
7400
|
{ type: "runtime_notice", body: "429 Too Many Requests" },
|
|
7296
7401
|
{ type: "result", ok: false, reason: "rate_limited" }
|
|
7297
7402
|
]
|
|
@@ -7301,7 +7406,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7301
7406
|
request: makeRequest3(),
|
|
7302
7407
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
|
|
7303
7408
|
expected: [
|
|
7304
|
-
|
|
7409
|
+
startedSession(NEW_THREAD_ID),
|
|
7305
7410
|
{ type: "runtime_notice", body: "503 Service Unavailable" },
|
|
7306
7411
|
{ type: "result", ok: false, reason: "server_error" }
|
|
7307
7412
|
]
|
|
@@ -7314,7 +7419,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7314
7419
|
turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
|
|
7315
7420
|
],
|
|
7316
7421
|
expected: [
|
|
7317
|
-
|
|
7422
|
+
startedSession(NEW_THREAD_ID),
|
|
7318
7423
|
{
|
|
7319
7424
|
type: "runtime_notice",
|
|
7320
7425
|
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
@@ -7327,7 +7432,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7327
7432
|
request: makeRequest3(),
|
|
7328
7433
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
|
|
7329
7434
|
expected: [
|
|
7330
|
-
|
|
7435
|
+
startedSession(NEW_THREAD_ID),
|
|
7331
7436
|
{ type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
|
|
7332
7437
|
{ type: "result", ok: false, reason: "auth_expired" }
|
|
7333
7438
|
]
|
|
@@ -7339,7 +7444,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7339
7444
|
request: makeRequest3(),
|
|
7340
7445
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
|
|
7341
7446
|
expected: [
|
|
7342
|
-
|
|
7447
|
+
startedSession(NEW_THREAD_ID),
|
|
7343
7448
|
{ type: "runtime_notice", body: "something local broke" },
|
|
7344
7449
|
{ type: "result", ok: false, reason: "error:something local broke" }
|
|
7345
7450
|
]
|
|
@@ -7352,7 +7457,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7352
7457
|
turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
|
|
7353
7458
|
],
|
|
7354
7459
|
expected: [
|
|
7355
|
-
|
|
7460
|
+
startedSession(NEW_THREAD_ID),
|
|
7356
7461
|
{ type: "runtime_notice", body: BOUNDED_NOTICE2 },
|
|
7357
7462
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
7358
7463
|
]
|
|
@@ -7363,26 +7468,32 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7363
7468
|
name: "empty failure text emits no runtime notice",
|
|
7364
7469
|
request: makeRequest3(),
|
|
7365
7470
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
|
|
7366
|
-
expected: [
|
|
7471
|
+
expected: [
|
|
7472
|
+
startedSession(NEW_THREAD_ID),
|
|
7473
|
+
{ type: "result", ok: false, reason: "error:unknown" }
|
|
7474
|
+
]
|
|
7367
7475
|
},
|
|
7368
7476
|
{
|
|
7369
7477
|
name: "whitespace-only failure text emits no runtime notice",
|
|
7370
7478
|
request: makeRequest3(),
|
|
7371
7479
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
|
|
7372
|
-
expected: [
|
|
7480
|
+
expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
|
|
7373
7481
|
},
|
|
7374
7482
|
{
|
|
7375
7483
|
name: "bare status token emits no runtime notice",
|
|
7376
7484
|
request: makeRequest3(),
|
|
7377
7485
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
|
|
7378
|
-
expected: [
|
|
7486
|
+
expected: [
|
|
7487
|
+
startedSession(NEW_THREAD_ID),
|
|
7488
|
+
{ type: "result", ok: false, reason: "rate_limited" }
|
|
7489
|
+
]
|
|
7379
7490
|
},
|
|
7380
7491
|
{
|
|
7381
7492
|
name: "single-word provider sentence emits a runtime notice",
|
|
7382
7493
|
request: makeRequest3(),
|
|
7383
7494
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
|
|
7384
7495
|
expected: [
|
|
7385
|
-
|
|
7496
|
+
startedSession(NEW_THREAD_ID),
|
|
7386
7497
|
{ type: "runtime_notice", body: "Unavailable." },
|
|
7387
7498
|
{ type: "result", ok: false, reason: "error:Unavailable." }
|
|
7388
7499
|
]
|
|
@@ -7410,7 +7521,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7410
7521
|
turnCompleted()
|
|
7411
7522
|
],
|
|
7412
7523
|
expected: [
|
|
7413
|
-
|
|
7524
|
+
startedSession(NEW_THREAD_ID),
|
|
7414
7525
|
{
|
|
7415
7526
|
type: "result",
|
|
7416
7527
|
ok: false,
|
|
@@ -7430,8 +7541,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7430
7541
|
turnCompleted()
|
|
7431
7542
|
],
|
|
7432
7543
|
expected: [
|
|
7433
|
-
|
|
7544
|
+
startedSession(NEW_THREAD_ID),
|
|
7434
7545
|
{ type: "text", body: "All good now.", terminal: true },
|
|
7546
|
+
closingSession(NEW_THREAD_ID),
|
|
7435
7547
|
{ type: "result", ok: true }
|
|
7436
7548
|
]
|
|
7437
7549
|
},
|
|
@@ -7443,7 +7555,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7443
7555
|
name: "zero-content success (empty runtime result)",
|
|
7444
7556
|
request: makeRequest3(),
|
|
7445
7557
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
|
|
7446
|
-
expected: [
|
|
7558
|
+
expected: [
|
|
7559
|
+
startedSession(NEW_THREAD_ID),
|
|
7560
|
+
closingSession(NEW_THREAD_ID),
|
|
7561
|
+
{ type: "result", ok: true }
|
|
7562
|
+
]
|
|
7447
7563
|
},
|
|
7448
7564
|
{
|
|
7449
7565
|
// Empty-final: a clean turn that ended on a tool call with no closing message.
|
|
@@ -7468,7 +7584,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7468
7584
|
turnCompleted()
|
|
7469
7585
|
],
|
|
7470
7586
|
expected: [
|
|
7471
|
-
|
|
7587
|
+
startedSession(NEW_THREAD_ID),
|
|
7472
7588
|
{
|
|
7473
7589
|
type: "tool",
|
|
7474
7590
|
id: "t1",
|
|
@@ -7486,6 +7602,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7486
7602
|
input: { changes: [{ path: "a.md", kind: "add" }] },
|
|
7487
7603
|
result: [{ path: "a.md", kind: "add" }]
|
|
7488
7604
|
},
|
|
7605
|
+
closingSession(NEW_THREAD_ID),
|
|
7489
7606
|
{ type: "result", ok: true }
|
|
7490
7607
|
]
|
|
7491
7608
|
},
|
|
@@ -7501,9 +7618,10 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7501
7618
|
turnCompleted()
|
|
7502
7619
|
],
|
|
7503
7620
|
expected: [
|
|
7504
|
-
|
|
7621
|
+
startedSession(NEW_THREAD_ID),
|
|
7505
7622
|
{ type: "thinking", text: "Let me check the notes." },
|
|
7506
7623
|
{ type: "text", body: "All set.", terminal: true },
|
|
7624
|
+
closingSession(NEW_THREAD_ID),
|
|
7507
7625
|
{ type: "result", ok: true }
|
|
7508
7626
|
]
|
|
7509
7627
|
},
|
|
@@ -7521,8 +7639,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7521
7639
|
}),
|
|
7522
7640
|
nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
|
|
7523
7641
|
expected: [
|
|
7524
|
-
|
|
7642
|
+
startedSession(NEW_THREAD_ID),
|
|
7525
7643
|
{ type: "text", body: "Done.", terminal: true },
|
|
7644
|
+
closingSession(NEW_THREAD_ID),
|
|
7526
7645
|
{
|
|
7527
7646
|
type: "result",
|
|
7528
7647
|
ok: true,
|
|
@@ -7544,7 +7663,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7544
7663
|
threadId: "th1",
|
|
7545
7664
|
cwd: DIR2,
|
|
7546
7665
|
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
7547
|
-
usageBaseline: null
|
|
7666
|
+
usageBaseline: null,
|
|
7667
|
+
compactions: 0
|
|
7548
7668
|
})
|
|
7549
7669
|
}),
|
|
7550
7670
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
@@ -7566,8 +7686,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7566
7686
|
}),
|
|
7567
7687
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
7568
7688
|
expected: [
|
|
7569
|
-
|
|
7689
|
+
startedSession("th1"),
|
|
7570
7690
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7691
|
+
closingSession("th1"),
|
|
7571
7692
|
{ type: "result", ok: true }
|
|
7572
7693
|
]
|
|
7573
7694
|
},
|
|
@@ -7589,8 +7710,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7589
7710
|
}),
|
|
7590
7711
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
7591
7712
|
expected: [
|
|
7592
|
-
|
|
7713
|
+
startedSession("th1"),
|
|
7593
7714
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7715
|
+
closingSession("th1"),
|
|
7594
7716
|
{ type: "result", ok: true }
|
|
7595
7717
|
]
|
|
7596
7718
|
},
|
|
@@ -7616,8 +7738,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
7616
7738
|
turnCompleted()
|
|
7617
7739
|
],
|
|
7618
7740
|
expected: [
|
|
7619
|
-
|
|
7741
|
+
startedSession(NEW_THREAD_ID, DIR2, true),
|
|
7620
7742
|
{ type: "text", body: "Fresh start.", terminal: true },
|
|
7743
|
+
closingSession(NEW_THREAD_ID),
|
|
7621
7744
|
{ type: "result", ok: true }
|
|
7622
7745
|
]
|
|
7623
7746
|
}
|
|
@@ -7699,7 +7822,7 @@ var ConnectorHealthStore = class {
|
|
|
7699
7822
|
// src/dispatcher.ts
|
|
7700
7823
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7701
7824
|
import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
7702
|
-
import { join as
|
|
7825
|
+
import { join as join19 } from "path";
|
|
7703
7826
|
|
|
7704
7827
|
// src/runner/launch.ts
|
|
7705
7828
|
import { createInterface } from "readline";
|
|
@@ -9097,6 +9220,7 @@ function buildCompanionTurnRequest(params) {
|
|
|
9097
9220
|
const { turnContext: t } = params;
|
|
9098
9221
|
return {
|
|
9099
9222
|
systemPrompt: t.systemPrompt,
|
|
9223
|
+
...t.standing ? { standing: t.standing } : {},
|
|
9100
9224
|
prompt: t.prompt,
|
|
9101
9225
|
content: t.content,
|
|
9102
9226
|
config: t.config,
|
|
@@ -9208,6 +9332,62 @@ function message(err) {
|
|
|
9208
9332
|
return err instanceof Error ? err.message : String(err);
|
|
9209
9333
|
}
|
|
9210
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
|
+
|
|
9211
9391
|
// src/turn-seq-floor.ts
|
|
9212
9392
|
var SeqFloorUnavailable = class extends Error {
|
|
9213
9393
|
constructor(detail) {
|
|
@@ -9305,15 +9485,15 @@ function reportFailure(config, body) {
|
|
|
9305
9485
|
|
|
9306
9486
|
// src/prepared.ts
|
|
9307
9487
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "fs";
|
|
9308
|
-
import { join as
|
|
9488
|
+
import { join as join17 } from "path";
|
|
9309
9489
|
function dirFor(workspaceId) {
|
|
9310
|
-
return
|
|
9490
|
+
return join17(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
9311
9491
|
}
|
|
9312
9492
|
function conversationDir(workspaceId, conversationId) {
|
|
9313
|
-
return
|
|
9493
|
+
return join17(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
9314
9494
|
}
|
|
9315
9495
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
9316
|
-
return
|
|
9496
|
+
return join17(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
9317
9497
|
}
|
|
9318
9498
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
9319
9499
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -9345,9 +9525,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
|
|
|
9345
9525
|
|
|
9346
9526
|
// src/transcript-writer.ts
|
|
9347
9527
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
9348
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
9528
|
+
import { basename as basename3, dirname as dirname7, join as join18 } from "path";
|
|
9349
9529
|
function transcriptsDir() {
|
|
9350
|
-
return
|
|
9530
|
+
return join18(runnerStateDir(), "transcripts");
|
|
9351
9531
|
}
|
|
9352
9532
|
var RETAIN = 200;
|
|
9353
9533
|
var ANOMALY_RETAIN = 50;
|
|
@@ -9357,7 +9537,7 @@ var TranscriptWriter = class {
|
|
|
9357
9537
|
onWarn;
|
|
9358
9538
|
constructor(dir2, meta, onWarn) {
|
|
9359
9539
|
this.onWarn = onWarn;
|
|
9360
|
-
this.path =
|
|
9540
|
+
this.path = join18(dir2, fileName(meta));
|
|
9361
9541
|
try {
|
|
9362
9542
|
mkdirSync8(dir2, { recursive: true });
|
|
9363
9543
|
try {
|
|
@@ -9386,10 +9566,10 @@ var TranscriptWriter = class {
|
|
|
9386
9566
|
preserveAnomaly() {
|
|
9387
9567
|
if (this.broken) return;
|
|
9388
9568
|
try {
|
|
9389
|
-
const dir2 =
|
|
9569
|
+
const dir2 = join18(dirname7(this.path), "anomalies");
|
|
9390
9570
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
9391
9571
|
chmodSync4(dir2, 448);
|
|
9392
|
-
const target =
|
|
9572
|
+
const target = join18(dir2, basename3(this.path));
|
|
9393
9573
|
copyFileSync(this.path, target);
|
|
9394
9574
|
chmodSync4(target, 384);
|
|
9395
9575
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9435,7 +9615,7 @@ function pruneOld(dir2, retain) {
|
|
|
9435
9615
|
const drop = files.sort().slice(0, files.length - retain);
|
|
9436
9616
|
for (const f of drop) {
|
|
9437
9617
|
try {
|
|
9438
|
-
rmSync6(
|
|
9618
|
+
rmSync6(join18(dir2, f), { force: true });
|
|
9439
9619
|
} catch {
|
|
9440
9620
|
}
|
|
9441
9621
|
}
|
|
@@ -10344,6 +10524,8 @@ var TurnExecution = class {
|
|
|
10344
10524
|
turnId: this.turnId,
|
|
10345
10525
|
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
10346
10526
|
}),
|
|
10527
|
+
// Whether a resumed thread compacted since its standing preamble was sent.
|
|
10528
|
+
countCompactions: createCodexCompactionCounter(),
|
|
10347
10529
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
10348
10530
|
})
|
|
10349
10531
|
);
|
|
@@ -10404,6 +10586,9 @@ var TurnExecution = class {
|
|
|
10404
10586
|
);
|
|
10405
10587
|
return {
|
|
10406
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,
|
|
10407
10592
|
prompt: context.prompt,
|
|
10408
10593
|
content: context.content
|
|
10409
10594
|
};
|
|
@@ -10815,7 +11000,7 @@ function checkoutState(cwd) {
|
|
|
10815
11000
|
if (entries.length === 0) {
|
|
10816
11001
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10817
11002
|
}
|
|
10818
|
-
const gitPath =
|
|
11003
|
+
const gitPath = join19(cwd, ".git");
|
|
10819
11004
|
if (!existsSync12(gitPath)) return { ok: true, reason: "usable" };
|
|
10820
11005
|
let stat;
|
|
10821
11006
|
try {
|
|
@@ -10823,7 +11008,7 @@ function checkoutState(cwd) {
|
|
|
10823
11008
|
} catch (error) {
|
|
10824
11009
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10825
11010
|
}
|
|
10826
|
-
if (stat.isDirectory() && !existsSync12(
|
|
11011
|
+
if (stat.isDirectory() && !existsSync12(join19(gitPath, "HEAD")))
|
|
10827
11012
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10828
11013
|
return { ok: true, reason: "usable" };
|
|
10829
11014
|
}
|
|
@@ -11030,7 +11215,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
11030
11215
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
11031
11216
|
import { constants as constants3, tmpdir as tmpdir3 } from "os";
|
|
11032
11217
|
import {
|
|
11033
|
-
createReadStream,
|
|
11218
|
+
createReadStream as createReadStream2,
|
|
11034
11219
|
createWriteStream as createWriteStream2,
|
|
11035
11220
|
existsSync as existsSync15,
|
|
11036
11221
|
mkdirSync as mkdirSync10,
|
|
@@ -11041,7 +11226,7 @@ import {
|
|
|
11041
11226
|
statSync as statSync4,
|
|
11042
11227
|
writeFileSync as writeFileSync6
|
|
11043
11228
|
} from "fs";
|
|
11044
|
-
import { join as
|
|
11229
|
+
import { join as join22 } from "path";
|
|
11045
11230
|
import { Writable } from "stream";
|
|
11046
11231
|
|
|
11047
11232
|
// src/providers/docker-cli.ts
|
|
@@ -11235,7 +11420,7 @@ import {
|
|
|
11235
11420
|
renameSync as renameSync5,
|
|
11236
11421
|
rmSync as rmSync8
|
|
11237
11422
|
} from "fs";
|
|
11238
|
-
import { join as
|
|
11423
|
+
import { join as join20 } from "path";
|
|
11239
11424
|
function routes(t) {
|
|
11240
11425
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
11241
11426
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -11281,7 +11466,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11281
11466
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
11282
11467
|
let entry = {};
|
|
11283
11468
|
try {
|
|
11284
|
-
entry = JSON.parse(readFileSync9(
|
|
11469
|
+
entry = JSON.parse(readFileSync9(join20(from, name), "utf8"));
|
|
11285
11470
|
} catch {
|
|
11286
11471
|
}
|
|
11287
11472
|
return { name, entry };
|
|
@@ -11294,7 +11479,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11294
11479
|
let published = 0;
|
|
11295
11480
|
for (const { name } of staged) {
|
|
11296
11481
|
try {
|
|
11297
|
-
move(
|
|
11482
|
+
move(join20(from, name), join20(to, name));
|
|
11298
11483
|
} catch {
|
|
11299
11484
|
return { published, complete: false };
|
|
11300
11485
|
}
|
|
@@ -11307,7 +11492,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11307
11492
|
// src/providers/runner-base.ts
|
|
11308
11493
|
import { createHash as createHash3 } from "crypto";
|
|
11309
11494
|
import { existsSync as existsSync14, realpathSync as realpathSync4 } from "fs";
|
|
11310
|
-
import { dirname as dirname8, join as
|
|
11495
|
+
import { dirname as dirname8, join as join21, sep as sep3 } from "path";
|
|
11311
11496
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11312
11497
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
11313
11498
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -11355,11 +11540,11 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
11355
11540
|
function ownInstall(moduleFile = fileURLToPath5(import.meta.url)) {
|
|
11356
11541
|
let dir2 = dirname8(moduleFile);
|
|
11357
11542
|
for (let i = 0; i < 4; i++) {
|
|
11358
|
-
if (existsSync14(
|
|
11543
|
+
if (existsSync14(join21(dir2, "dist", "cli.js")) && existsSync14(join21(dir2, "package.json"))) break;
|
|
11359
11544
|
dir2 = dirname8(dir2);
|
|
11360
11545
|
}
|
|
11361
|
-
if (!existsSync14(
|
|
11362
|
-
const sdk =
|
|
11546
|
+
if (!existsSync14(join21(dir2, "dist", "cli.js"))) return null;
|
|
11547
|
+
const sdk = join21(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
11363
11548
|
if (!existsSync14(sdk)) return null;
|
|
11364
11549
|
const root = realpathSync4(dir2);
|
|
11365
11550
|
return realpathSync4(sdk).startsWith(root + sep3) ? root : null;
|
|
@@ -11498,39 +11683,39 @@ function networkCreateError(stderr) {
|
|
|
11498
11683
|
}
|
|
11499
11684
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11500
11685
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11501
|
-
return
|
|
11686
|
+
return join22(deviceDir, "attempts", attemptId, "session.tar");
|
|
11502
11687
|
}
|
|
11503
11688
|
function pruneOtherAttempts(deviceDir, pair, keep) {
|
|
11504
|
-
const root =
|
|
11689
|
+
const root = join22(deviceDir, "attempts");
|
|
11505
11690
|
if (!existsSync15(root)) return;
|
|
11506
11691
|
for (const id of readdirSync4(root)) {
|
|
11507
11692
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
11508
11693
|
let pairOf = "";
|
|
11509
11694
|
try {
|
|
11510
|
-
pairOf = readFileSync10(
|
|
11695
|
+
pairOf = readFileSync10(join22(root, id, "pair"), "utf8");
|
|
11511
11696
|
} catch {
|
|
11512
11697
|
continue;
|
|
11513
11698
|
}
|
|
11514
11699
|
if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
|
|
11515
|
-
rmSync9(
|
|
11700
|
+
rmSync9(join22(root, id), { recursive: true, force: true });
|
|
11516
11701
|
}
|
|
11517
11702
|
}
|
|
11518
11703
|
}
|
|
11519
11704
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
11520
11705
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
11521
|
-
const root =
|
|
11706
|
+
const root = join22(deviceDir, "attempts");
|
|
11522
11707
|
if (!existsSync15(root)) return [];
|
|
11523
11708
|
const removed = [];
|
|
11524
11709
|
for (const id of readdirSync4(root)) {
|
|
11525
11710
|
if (!UUID_RE.test(id)) continue;
|
|
11526
11711
|
let kept;
|
|
11527
11712
|
try {
|
|
11528
|
-
kept = statSync4(
|
|
11713
|
+
kept = statSync4(join22(root, id)).mtimeMs;
|
|
11529
11714
|
} catch {
|
|
11530
11715
|
continue;
|
|
11531
11716
|
}
|
|
11532
11717
|
if (now - kept < maxAgeMs) continue;
|
|
11533
|
-
rmSync9(
|
|
11718
|
+
rmSync9(join22(root, id), { recursive: true, force: true });
|
|
11534
11719
|
removed.push(id);
|
|
11535
11720
|
}
|
|
11536
11721
|
return removed;
|
|
@@ -11731,11 +11916,11 @@ function createDockerProvider(opts) {
|
|
|
11731
11916
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
11732
11917
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
11733
11918
|
};
|
|
11734
|
-
const socketRoot = opts.socketRoot ??
|
|
11919
|
+
const socketRoot = opts.socketRoot ?? join22(
|
|
11735
11920
|
process.env.XDG_RUNTIME_DIR && existsSync15(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
11736
11921
|
`cabane-runners-${opts.deviceKey}`
|
|
11737
11922
|
);
|
|
11738
|
-
const socketDir = (name) =>
|
|
11923
|
+
const socketDir = (name) => join22(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
11739
11924
|
const create = async (name, attempt) => {
|
|
11740
11925
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
11741
11926
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -11877,7 +12062,7 @@ function createDockerProvider(opts) {
|
|
|
11877
12062
|
}
|
|
11878
12063
|
};
|
|
11879
12064
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
11880
|
-
const pendingPath = (name) =>
|
|
12065
|
+
const pendingPath = (name) => join22(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
11881
12066
|
const readPending = (name) => {
|
|
11882
12067
|
try {
|
|
11883
12068
|
const ids = JSON.parse(readFileSync10(pendingPath(name), "utf8"));
|
|
@@ -11891,12 +12076,12 @@ function createDockerProvider(opts) {
|
|
|
11891
12076
|
rmSync9(pendingPath(name), { force: true });
|
|
11892
12077
|
return;
|
|
11893
12078
|
}
|
|
11894
|
-
mkdirSync10(
|
|
12079
|
+
mkdirSync10(join22(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
11895
12080
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
11896
12081
|
};
|
|
11897
12082
|
const keepSession = async (name, attempt, attemptId) => {
|
|
11898
12083
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
11899
|
-
mkdirSync10(
|
|
12084
|
+
mkdirSync10(join22(archive, ".."), { recursive: true });
|
|
11900
12085
|
const partial = `${archive}.${process.pid}.partial`;
|
|
11901
12086
|
const file = new CappedFile(partial, sessionCap);
|
|
11902
12087
|
const r = await execIn(
|
|
@@ -11913,7 +12098,7 @@ function createDockerProvider(opts) {
|
|
|
11913
12098
|
);
|
|
11914
12099
|
if (r.code === 0 && !file.over) {
|
|
11915
12100
|
renameSync6(partial, archive);
|
|
11916
|
-
writeFileSync6(
|
|
12101
|
+
writeFileSync6(join22(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
11917
12102
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
11918
12103
|
await writeMarker(name, attemptId);
|
|
11919
12104
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -11927,7 +12112,7 @@ function createDockerProvider(opts) {
|
|
|
11927
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"
|
|
11928
12113
|
);
|
|
11929
12114
|
};
|
|
11930
|
-
const stagingDir = (name) =>
|
|
12115
|
+
const stagingDir = (name) => join22(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
11931
12116
|
const ensureRunning = async (name) => {
|
|
11932
12117
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
11933
12118
|
if (seen.code !== 0) return false;
|
|
@@ -11935,7 +12120,7 @@ function createDockerProvider(opts) {
|
|
|
11935
12120
|
return (await docker.run(["start", name])).code === 0;
|
|
11936
12121
|
};
|
|
11937
12122
|
const dropArchive = (attemptId) => {
|
|
11938
|
-
rmSync9(
|
|
12123
|
+
rmSync9(join22(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
11939
12124
|
recursive: true,
|
|
11940
12125
|
force: true
|
|
11941
12126
|
});
|
|
@@ -12004,7 +12189,7 @@ function createDockerProvider(opts) {
|
|
|
12004
12189
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
12005
12190
|
await must(["start", name], "starting the container");
|
|
12006
12191
|
const git = await serveGitCredentials({
|
|
12007
|
-
socketPath:
|
|
12192
|
+
socketPath: join22(socketDir(name), "git.sock"),
|
|
12008
12193
|
hosts: opts.gitHosts,
|
|
12009
12194
|
fill: opts.gitFill,
|
|
12010
12195
|
log
|
|
@@ -12040,7 +12225,7 @@ function createDockerProvider(opts) {
|
|
|
12040
12225
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
12041
12226
|
if (existsSync15(archive)) {
|
|
12042
12227
|
const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
|
|
12043
|
-
input:
|
|
12228
|
+
input: createReadStream2(archive)
|
|
12044
12229
|
});
|
|
12045
12230
|
if (r.code !== 0) {
|
|
12046
12231
|
log.warn(
|
|
@@ -12153,15 +12338,15 @@ function createDockerProvider(opts) {
|
|
|
12153
12338
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
12154
12339
|
if (transcripts) {
|
|
12155
12340
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
12156
|
-
if (kept.length) mkdirSync10(
|
|
12157
|
-
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);
|
|
12158
12343
|
await removeIn(
|
|
12159
12344
|
name,
|
|
12160
12345
|
`${CONTAINER_STATE}/transcripts`,
|
|
12161
12346
|
kept.map((f) => f.name)
|
|
12162
12347
|
);
|
|
12163
12348
|
}
|
|
12164
|
-
const outboxRel =
|
|
12349
|
+
const outboxRel = join22("outbox", encodeURIComponent(attempt.agentId));
|
|
12165
12350
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
12166
12351
|
let unpublished = false;
|
|
12167
12352
|
if (outbox) {
|
|
@@ -12187,7 +12372,7 @@ function createDockerProvider(opts) {
|
|
|
12187
12372
|
const staged = stagingDir(name);
|
|
12188
12373
|
mkdirSync10(staged, { recursive: true });
|
|
12189
12374
|
writeFileSync6(
|
|
12190
|
-
|
|
12375
|
+
join22(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
12191
12376
|
`${JSON.stringify(entry)}
|
|
12192
12377
|
`
|
|
12193
12378
|
);
|
|
@@ -12200,7 +12385,7 @@ function createDockerProvider(opts) {
|
|
|
12200
12385
|
if (outbox.complete) {
|
|
12201
12386
|
const published = publishStaged(
|
|
12202
12387
|
stagingDir(name),
|
|
12203
|
-
|
|
12388
|
+
join22(opts.hostStateDir, outboxRel),
|
|
12204
12389
|
opts.moveFile
|
|
12205
12390
|
);
|
|
12206
12391
|
queued = published.published;
|
|
@@ -12415,8 +12600,8 @@ import {
|
|
|
12415
12600
|
} from "fs";
|
|
12416
12601
|
import { createRequire as createRequire2 } from "module";
|
|
12417
12602
|
import { isIP } from "net";
|
|
12418
|
-
import { homedir as
|
|
12419
|
-
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";
|
|
12420
12605
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
12421
12606
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
12422
12607
|
function accountHome() {
|
|
@@ -12513,7 +12698,7 @@ function companionStoreWatch(log) {
|
|
|
12513
12698
|
return new CompanionStoreWatch({
|
|
12514
12699
|
log,
|
|
12515
12700
|
roots: () => {
|
|
12516
|
-
const home =
|
|
12701
|
+
const home = homedir7();
|
|
12517
12702
|
return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
|
|
12518
12703
|
}
|
|
12519
12704
|
});
|
|
@@ -12612,8 +12797,8 @@ function emptyHarnessVersions() {
|
|
|
12612
12797
|
}
|
|
12613
12798
|
|
|
12614
12799
|
// src/harness-copy.ts
|
|
12615
|
-
import { homedir as
|
|
12616
|
-
import { join as
|
|
12800
|
+
import { homedir as homedir8 } from "os";
|
|
12801
|
+
import { join as join24 } from "path";
|
|
12617
12802
|
var HARNESS_LABELS = {
|
|
12618
12803
|
"claude-code": "Claude Code",
|
|
12619
12804
|
codex: "Codex",
|
|
@@ -12627,7 +12812,7 @@ function failureText(runtime, reason) {
|
|
|
12627
12812
|
case "spawn":
|
|
12628
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())}`;
|
|
12629
12814
|
case "config":
|
|
12630
|
-
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.`;
|
|
12631
12816
|
case "unreadable":
|
|
12632
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.`;
|
|
12633
12818
|
}
|
|
@@ -12967,7 +13152,7 @@ import {
|
|
|
12967
13152
|
rmSync as rmSync11,
|
|
12968
13153
|
writeFileSync as writeFileSync8
|
|
12969
13154
|
} from "fs";
|
|
12970
|
-
import { join as
|
|
13155
|
+
import { join as join25 } from "path";
|
|
12971
13156
|
var MAX_ENTRIES = 2e3;
|
|
12972
13157
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12973
13158
|
var Outbox = class {
|
|
@@ -12980,10 +13165,10 @@ var Outbox = class {
|
|
|
12980
13165
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
12981
13166
|
// cases route writes at the right tmpdir.
|
|
12982
13167
|
dir() {
|
|
12983
|
-
return
|
|
13168
|
+
return join25(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
12984
13169
|
}
|
|
12985
13170
|
fileFor(turnId, seq) {
|
|
12986
|
-
return
|
|
13171
|
+
return join25(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
12987
13172
|
}
|
|
12988
13173
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
12989
13174
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -13025,7 +13210,7 @@ var Outbox = class {
|
|
|
13025
13210
|
const entries = [];
|
|
13026
13211
|
for (const name of names) {
|
|
13027
13212
|
if (!name.endsWith(".json")) continue;
|
|
13028
|
-
const full =
|
|
13213
|
+
const full = join25(dir2, name);
|
|
13029
13214
|
try {
|
|
13030
13215
|
const parsed = JSON.parse(readFileSync12(full, "utf8"));
|
|
13031
13216
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -13249,7 +13434,7 @@ var SseSubscriber = class {
|
|
|
13249
13434
|
// src/updater.ts
|
|
13250
13435
|
import { spawn as spawn8 } from "child_process";
|
|
13251
13436
|
import { copyFileSync as copyFileSync3, existsSync as existsSync18, mkdirSync as mkdirSync16, renameSync as renameSync11, rmSync as rmSync14 } from "fs";
|
|
13252
|
-
import { dirname as dirname13, join as
|
|
13437
|
+
import { dirname as dirname13, join as join29 } from "path";
|
|
13253
13438
|
|
|
13254
13439
|
// src/boot-rule.ts
|
|
13255
13440
|
import {
|
|
@@ -13261,7 +13446,7 @@ import {
|
|
|
13261
13446
|
symlinkSync,
|
|
13262
13447
|
writeFileSync as writeFileSync9
|
|
13263
13448
|
} from "fs";
|
|
13264
|
-
import { dirname as dirname10, join as
|
|
13449
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
13265
13450
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
13266
13451
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
13267
13452
|
function superseded(pending, ...running) {
|
|
@@ -13294,26 +13479,26 @@ import {
|
|
|
13294
13479
|
rmSync as rmSync13,
|
|
13295
13480
|
symlinkSync as symlinkSync2
|
|
13296
13481
|
} from "fs";
|
|
13297
|
-
import { dirname as dirname11, join as
|
|
13482
|
+
import { dirname as dirname11, join as join27 } from "path";
|
|
13298
13483
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
13299
13484
|
function versionsDir() {
|
|
13300
|
-
return
|
|
13485
|
+
return join27(cabaneDir(), "versions");
|
|
13301
13486
|
}
|
|
13302
13487
|
function currentLink() {
|
|
13303
|
-
return
|
|
13488
|
+
return join27(cabaneDir(), "current");
|
|
13304
13489
|
}
|
|
13305
13490
|
function bootGuardPath() {
|
|
13306
|
-
return
|
|
13491
|
+
return join27(cabaneDir(), "boot-guard.mjs");
|
|
13307
13492
|
}
|
|
13308
13493
|
function versionPackageRoot(version) {
|
|
13309
|
-
return
|
|
13494
|
+
return join27(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
13310
13495
|
}
|
|
13311
13496
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
13312
13497
|
return dirname11(dirname11(fileURLToPath7(moduleUrl)));
|
|
13313
13498
|
}
|
|
13314
13499
|
function packageVersion(root) {
|
|
13315
13500
|
try {
|
|
13316
|
-
const pkg2 = JSON.parse(readFileSync14(
|
|
13501
|
+
const pkg2 = JSON.parse(readFileSync14(join27(root, "package.json"), "utf8"));
|
|
13317
13502
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
13318
13503
|
} catch {
|
|
13319
13504
|
return null;
|
|
@@ -13322,7 +13507,7 @@ function packageVersion(root) {
|
|
|
13322
13507
|
function currentRoot() {
|
|
13323
13508
|
try {
|
|
13324
13509
|
const root = realpathSync7(currentLink());
|
|
13325
|
-
return existsSync17(
|
|
13510
|
+
return existsSync17(join27(root, "dist", "cli.js")) ? root : null;
|
|
13326
13511
|
} catch {
|
|
13327
13512
|
return null;
|
|
13328
13513
|
}
|
|
@@ -13345,7 +13530,7 @@ function pruneVersions(keep) {
|
|
|
13345
13530
|
const removed = [];
|
|
13346
13531
|
for (const name of readdirSync6(dir2)) {
|
|
13347
13532
|
if (keep.includes(name)) continue;
|
|
13348
|
-
rmSync13(
|
|
13533
|
+
rmSync13(join27(dir2, name), { recursive: true, force: true });
|
|
13349
13534
|
removed.push(name);
|
|
13350
13535
|
}
|
|
13351
13536
|
return removed;
|
|
@@ -13364,7 +13549,7 @@ function compareVersions(a, b) {
|
|
|
13364
13549
|
|
|
13365
13550
|
// src/update-state.ts
|
|
13366
13551
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync15, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
13367
|
-
import { dirname as dirname12, join as
|
|
13552
|
+
import { dirname as dirname12, join as join28 } from "path";
|
|
13368
13553
|
import { z as z18 } from "zod";
|
|
13369
13554
|
var resultSchema = z18.object({
|
|
13370
13555
|
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -13388,7 +13573,7 @@ var stateSchema = z18.object({
|
|
|
13388
13573
|
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
13389
13574
|
});
|
|
13390
13575
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13391
|
-
return
|
|
13576
|
+
return join28(dir2, "update.json");
|
|
13392
13577
|
}
|
|
13393
13578
|
function readUpdateState(path = updateStatePath()) {
|
|
13394
13579
|
try {
|
|
@@ -13437,7 +13622,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
13437
13622
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
13438
13623
|
}
|
|
13439
13624
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
13440
|
-
const built =
|
|
13625
|
+
const built = join29(root, "dist", "boot-guard.js");
|
|
13441
13626
|
if (!existsSync18(built)) return false;
|
|
13442
13627
|
const target = bootGuardPath();
|
|
13443
13628
|
mkdirSync16(dirname13(target), { recursive: true });
|
|
@@ -13477,17 +13662,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
13477
13662
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
13478
13663
|
const run = deps.run ?? runProcess;
|
|
13479
13664
|
const node = deps.execPath ?? process.execPath;
|
|
13480
|
-
const target =
|
|
13665
|
+
const target = join29(versionsDir(), version);
|
|
13481
13666
|
const placed = versionPackageRoot(version);
|
|
13482
13667
|
if (existsSync18(placed)) {
|
|
13483
13668
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
13484
13669
|
return { ok: true, root: placed };
|
|
13485
13670
|
}
|
|
13486
|
-
const npm =
|
|
13671
|
+
const npm = join29(dirname13(node), "npm");
|
|
13487
13672
|
if (!existsSync18(npm))
|
|
13488
13673
|
return { ok: false, reason: `there is no npm beside Node at ${dirname13(node)}` };
|
|
13489
13674
|
mkdirSync16(versionsDir(), { recursive: true });
|
|
13490
|
-
const staging =
|
|
13675
|
+
const staging = join29(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
13491
13676
|
rmSync14(staging, { recursive: true, force: true });
|
|
13492
13677
|
const installed = await run(
|
|
13493
13678
|
npm,
|
|
@@ -13516,7 +13701,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13516
13701
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
13517
13702
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
13518
13703
|
}
|
|
13519
|
-
const root =
|
|
13704
|
+
const root = join29(staging, "node_modules", "@cabane", "companion");
|
|
13520
13705
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
13521
13706
|
if (verdict) {
|
|
13522
13707
|
rmSync14(staging, { recursive: true, force: true });
|
|
@@ -13528,7 +13713,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13528
13713
|
}
|
|
13529
13714
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
13530
13715
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
13531
|
-
const answered = await deps.run(deps.execPath, [
|
|
13716
|
+
const answered = await deps.run(deps.execPath, [join29(root, "dist", "bin.js"), "--version"], {
|
|
13532
13717
|
timeoutMs: 6e4,
|
|
13533
13718
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
13534
13719
|
});
|
|
@@ -13536,7 +13721,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
13536
13721
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
13537
13722
|
}
|
|
13538
13723
|
if (harnesses.length === 0) return null;
|
|
13539
|
-
const probed = await deps.run(deps.execPath, [
|
|
13724
|
+
const probed = await deps.run(deps.execPath, [join29(root, "dist", "postinstall.js"), "--json"], {
|
|
13540
13725
|
timeoutMs: 6e4
|
|
13541
13726
|
});
|
|
13542
13727
|
let resolutions = {};
|
|
@@ -15337,9 +15522,9 @@ function handleUncaught(log, err, origin) {
|
|
|
15337
15522
|
|
|
15338
15523
|
// src/crash-marker.ts
|
|
15339
15524
|
import { existsSync as existsSync19, mkdirSync as mkdirSync17, readFileSync as readFileSync16, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "fs";
|
|
15340
|
-
import { join as
|
|
15525
|
+
import { join as join30 } from "path";
|
|
15341
15526
|
function crashMarkerPath() {
|
|
15342
|
-
return
|
|
15527
|
+
return join30(cabaneDir(), "last-error.json");
|
|
15343
15528
|
}
|
|
15344
15529
|
function recordCrash(rec) {
|
|
15345
15530
|
try {
|
|
@@ -15359,7 +15544,7 @@ function clearCrash() {
|
|
|
15359
15544
|
// src/stray-companion.ts
|
|
15360
15545
|
import { spawnSync } from "child_process";
|
|
15361
15546
|
import { readFileSync as readFileSync17 } from "fs";
|
|
15362
|
-
import { join as
|
|
15547
|
+
import { join as join31 } from "path";
|
|
15363
15548
|
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
15364
15549
|
var START = /\sstart(\s|$)/;
|
|
15365
15550
|
function companionStarts(psOutput) {
|
|
@@ -15376,7 +15561,7 @@ function companionStarts(psOutput) {
|
|
|
15376
15561
|
}
|
|
15377
15562
|
function stateDirOf(env) {
|
|
15378
15563
|
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
15379
|
-
return home ?
|
|
15564
|
+
return home ? join31(home, ".cabane") : null;
|
|
15380
15565
|
}
|
|
15381
15566
|
function envFromPsE(output, command) {
|
|
15382
15567
|
const line = output.replace(/\n$/, "");
|