@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/cli.js
CHANGED
|
@@ -2084,9 +2084,15 @@ var init_turn_request = __esm({
|
|
|
2084
2084
|
resolvedMcpServersSchema = z9.record(z9.string(), resolvedMcpServerSchema);
|
|
2085
2085
|
hostInjectedServersSchema = z9.record(z9.string(), z9.unknown());
|
|
2086
2086
|
turnRequestSchema = z9.object({
|
|
2087
|
-
// Server-composed
|
|
2088
|
-
// charter)
|
|
2087
|
+
// Server-composed standing instructions: the core segment (body, tool naming, identity, agent
|
|
2088
|
+
// charter) and, after it, the conversation segment (workspace and room charters, skill indexes,
|
|
2089
|
+
// roster). One string to the adapter, current as of this dispatch.
|
|
2089
2090
|
systemPrompt: z9.string(),
|
|
2091
|
+
// The same text as its two ordered segments, so an adapter that writes standing text into the
|
|
2092
|
+
// conversation (Codex's input preamble) can tell which half changed. `systemPrompt` is
|
|
2093
|
+
// `joinStanding(standing)`. Optional: absent from an API older than the split, where
|
|
2094
|
+
// `systemPrompt` is the core alone and the conversation material rides `prompt`.
|
|
2095
|
+
standing: z9.object({ core: z9.string(), conversation: z9.string() }).optional(),
|
|
2090
2096
|
// Server-composed per-turn user text (anchor reminder + the triggering message).
|
|
2091
2097
|
prompt: z9.string(),
|
|
2092
2098
|
// The multi-block user-message body (text + vision).
|
|
@@ -2136,6 +2142,22 @@ var init_turn_request = __esm({
|
|
|
2136
2142
|
}
|
|
2137
2143
|
});
|
|
2138
2144
|
|
|
2145
|
+
// packages/agent-runtime/src/standing.ts
|
|
2146
|
+
function joinStanding(segments) {
|
|
2147
|
+
const conversation = segments.conversation.trim();
|
|
2148
|
+
return conversation.length > 0 ? `${segments.core}${STANDING_SEPARATOR}${conversation}` : segments.core;
|
|
2149
|
+
}
|
|
2150
|
+
function standingSegments(req) {
|
|
2151
|
+
return req.standing ?? { core: req.systemPrompt, conversation: "" };
|
|
2152
|
+
}
|
|
2153
|
+
var STANDING_SEPARATOR;
|
|
2154
|
+
var init_standing = __esm({
|
|
2155
|
+
"packages/agent-runtime/src/standing.ts"() {
|
|
2156
|
+
"use strict";
|
|
2157
|
+
STANDING_SEPARATOR = "\n\n---\n\n";
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
|
|
2139
2161
|
// packages/agent-runtime/src/conformance.ts
|
|
2140
2162
|
import { z as z10 } from "zod";
|
|
2141
2163
|
var conformanceFixtureSchema;
|
|
@@ -5374,7 +5396,14 @@ function fingerprintPrompt(prompt) {
|
|
|
5374
5396
|
return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
|
|
5375
5397
|
}
|
|
5376
5398
|
function encodeSession3(state) {
|
|
5377
|
-
return JSON.stringify(
|
|
5399
|
+
return JSON.stringify({
|
|
5400
|
+
threadId: state.threadId,
|
|
5401
|
+
cwd: state.cwd,
|
|
5402
|
+
promptFingerprint: state.promptFingerprint,
|
|
5403
|
+
conversationFingerprint: state.conversationFingerprint ?? null,
|
|
5404
|
+
compactions: state.compactions ?? null,
|
|
5405
|
+
usageBaseline: state.usageBaseline
|
|
5406
|
+
});
|
|
5378
5407
|
}
|
|
5379
5408
|
function decideResume3(stored, currentCwd) {
|
|
5380
5409
|
if (!stored) return { fresh: true, reason: "no_session" };
|
|
@@ -5395,6 +5424,8 @@ function decideResume3(stored, currentCwd) {
|
|
|
5395
5424
|
return {
|
|
5396
5425
|
resume: state.threadId,
|
|
5397
5426
|
promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
|
|
5427
|
+
conversationFingerprint: typeof state.conversationFingerprint === "string" ? state.conversationFingerprint : null,
|
|
5428
|
+
compactions: typeof state.compactions === "number" && Number.isInteger(state.compactions) && state.compactions >= 0 ? state.compactions : null,
|
|
5398
5429
|
usageBaseline: readUsageBaseline(state.usageBaseline)
|
|
5399
5430
|
};
|
|
5400
5431
|
}
|
|
@@ -5432,6 +5463,7 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5432
5463
|
let usage;
|
|
5433
5464
|
let cumulativeUsage;
|
|
5434
5465
|
let threadIdForSession = ctx2.resumedThreadId;
|
|
5466
|
+
let lastState = ctx2.storedState ?? null;
|
|
5435
5467
|
let settled = false;
|
|
5436
5468
|
function* flushInterim() {
|
|
5437
5469
|
const sealed = sealHeld2(held, false);
|
|
@@ -5447,14 +5479,14 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5447
5479
|
const state = encodeSession3({
|
|
5448
5480
|
threadId,
|
|
5449
5481
|
cwd: ctx2.cwd ?? "",
|
|
5450
|
-
promptFingerprint: ctx2.promptFingerprint ?? null,
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
// closing write records the new baseline.
|
|
5482
|
+
promptFingerprint: ctx2.heldStanding?.promptFingerprint ?? null,
|
|
5483
|
+
conversationFingerprint: ctx2.heldStanding?.conversationFingerprint ?? null,
|
|
5484
|
+
compactions: ctx2.heldStanding?.compactions ?? null,
|
|
5454
5485
|
usageBaseline: ctx2.usageBaseline ?? null
|
|
5455
5486
|
});
|
|
5456
5487
|
if (threadId !== ctx2.resumedThreadId || state !== ctx2.storedState) {
|
|
5457
5488
|
sessionEmitted = true;
|
|
5489
|
+
lastState = state;
|
|
5458
5490
|
yield {
|
|
5459
5491
|
type: "session",
|
|
5460
5492
|
state,
|
|
@@ -5553,14 +5585,16 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5553
5585
|
if (sealed) yield sealed;
|
|
5554
5586
|
reason = "no_terminal";
|
|
5555
5587
|
}
|
|
5556
|
-
if (ok &&
|
|
5588
|
+
if (ok && threadIdForSession) {
|
|
5557
5589
|
const closing = encodeSession3({
|
|
5558
5590
|
threadId: threadIdForSession,
|
|
5559
5591
|
cwd: ctx2.cwd ?? "",
|
|
5560
5592
|
promptFingerprint: ctx2.promptFingerprint ?? null,
|
|
5561
|
-
|
|
5593
|
+
conversationFingerprint: ctx2.conversationFingerprint ?? null,
|
|
5594
|
+
compactions: ctx2.compactions ?? null,
|
|
5595
|
+
usageBaseline: cumulativeUsage ?? ctx2.usageBaseline ?? null
|
|
5562
5596
|
});
|
|
5563
|
-
yield { type: "session", state: closing };
|
|
5597
|
+
if (closing !== lastState) yield { type: "session", state: closing };
|
|
5564
5598
|
}
|
|
5565
5599
|
const resolvedConfig = ctx2.resolvedReasoningEffort ? { reasoningEffort: ctx2.resolvedReasoningEffort } : void 0;
|
|
5566
5600
|
yield {
|
|
@@ -5640,15 +5674,27 @@ var init_model3 = __esm({
|
|
|
5640
5674
|
});
|
|
5641
5675
|
|
|
5642
5676
|
// packages/agent-runtime/src/codex/run-spec.ts
|
|
5643
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null,
|
|
5677
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
|
|
5644
5678
|
const { policy, config } = req;
|
|
5645
5679
|
const directory = req.local.cwd ?? "";
|
|
5646
5680
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
5647
5681
|
const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
|
|
5648
5682
|
const model = config.model ? parseCodexModel(config.model) : null;
|
|
5649
|
-
const
|
|
5650
|
-
const
|
|
5651
|
-
const
|
|
5683
|
+
const segments = standingSegments(req);
|
|
5684
|
+
const conversationText = segments.conversation.trim();
|
|
5685
|
+
const fingerprints = {
|
|
5686
|
+
core: fingerprintPrompt(segments.core),
|
|
5687
|
+
conversation: conversationText.length > 0 ? fingerprintPrompt(conversationText) : null
|
|
5688
|
+
};
|
|
5689
|
+
const resumed = resumeThreadId !== null;
|
|
5690
|
+
const held = resumed ? threadStanding : null;
|
|
5691
|
+
const viaInput = baseInstructionsFile === null;
|
|
5692
|
+
const preamble = {
|
|
5693
|
+
core: viaInput && (held?.core ?? null) !== fingerprints.core,
|
|
5694
|
+
// Compared both ways: a held segment that is now empty differs from it, and nothing held
|
|
5695
|
+
// equals nothing to send.
|
|
5696
|
+
conversation: viaInput && (held?.conversation ?? null) !== fingerprints.conversation
|
|
5697
|
+
};
|
|
5652
5698
|
return {
|
|
5653
5699
|
resumeThreadId,
|
|
5654
5700
|
directory,
|
|
@@ -5657,9 +5703,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
5657
5703
|
skipGitRepoCheck: true,
|
|
5658
5704
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
5659
5705
|
baseInstructionsFile,
|
|
5660
|
-
input: withImages(buildInput(req,
|
|
5661
|
-
|
|
5662
|
-
|
|
5706
|
+
input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
|
|
5707
|
+
preamble,
|
|
5708
|
+
fingerprints,
|
|
5663
5709
|
config: buildConfig(req, baseInstructionsFile),
|
|
5664
5710
|
secretEnv: codexSecretEnv(req)
|
|
5665
5711
|
};
|
|
@@ -5675,12 +5721,24 @@ function codexSecretEnv(req) {
|
|
|
5675
5721
|
}
|
|
5676
5722
|
return Object.keys(env).length > 0 ? env : null;
|
|
5677
5723
|
}
|
|
5678
|
-
function buildInput(req,
|
|
5724
|
+
function buildInput(req, segments, preamble, resumed) {
|
|
5679
5725
|
const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
|
|
5680
5726
|
const body = userText.trim().length > 0 ? userText : req.prompt;
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5727
|
+
const standing = [];
|
|
5728
|
+
if (!resumed) {
|
|
5729
|
+
if (preamble.core) standing.push(joinStanding(segments).trim());
|
|
5730
|
+
} else {
|
|
5731
|
+
if (preamble.core) standing.push(`${CORE_REPLACES}
|
|
5732
|
+
|
|
5733
|
+
${segments.core.trim()}`);
|
|
5734
|
+
if (preamble.conversation) {
|
|
5735
|
+
standing.push(`${CONVERSATION_REPLACES}
|
|
5736
|
+
|
|
5737
|
+
${segments.conversation.trim()}`.trim());
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
const lead = standing.filter((s) => s.length > 0);
|
|
5741
|
+
return lead.length > 0 ? `${lead.join("\n\n---\n\n")}
|
|
5684
5742
|
|
|
5685
5743
|
${body}` : body;
|
|
5686
5744
|
}
|
|
@@ -5766,10 +5824,11 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
5766
5824
|
function isStringRecord2(v) {
|
|
5767
5825
|
return !!v && typeof v === "object" && Object.values(v).every((x) => typeof x === "string");
|
|
5768
5826
|
}
|
|
5769
|
-
var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS;
|
|
5827
|
+
var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS, CORE_REPLACES, CONVERSATION_REPLACES;
|
|
5770
5828
|
var init_run_spec2 = __esm({
|
|
5771
5829
|
"packages/agent-runtime/src/codex/run-spec.ts"() {
|
|
5772
5830
|
"use strict";
|
|
5831
|
+
init_standing();
|
|
5773
5832
|
init_policy3();
|
|
5774
5833
|
init_model3();
|
|
5775
5834
|
init_session3();
|
|
@@ -5784,6 +5843,8 @@ var init_run_spec2 = __esm({
|
|
|
5784
5843
|
"CABANE_CONVERSATION_TITLE",
|
|
5785
5844
|
"CABANE_HOST_ACCESS"
|
|
5786
5845
|
];
|
|
5846
|
+
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.";
|
|
5847
|
+
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.";
|
|
5787
5848
|
}
|
|
5788
5849
|
});
|
|
5789
5850
|
|
|
@@ -5922,7 +5983,6 @@ function createCodexAdapter(deps = {}) {
|
|
|
5922
5983
|
const directory = req.local.cwd ?? "";
|
|
5923
5984
|
const decision = decideResume3(req.session, directory);
|
|
5924
5985
|
const resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
5925
|
-
const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
|
|
5926
5986
|
const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
5927
5987
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
5928
5988
|
deps.onWarn?.(
|
|
@@ -5939,6 +5999,32 @@ function createCodexAdapter(deps = {}) {
|
|
|
5939
5999
|
degraded = false;
|
|
5940
6000
|
}
|
|
5941
6001
|
}
|
|
6002
|
+
let compactions = 0;
|
|
6003
|
+
let threadStanding = null;
|
|
6004
|
+
if ("resume" in decision) {
|
|
6005
|
+
compactions = deps.countCompactions ? await deps.countCompactions(decision.resume) : null;
|
|
6006
|
+
if (signal.aborted) return;
|
|
6007
|
+
if (compactions === null) {
|
|
6008
|
+
deps.onWarn?.(
|
|
6009
|
+
"codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions (ENG210)",
|
|
6010
|
+
{ threadId: decision.resume }
|
|
6011
|
+
);
|
|
6012
|
+
} else if (decision.compactions === compactions) {
|
|
6013
|
+
threadStanding = {
|
|
6014
|
+
core: decision.promptFingerprint,
|
|
6015
|
+
conversation: decision.conversationFingerprint
|
|
6016
|
+
};
|
|
6017
|
+
} else if (decision.compactions !== null && compactions > decision.compactions) {
|
|
6018
|
+
deps.onWarn?.(
|
|
6019
|
+
"codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions (ENG210)",
|
|
6020
|
+
{
|
|
6021
|
+
threadId: decision.resume,
|
|
6022
|
+
compactions,
|
|
6023
|
+
compactionsSince: compactions - decision.compactions
|
|
6024
|
+
}
|
|
6025
|
+
);
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
5942
6028
|
let instructions = null;
|
|
5943
6029
|
if (!req.policy.hostFs) {
|
|
5944
6030
|
if (!deps.writeInstructionsFile) {
|
|
@@ -5983,7 +6069,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
5983
6069
|
req,
|
|
5984
6070
|
resumeThreadId,
|
|
5985
6071
|
instructions?.path ?? null,
|
|
5986
|
-
|
|
6072
|
+
threadStanding,
|
|
5987
6073
|
images?.paths ?? []
|
|
5988
6074
|
);
|
|
5989
6075
|
if (spec.model === null) {
|
|
@@ -5998,10 +6084,18 @@ function createCodexAdapter(deps = {}) {
|
|
|
5998
6084
|
cwd: req.local.cwd,
|
|
5999
6085
|
resumedThreadId: resumeThreadId,
|
|
6000
6086
|
degraded,
|
|
6001
|
-
// Which
|
|
6002
|
-
// later copy is what the model reads.
|
|
6003
|
-
//
|
|
6004
|
-
promptFingerprint: spec.
|
|
6087
|
+
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6088
|
+
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6089
|
+
// the spec, so the record can't disagree with what was sent.
|
|
6090
|
+
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6091
|
+
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6092
|
+
compactions,
|
|
6093
|
+
// What the thread held before this turn, for the write at `thread.started`.
|
|
6094
|
+
heldStanding: {
|
|
6095
|
+
promptFingerprint: threadStanding?.core ?? null,
|
|
6096
|
+
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6097
|
+
compactions: threadStanding ? compactions : null
|
|
6098
|
+
},
|
|
6005
6099
|
storedState: req.session,
|
|
6006
6100
|
usageBaseline: threadUsageBaseline,
|
|
6007
6101
|
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
@@ -6072,7 +6166,7 @@ function makeRequest3(overrides2 = {}) {
|
|
|
6072
6166
|
...overrides2
|
|
6073
6167
|
};
|
|
6074
6168
|
}
|
|
6075
|
-
var ABORT_SENTINEL3, NEW_THREAD_ID, BOUNDED_NOTICE2, COMPANION_POLICY2, DIR2, PROMPT, threadStarted, agentMessage, reasoning, toolFrame, turnCompleted, turnFailed, fatalError, errorItem, OP_SUCCESS, OP_FAILURE,
|
|
6169
|
+
var ABORT_SENTINEL3, NEW_THREAD_ID, BOUNDED_NOTICE2, COMPANION_POLICY2, DIR2, PROMPT, threadStarted, agentMessage, reasoning, toolFrame, turnCompleted, turnFailed, fatalError, errorItem, OP_SUCCESS, OP_FAILURE, startedSession, closingSession, CODEX_CONFORMANCE_FIXTURES;
|
|
6076
6170
|
var init_conformance4 = __esm({
|
|
6077
6171
|
"packages/agent-runtime/src/codex/conformance.ts"() {
|
|
6078
6172
|
"use strict";
|
|
@@ -6127,11 +6221,21 @@ var init_conformance4 = __esm({
|
|
|
6127
6221
|
message: "files.read returned more than the result ceiling"
|
|
6128
6222
|
}
|
|
6129
6223
|
};
|
|
6130
|
-
|
|
6224
|
+
startedSession = (threadId, cwd = DIR2, degraded = false) => ({
|
|
6131
6225
|
type: "session",
|
|
6132
|
-
state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
|
|
6226
|
+
state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
|
|
6133
6227
|
...degraded ? { degraded: true } : {}
|
|
6134
6228
|
});
|
|
6229
|
+
closingSession = (threadId, usageBaseline = null) => ({
|
|
6230
|
+
type: "session",
|
|
6231
|
+
state: encodeSession3({
|
|
6232
|
+
threadId,
|
|
6233
|
+
cwd: DIR2,
|
|
6234
|
+
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
6235
|
+
usageBaseline,
|
|
6236
|
+
compactions: 0
|
|
6237
|
+
})
|
|
6238
|
+
});
|
|
6135
6239
|
CODEX_CONFORMANCE_FIXTURES = [
|
|
6136
6240
|
{
|
|
6137
6241
|
// A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
|
|
@@ -6154,11 +6258,11 @@ var init_conformance4 = __esm({
|
|
|
6154
6258
|
})
|
|
6155
6259
|
],
|
|
6156
6260
|
expected: [
|
|
6157
|
-
|
|
6261
|
+
startedSession(NEW_THREAD_ID),
|
|
6158
6262
|
{ type: "text", body: "Hello there.", terminal: true },
|
|
6159
6263
|
// The closing session write — the thread's ending totals, recorded as the baseline the next turn
|
|
6160
6264
|
// subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
|
|
6161
|
-
|
|
6265
|
+
closingSession(NEW_THREAD_ID, {
|
|
6162
6266
|
inputTokens: 100,
|
|
6163
6267
|
outputTokens: 50,
|
|
6164
6268
|
cacheReadTokens: 20,
|
|
@@ -6205,7 +6309,7 @@ var init_conformance4 = __esm({
|
|
|
6205
6309
|
turnCompleted()
|
|
6206
6310
|
],
|
|
6207
6311
|
expected: [
|
|
6208
|
-
|
|
6312
|
+
startedSession(NEW_THREAD_ID),
|
|
6209
6313
|
{ type: "text", body: "Reading your notes.", terminal: false },
|
|
6210
6314
|
{
|
|
6211
6315
|
type: "tool",
|
|
@@ -6225,6 +6329,7 @@ var init_conformance4 = __esm({
|
|
|
6225
6329
|
result: [{ type: "text", text: "sleep early" }]
|
|
6226
6330
|
},
|
|
6227
6331
|
{ type: "text", body: "Here's the summary.", terminal: true },
|
|
6332
|
+
closingSession(NEW_THREAD_ID),
|
|
6228
6333
|
{ type: "result", ok: true }
|
|
6229
6334
|
]
|
|
6230
6335
|
},
|
|
@@ -6258,7 +6363,7 @@ var init_conformance4 = __esm({
|
|
|
6258
6363
|
turnCompleted()
|
|
6259
6364
|
],
|
|
6260
6365
|
expected: [
|
|
6261
|
-
|
|
6366
|
+
startedSession(NEW_THREAD_ID),
|
|
6262
6367
|
{
|
|
6263
6368
|
type: "tool",
|
|
6264
6369
|
id: "query-success",
|
|
@@ -6276,6 +6381,7 @@ var init_conformance4 = __esm({
|
|
|
6276
6381
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6277
6382
|
result: OP_SUCCESS
|
|
6278
6383
|
},
|
|
6384
|
+
closingSession(NEW_THREAD_ID),
|
|
6279
6385
|
{ type: "result", ok: true }
|
|
6280
6386
|
]
|
|
6281
6387
|
},
|
|
@@ -6311,7 +6417,7 @@ var init_conformance4 = __esm({
|
|
|
6311
6417
|
turnCompleted()
|
|
6312
6418
|
],
|
|
6313
6419
|
expected: [
|
|
6314
|
-
|
|
6420
|
+
startedSession(NEW_THREAD_ID),
|
|
6315
6421
|
{
|
|
6316
6422
|
type: "tool",
|
|
6317
6423
|
id: "query-failed",
|
|
@@ -6329,6 +6435,7 @@ var init_conformance4 = __esm({
|
|
|
6329
6435
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6330
6436
|
result: OP_FAILURE
|
|
6331
6437
|
},
|
|
6438
|
+
closingSession(NEW_THREAD_ID),
|
|
6332
6439
|
{ type: "result", ok: true }
|
|
6333
6440
|
]
|
|
6334
6441
|
},
|
|
@@ -6352,7 +6459,7 @@ var init_conformance4 = __esm({
|
|
|
6352
6459
|
turnCompleted()
|
|
6353
6460
|
],
|
|
6354
6461
|
expected: [
|
|
6355
|
-
|
|
6462
|
+
startedSession(NEW_THREAD_ID),
|
|
6356
6463
|
{
|
|
6357
6464
|
type: "tool",
|
|
6358
6465
|
id: "mcp-failed",
|
|
@@ -6370,6 +6477,7 @@ var init_conformance4 = __esm({
|
|
|
6370
6477
|
input: { q: "receipt" },
|
|
6371
6478
|
result: "connection closed"
|
|
6372
6479
|
},
|
|
6480
|
+
closingSession(NEW_THREAD_ID),
|
|
6373
6481
|
{ type: "result", ok: true }
|
|
6374
6482
|
]
|
|
6375
6483
|
},
|
|
@@ -6395,7 +6503,7 @@ var init_conformance4 = __esm({
|
|
|
6395
6503
|
turnCompleted()
|
|
6396
6504
|
],
|
|
6397
6505
|
expected: [
|
|
6398
|
-
|
|
6506
|
+
startedSession(NEW_THREAD_ID),
|
|
6399
6507
|
{
|
|
6400
6508
|
type: "tool",
|
|
6401
6509
|
id: "mcp-image",
|
|
@@ -6413,6 +6521,7 @@ var init_conformance4 = __esm({
|
|
|
6413
6521
|
input: {},
|
|
6414
6522
|
result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
|
|
6415
6523
|
},
|
|
6524
|
+
closingSession(NEW_THREAD_ID),
|
|
6416
6525
|
{ type: "result", ok: true }
|
|
6417
6526
|
]
|
|
6418
6527
|
},
|
|
@@ -6453,7 +6562,7 @@ var init_conformance4 = __esm({
|
|
|
6453
6562
|
turnCompleted()
|
|
6454
6563
|
],
|
|
6455
6564
|
expected: [
|
|
6456
|
-
|
|
6565
|
+
startedSession(NEW_THREAD_ID),
|
|
6457
6566
|
{ type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
|
|
6458
6567
|
{
|
|
6459
6568
|
type: "tool",
|
|
@@ -6464,6 +6573,7 @@ var init_conformance4 = __esm({
|
|
|
6464
6573
|
input: { path: "/product/tasks" },
|
|
6465
6574
|
result: [{ type: "text", text: "CT481.md" }]
|
|
6466
6575
|
},
|
|
6576
|
+
closingSession(NEW_THREAD_ID),
|
|
6467
6577
|
{ type: "result", ok: true }
|
|
6468
6578
|
]
|
|
6469
6579
|
},
|
|
@@ -6481,7 +6591,7 @@ var init_conformance4 = __esm({
|
|
|
6481
6591
|
turnCompleted()
|
|
6482
6592
|
],
|
|
6483
6593
|
expected: [
|
|
6484
|
-
|
|
6594
|
+
startedSession(NEW_THREAD_ID),
|
|
6485
6595
|
{
|
|
6486
6596
|
type: "tool",
|
|
6487
6597
|
id: "ws1",
|
|
@@ -6498,6 +6608,7 @@ var init_conformance4 = __esm({
|
|
|
6498
6608
|
summary: "best pizza in nyc",
|
|
6499
6609
|
input: { query: "best pizza in nyc" }
|
|
6500
6610
|
},
|
|
6611
|
+
closingSession(NEW_THREAD_ID),
|
|
6501
6612
|
{ type: "result", ok: true }
|
|
6502
6613
|
]
|
|
6503
6614
|
},
|
|
@@ -6513,7 +6624,7 @@ var init_conformance4 = __esm({
|
|
|
6513
6624
|
turnCompleted()
|
|
6514
6625
|
],
|
|
6515
6626
|
expected: [
|
|
6516
|
-
|
|
6627
|
+
startedSession(NEW_THREAD_ID),
|
|
6517
6628
|
{
|
|
6518
6629
|
type: "tool",
|
|
6519
6630
|
id: "ws2",
|
|
@@ -6530,6 +6641,7 @@ var init_conformance4 = __esm({
|
|
|
6530
6641
|
summary: "",
|
|
6531
6642
|
input: { query: "" }
|
|
6532
6643
|
},
|
|
6644
|
+
closingSession(NEW_THREAD_ID),
|
|
6533
6645
|
{ type: "result", ok: true }
|
|
6534
6646
|
]
|
|
6535
6647
|
},
|
|
@@ -6560,7 +6672,7 @@ var init_conformance4 = __esm({
|
|
|
6560
6672
|
turnCompleted()
|
|
6561
6673
|
],
|
|
6562
6674
|
expected: [
|
|
6563
|
-
|
|
6675
|
+
startedSession(NEW_THREAD_ID),
|
|
6564
6676
|
{ type: "text", body: "Working on it.", terminal: false },
|
|
6565
6677
|
{
|
|
6566
6678
|
type: "tool",
|
|
@@ -6592,7 +6704,7 @@ var init_conformance4 = __esm({
|
|
|
6592
6704
|
turnFailed("provider exploded")
|
|
6593
6705
|
],
|
|
6594
6706
|
expected: [
|
|
6595
|
-
|
|
6707
|
+
startedSession(NEW_THREAD_ID),
|
|
6596
6708
|
{ type: "text", body: "Partial work.", terminal: false },
|
|
6597
6709
|
{ type: "runtime_notice", body: "provider exploded" },
|
|
6598
6710
|
{ type: "result", ok: false, reason: "error:provider exploded" }
|
|
@@ -6609,7 +6721,7 @@ var init_conformance4 = __esm({
|
|
|
6609
6721
|
fatalError("stream died")
|
|
6610
6722
|
],
|
|
6611
6723
|
expected: [
|
|
6612
|
-
|
|
6724
|
+
startedSession(NEW_THREAD_ID),
|
|
6613
6725
|
{ type: "text", body: "Starting.", terminal: false },
|
|
6614
6726
|
{ type: "runtime_notice", body: "stream died" },
|
|
6615
6727
|
{ type: "result", ok: false, reason: "error:stream died" }
|
|
@@ -6622,7 +6734,7 @@ var init_conformance4 = __esm({
|
|
|
6622
6734
|
request: makeRequest3(),
|
|
6623
6735
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
|
|
6624
6736
|
expected: [
|
|
6625
|
-
|
|
6737
|
+
startedSession(NEW_THREAD_ID),
|
|
6626
6738
|
{ type: "runtime_notice", body: "429 Too Many Requests" },
|
|
6627
6739
|
{ type: "result", ok: false, reason: "rate_limited" }
|
|
6628
6740
|
]
|
|
@@ -6632,7 +6744,7 @@ var init_conformance4 = __esm({
|
|
|
6632
6744
|
request: makeRequest3(),
|
|
6633
6745
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
|
|
6634
6746
|
expected: [
|
|
6635
|
-
|
|
6747
|
+
startedSession(NEW_THREAD_ID),
|
|
6636
6748
|
{ type: "runtime_notice", body: "503 Service Unavailable" },
|
|
6637
6749
|
{ type: "result", ok: false, reason: "server_error" }
|
|
6638
6750
|
]
|
|
@@ -6645,7 +6757,7 @@ var init_conformance4 = __esm({
|
|
|
6645
6757
|
turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
|
|
6646
6758
|
],
|
|
6647
6759
|
expected: [
|
|
6648
|
-
|
|
6760
|
+
startedSession(NEW_THREAD_ID),
|
|
6649
6761
|
{
|
|
6650
6762
|
type: "runtime_notice",
|
|
6651
6763
|
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
@@ -6658,7 +6770,7 @@ var init_conformance4 = __esm({
|
|
|
6658
6770
|
request: makeRequest3(),
|
|
6659
6771
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
|
|
6660
6772
|
expected: [
|
|
6661
|
-
|
|
6773
|
+
startedSession(NEW_THREAD_ID),
|
|
6662
6774
|
{ type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
|
|
6663
6775
|
{ type: "result", ok: false, reason: "auth_expired" }
|
|
6664
6776
|
]
|
|
@@ -6670,7 +6782,7 @@ var init_conformance4 = __esm({
|
|
|
6670
6782
|
request: makeRequest3(),
|
|
6671
6783
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
|
|
6672
6784
|
expected: [
|
|
6673
|
-
|
|
6785
|
+
startedSession(NEW_THREAD_ID),
|
|
6674
6786
|
{ type: "runtime_notice", body: "something local broke" },
|
|
6675
6787
|
{ type: "result", ok: false, reason: "error:something local broke" }
|
|
6676
6788
|
]
|
|
@@ -6683,7 +6795,7 @@ var init_conformance4 = __esm({
|
|
|
6683
6795
|
turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
|
|
6684
6796
|
],
|
|
6685
6797
|
expected: [
|
|
6686
|
-
|
|
6798
|
+
startedSession(NEW_THREAD_ID),
|
|
6687
6799
|
{ type: "runtime_notice", body: BOUNDED_NOTICE2 },
|
|
6688
6800
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
6689
6801
|
]
|
|
@@ -6694,26 +6806,32 @@ var init_conformance4 = __esm({
|
|
|
6694
6806
|
name: "empty failure text emits no runtime notice",
|
|
6695
6807
|
request: makeRequest3(),
|
|
6696
6808
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
|
|
6697
|
-
expected: [
|
|
6809
|
+
expected: [
|
|
6810
|
+
startedSession(NEW_THREAD_ID),
|
|
6811
|
+
{ type: "result", ok: false, reason: "error:unknown" }
|
|
6812
|
+
]
|
|
6698
6813
|
},
|
|
6699
6814
|
{
|
|
6700
6815
|
name: "whitespace-only failure text emits no runtime notice",
|
|
6701
6816
|
request: makeRequest3(),
|
|
6702
6817
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
|
|
6703
|
-
expected: [
|
|
6818
|
+
expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
|
|
6704
6819
|
},
|
|
6705
6820
|
{
|
|
6706
6821
|
name: "bare status token emits no runtime notice",
|
|
6707
6822
|
request: makeRequest3(),
|
|
6708
6823
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
|
|
6709
|
-
expected: [
|
|
6824
|
+
expected: [
|
|
6825
|
+
startedSession(NEW_THREAD_ID),
|
|
6826
|
+
{ type: "result", ok: false, reason: "rate_limited" }
|
|
6827
|
+
]
|
|
6710
6828
|
},
|
|
6711
6829
|
{
|
|
6712
6830
|
name: "single-word provider sentence emits a runtime notice",
|
|
6713
6831
|
request: makeRequest3(),
|
|
6714
6832
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
|
|
6715
6833
|
expected: [
|
|
6716
|
-
|
|
6834
|
+
startedSession(NEW_THREAD_ID),
|
|
6717
6835
|
{ type: "runtime_notice", body: "Unavailable." },
|
|
6718
6836
|
{ type: "result", ok: false, reason: "error:Unavailable." }
|
|
6719
6837
|
]
|
|
@@ -6741,7 +6859,7 @@ var init_conformance4 = __esm({
|
|
|
6741
6859
|
turnCompleted()
|
|
6742
6860
|
],
|
|
6743
6861
|
expected: [
|
|
6744
|
-
|
|
6862
|
+
startedSession(NEW_THREAD_ID),
|
|
6745
6863
|
{
|
|
6746
6864
|
type: "result",
|
|
6747
6865
|
ok: false,
|
|
@@ -6761,8 +6879,9 @@ var init_conformance4 = __esm({
|
|
|
6761
6879
|
turnCompleted()
|
|
6762
6880
|
],
|
|
6763
6881
|
expected: [
|
|
6764
|
-
|
|
6882
|
+
startedSession(NEW_THREAD_ID),
|
|
6765
6883
|
{ type: "text", body: "All good now.", terminal: true },
|
|
6884
|
+
closingSession(NEW_THREAD_ID),
|
|
6766
6885
|
{ type: "result", ok: true }
|
|
6767
6886
|
]
|
|
6768
6887
|
},
|
|
@@ -6774,7 +6893,11 @@ var init_conformance4 = __esm({
|
|
|
6774
6893
|
name: "zero-content success (empty runtime result)",
|
|
6775
6894
|
request: makeRequest3(),
|
|
6776
6895
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
|
|
6777
|
-
expected: [
|
|
6896
|
+
expected: [
|
|
6897
|
+
startedSession(NEW_THREAD_ID),
|
|
6898
|
+
closingSession(NEW_THREAD_ID),
|
|
6899
|
+
{ type: "result", ok: true }
|
|
6900
|
+
]
|
|
6778
6901
|
},
|
|
6779
6902
|
{
|
|
6780
6903
|
// Empty-final: a clean turn that ended on a tool call with no closing message.
|
|
@@ -6799,7 +6922,7 @@ var init_conformance4 = __esm({
|
|
|
6799
6922
|
turnCompleted()
|
|
6800
6923
|
],
|
|
6801
6924
|
expected: [
|
|
6802
|
-
|
|
6925
|
+
startedSession(NEW_THREAD_ID),
|
|
6803
6926
|
{
|
|
6804
6927
|
type: "tool",
|
|
6805
6928
|
id: "t1",
|
|
@@ -6817,6 +6940,7 @@ var init_conformance4 = __esm({
|
|
|
6817
6940
|
input: { changes: [{ path: "a.md", kind: "add" }] },
|
|
6818
6941
|
result: [{ path: "a.md", kind: "add" }]
|
|
6819
6942
|
},
|
|
6943
|
+
closingSession(NEW_THREAD_ID),
|
|
6820
6944
|
{ type: "result", ok: true }
|
|
6821
6945
|
]
|
|
6822
6946
|
},
|
|
@@ -6832,9 +6956,10 @@ var init_conformance4 = __esm({
|
|
|
6832
6956
|
turnCompleted()
|
|
6833
6957
|
],
|
|
6834
6958
|
expected: [
|
|
6835
|
-
|
|
6959
|
+
startedSession(NEW_THREAD_ID),
|
|
6836
6960
|
{ type: "thinking", text: "Let me check the notes." },
|
|
6837
6961
|
{ type: "text", body: "All set.", terminal: true },
|
|
6962
|
+
closingSession(NEW_THREAD_ID),
|
|
6838
6963
|
{ type: "result", ok: true }
|
|
6839
6964
|
]
|
|
6840
6965
|
},
|
|
@@ -6852,8 +6977,9 @@ var init_conformance4 = __esm({
|
|
|
6852
6977
|
}),
|
|
6853
6978
|
nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
|
|
6854
6979
|
expected: [
|
|
6855
|
-
|
|
6980
|
+
startedSession(NEW_THREAD_ID),
|
|
6856
6981
|
{ type: "text", body: "Done.", terminal: true },
|
|
6982
|
+
closingSession(NEW_THREAD_ID),
|
|
6857
6983
|
{
|
|
6858
6984
|
type: "result",
|
|
6859
6985
|
ok: true,
|
|
@@ -6875,7 +7001,8 @@ var init_conformance4 = __esm({
|
|
|
6875
7001
|
threadId: "th1",
|
|
6876
7002
|
cwd: DIR2,
|
|
6877
7003
|
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
6878
|
-
usageBaseline: null
|
|
7004
|
+
usageBaseline: null,
|
|
7005
|
+
compactions: 0
|
|
6879
7006
|
})
|
|
6880
7007
|
}),
|
|
6881
7008
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
@@ -6897,8 +7024,9 @@ var init_conformance4 = __esm({
|
|
|
6897
7024
|
}),
|
|
6898
7025
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
6899
7026
|
expected: [
|
|
6900
|
-
|
|
7027
|
+
startedSession("th1"),
|
|
6901
7028
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7029
|
+
closingSession("th1"),
|
|
6902
7030
|
{ type: "result", ok: true }
|
|
6903
7031
|
]
|
|
6904
7032
|
},
|
|
@@ -6920,8 +7048,9 @@ var init_conformance4 = __esm({
|
|
|
6920
7048
|
}),
|
|
6921
7049
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
6922
7050
|
expected: [
|
|
6923
|
-
|
|
7051
|
+
startedSession("th1"),
|
|
6924
7052
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7053
|
+
closingSession("th1"),
|
|
6925
7054
|
{ type: "result", ok: true }
|
|
6926
7055
|
]
|
|
6927
7056
|
},
|
|
@@ -6947,8 +7076,9 @@ var init_conformance4 = __esm({
|
|
|
6947
7076
|
turnCompleted()
|
|
6948
7077
|
],
|
|
6949
7078
|
expected: [
|
|
6950
|
-
|
|
7079
|
+
startedSession(NEW_THREAD_ID, DIR2, true),
|
|
6951
7080
|
{ type: "text", body: "Fresh start.", terminal: true },
|
|
7081
|
+
closingSession(NEW_THREAD_ID),
|
|
6952
7082
|
{ type: "result", ok: true }
|
|
6953
7083
|
]
|
|
6954
7084
|
}
|
|
@@ -6976,6 +7106,7 @@ var init_src = __esm({
|
|
|
6976
7106
|
init_empty_result();
|
|
6977
7107
|
init_failure();
|
|
6978
7108
|
init_turn_request();
|
|
7109
|
+
init_standing();
|
|
6979
7110
|
init_conformance();
|
|
6980
7111
|
init_transcript();
|
|
6981
7112
|
init_transcript_sink();
|
|
@@ -8529,6 +8660,7 @@ function buildCompanionTurnRequest(params) {
|
|
|
8529
8660
|
const { turnContext: t } = params;
|
|
8530
8661
|
return {
|
|
8531
8662
|
systemPrompt: t.systemPrompt,
|
|
8663
|
+
...t.standing ? { standing: t.standing } : {},
|
|
8532
8664
|
prompt: t.prompt,
|
|
8533
8665
|
content: t.content,
|
|
8534
8666
|
config: t.config,
|
|
@@ -8658,6 +8790,68 @@ var init_codex_images = __esm({
|
|
|
8658
8790
|
}
|
|
8659
8791
|
});
|
|
8660
8792
|
|
|
8793
|
+
// src/codex-compactions.ts
|
|
8794
|
+
import { createReadStream } from "fs";
|
|
8795
|
+
import { readdir } from "fs/promises";
|
|
8796
|
+
import { homedir as homedir8 } from "os";
|
|
8797
|
+
import { join as join23 } from "path";
|
|
8798
|
+
import { createInterface as createInterface2 } from "readline";
|
|
8799
|
+
function createCodexCompactionCounter(codexHome = process.env.CODEX_HOME || join23(homedir8(), ".codex")) {
|
|
8800
|
+
return async (threadId) => {
|
|
8801
|
+
try {
|
|
8802
|
+
const rollout = await findRollout(codexHome, threadId);
|
|
8803
|
+
return rollout ? await countCompactions(rollout) : null;
|
|
8804
|
+
} catch {
|
|
8805
|
+
return null;
|
|
8806
|
+
}
|
|
8807
|
+
};
|
|
8808
|
+
}
|
|
8809
|
+
async function findRollout(codexHome, threadId) {
|
|
8810
|
+
const id = UUID_V7.exec(threadId);
|
|
8811
|
+
if (!id) return null;
|
|
8812
|
+
const createdAt = parseInt(`${id[1]}${id[2]}`, 16);
|
|
8813
|
+
const suffix = `-${threadId}.jsonl`;
|
|
8814
|
+
for (const offset of [0, -DAY_MS, DAY_MS]) {
|
|
8815
|
+
const day = new Date(createdAt + offset);
|
|
8816
|
+
const dir2 = join23(
|
|
8817
|
+
codexHome,
|
|
8818
|
+
"sessions",
|
|
8819
|
+
String(day.getUTCFullYear()),
|
|
8820
|
+
String(day.getUTCMonth() + 1).padStart(2, "0"),
|
|
8821
|
+
String(day.getUTCDate()).padStart(2, "0")
|
|
8822
|
+
);
|
|
8823
|
+
let names;
|
|
8824
|
+
try {
|
|
8825
|
+
names = await readdir(dir2);
|
|
8826
|
+
} catch {
|
|
8827
|
+
continue;
|
|
8828
|
+
}
|
|
8829
|
+
const name = names.find((n) => n.startsWith("rollout-") && n.endsWith(suffix));
|
|
8830
|
+
if (name) return join23(dir2, name);
|
|
8831
|
+
}
|
|
8832
|
+
return null;
|
|
8833
|
+
}
|
|
8834
|
+
async function countCompactions(rollout) {
|
|
8835
|
+
const lines2 = createInterface2({
|
|
8836
|
+
input: createReadStream(rollout, { encoding: "utf8" }),
|
|
8837
|
+
crlfDelay: Infinity
|
|
8838
|
+
});
|
|
8839
|
+
let count = 0;
|
|
8840
|
+
for await (const line of lines2) {
|
|
8841
|
+
if (line.slice(0, RECORD_HEAD_CHARS).includes('"type":"compacted"')) count += 1;
|
|
8842
|
+
}
|
|
8843
|
+
return count;
|
|
8844
|
+
}
|
|
8845
|
+
var DAY_MS, UUID_V7, RECORD_HEAD_CHARS;
|
|
8846
|
+
var init_codex_compactions = __esm({
|
|
8847
|
+
"src/codex-compactions.ts"() {
|
|
8848
|
+
"use strict";
|
|
8849
|
+
DAY_MS = 864e5;
|
|
8850
|
+
UUID_V7 = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8851
|
+
RECORD_HEAD_CHARS = 200;
|
|
8852
|
+
}
|
|
8853
|
+
});
|
|
8854
|
+
|
|
8661
8855
|
// src/turn-seq-floor.ts
|
|
8662
8856
|
function resolveSeqFloor(sources, ctx2) {
|
|
8663
8857
|
const { serverFloor, outboxFloor } = sources;
|
|
@@ -8766,15 +8960,15 @@ var init_prepare_failure = __esm({
|
|
|
8766
8960
|
|
|
8767
8961
|
// src/prepared.ts
|
|
8768
8962
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync13, rmSync as rmSync9, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
|
|
8769
|
-
import { join as
|
|
8963
|
+
import { join as join24 } from "path";
|
|
8770
8964
|
function dirFor(workspaceId) {
|
|
8771
|
-
return
|
|
8965
|
+
return join24(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
8772
8966
|
}
|
|
8773
8967
|
function conversationDir(workspaceId, conversationId) {
|
|
8774
|
-
return
|
|
8968
|
+
return join24(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
8775
8969
|
}
|
|
8776
8970
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
8777
|
-
return
|
|
8971
|
+
return join24(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
8778
8972
|
}
|
|
8779
8973
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
8780
8974
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -8812,9 +9006,9 @@ var init_prepared = __esm({
|
|
|
8812
9006
|
|
|
8813
9007
|
// src/transcript-writer.ts
|
|
8814
9008
|
import { appendFileSync, chmodSync as chmodSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync14, readdirSync as readdirSync2, rmSync as rmSync10 } from "fs";
|
|
8815
|
-
import { basename as basename3, dirname as dirname12, join as
|
|
9009
|
+
import { basename as basename3, dirname as dirname12, join as join25 } from "path";
|
|
8816
9010
|
function transcriptsDir() {
|
|
8817
|
-
return
|
|
9011
|
+
return join25(runnerStateDir(), "transcripts");
|
|
8818
9012
|
}
|
|
8819
9013
|
function fileName(meta) {
|
|
8820
9014
|
const d = new Date(meta.ts);
|
|
@@ -8840,7 +9034,7 @@ function pruneOld(dir2, retain) {
|
|
|
8840
9034
|
const drop = files.sort().slice(0, files.length - retain);
|
|
8841
9035
|
for (const f of drop) {
|
|
8842
9036
|
try {
|
|
8843
|
-
rmSync10(
|
|
9037
|
+
rmSync10(join25(dir2, f), { force: true });
|
|
8844
9038
|
} catch {
|
|
8845
9039
|
}
|
|
8846
9040
|
}
|
|
@@ -8858,7 +9052,7 @@ var init_transcript_writer = __esm({
|
|
|
8858
9052
|
onWarn;
|
|
8859
9053
|
constructor(dir2, meta, onWarn) {
|
|
8860
9054
|
this.onWarn = onWarn;
|
|
8861
|
-
this.path =
|
|
9055
|
+
this.path = join25(dir2, fileName(meta));
|
|
8862
9056
|
try {
|
|
8863
9057
|
mkdirSync14(dir2, { recursive: true });
|
|
8864
9058
|
try {
|
|
@@ -8887,10 +9081,10 @@ var init_transcript_writer = __esm({
|
|
|
8887
9081
|
preserveAnomaly() {
|
|
8888
9082
|
if (this.broken) return;
|
|
8889
9083
|
try {
|
|
8890
|
-
const dir2 =
|
|
9084
|
+
const dir2 = join25(dirname12(this.path), "anomalies");
|
|
8891
9085
|
mkdirSync14(dir2, { recursive: true, mode: 448 });
|
|
8892
9086
|
chmodSync5(dir2, 448);
|
|
8893
|
-
const target =
|
|
9087
|
+
const target = join25(dir2, basename3(this.path));
|
|
8894
9088
|
copyFileSync2(this.path, target);
|
|
8895
9089
|
chmodSync5(target, 384);
|
|
8896
9090
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9190,6 +9384,7 @@ var init_turn_execution = __esm({
|
|
|
9190
9384
|
init_bundled_binaries();
|
|
9191
9385
|
init_codex_instructions();
|
|
9192
9386
|
init_codex_images();
|
|
9387
|
+
init_codex_compactions();
|
|
9193
9388
|
init_config();
|
|
9194
9389
|
init_api_error_shape();
|
|
9195
9390
|
init_errors();
|
|
@@ -9875,6 +10070,8 @@ var init_turn_execution = __esm({
|
|
|
9875
10070
|
turnId: this.turnId,
|
|
9876
10071
|
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
9877
10072
|
}),
|
|
10073
|
+
// Whether a resumed thread compacted since its standing preamble was sent.
|
|
10074
|
+
countCompactions: createCodexCompactionCounter(),
|
|
9878
10075
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
9879
10076
|
})
|
|
9880
10077
|
);
|
|
@@ -9935,6 +10132,9 @@ var init_turn_execution = __esm({
|
|
|
9935
10132
|
);
|
|
9936
10133
|
return {
|
|
9937
10134
|
systemPrompt: context.systemPrompt,
|
|
10135
|
+
// Replaced even when absent, so the segments can't outlive the
|
|
10136
|
+
// `systemPrompt` they were split from.
|
|
10137
|
+
standing: context.standing,
|
|
9938
10138
|
prompt: context.prompt,
|
|
9939
10139
|
content: context.content
|
|
9940
10140
|
};
|
|
@@ -10327,7 +10527,7 @@ var init_turn_execution = __esm({
|
|
|
10327
10527
|
// src/dispatcher.ts
|
|
10328
10528
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10329
10529
|
import { existsSync as existsSync15, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
10330
|
-
import { join as
|
|
10530
|
+
import { join as join26 } from "path";
|
|
10331
10531
|
function checkoutState(cwd) {
|
|
10332
10532
|
if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
|
|
10333
10533
|
if (!existsSync15(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
|
|
@@ -10340,7 +10540,7 @@ function checkoutState(cwd) {
|
|
|
10340
10540
|
if (entries.length === 0) {
|
|
10341
10541
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10342
10542
|
}
|
|
10343
|
-
const gitPath =
|
|
10543
|
+
const gitPath = join26(cwd, ".git");
|
|
10344
10544
|
if (!existsSync15(gitPath)) return { ok: true, reason: "usable" };
|
|
10345
10545
|
let stat;
|
|
10346
10546
|
try {
|
|
@@ -10348,7 +10548,7 @@ function checkoutState(cwd) {
|
|
|
10348
10548
|
} catch (error) {
|
|
10349
10549
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10350
10550
|
}
|
|
10351
|
-
if (stat.isDirectory() && !existsSync15(
|
|
10551
|
+
if (stat.isDirectory() && !existsSync15(join26(gitPath, "HEAD")))
|
|
10352
10552
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10353
10553
|
return { ok: true, reason: "usable" };
|
|
10354
10554
|
}
|
|
@@ -10726,8 +10926,8 @@ import {
|
|
|
10726
10926
|
} from "fs";
|
|
10727
10927
|
import { createRequire as createRequire3 } from "module";
|
|
10728
10928
|
import { isIP } from "net";
|
|
10729
|
-
import { homedir as
|
|
10730
|
-
import { dirname as dirname14, join as
|
|
10929
|
+
import { homedir as homedir9, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo3 } from "os";
|
|
10930
|
+
import { dirname as dirname14, join as join30, sep as sep5 } from "path";
|
|
10731
10931
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
10732
10932
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
10733
10933
|
function accountHome() {
|
|
@@ -10761,7 +10961,7 @@ async function resolvedAddresses(baseUrl) {
|
|
|
10761
10961
|
function companionPackageRoot(modulePath) {
|
|
10762
10962
|
for (let dir2 = dirname14(modulePath); ; dir2 = dirname14(dir2)) {
|
|
10763
10963
|
try {
|
|
10764
|
-
const manifest = JSON.parse(readFileSync16(
|
|
10964
|
+
const manifest = JSON.parse(readFileSync16(join30(dir2, "package.json"), "utf8"));
|
|
10765
10965
|
if (manifest.name === "@cabane/companion") return dir2;
|
|
10766
10966
|
} catch {
|
|
10767
10967
|
}
|
|
@@ -10838,22 +11038,22 @@ function companionStoreDenies(input) {
|
|
|
10838
11038
|
const always = [
|
|
10839
11039
|
cabaneDir2,
|
|
10840
11040
|
...homes.flatMap((home) => [
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
11041
|
+
join30(home, ".cabane"),
|
|
11042
|
+
join30(home, "*", ".cabane"),
|
|
11043
|
+
join30(home, "*", "*", ".cabane")
|
|
10844
11044
|
]),
|
|
10845
11045
|
// A glob anchored at the root has no directory to start from.
|
|
10846
|
-
...dirname14(parent) === parent ? [] : [
|
|
11046
|
+
...dirname14(parent) === parent ? [] : [join30(parent, "*", ".cabane")]
|
|
10847
11047
|
];
|
|
10848
11048
|
const companionHome = dirname14(cabaneDir2);
|
|
10849
|
-
const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [
|
|
11049
|
+
const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [join30(home, ".claude"), join30(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
|
|
10850
11050
|
return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
|
|
10851
11051
|
}
|
|
10852
11052
|
function companionStoreWatch(log2) {
|
|
10853
11053
|
return new CompanionStoreWatch({
|
|
10854
11054
|
log: log2,
|
|
10855
11055
|
roots: () => {
|
|
10856
|
-
const home =
|
|
11056
|
+
const home = homedir9();
|
|
10857
11057
|
return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
|
|
10858
11058
|
}
|
|
10859
11059
|
});
|
|
@@ -10863,7 +11063,7 @@ function isAtOrUnder(path, dir2) {
|
|
|
10863
11063
|
}
|
|
10864
11064
|
function expandHome(path, home) {
|
|
10865
11065
|
if (path === "~") return home;
|
|
10866
|
-
return path.startsWith("~/") ?
|
|
11066
|
+
return path.startsWith("~/") ? join30(home, path.slice(2)) : path;
|
|
10867
11067
|
}
|
|
10868
11068
|
function stripPort(pattern) {
|
|
10869
11069
|
return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
|
|
@@ -10881,8 +11081,8 @@ function patternsOverlap(a, b) {
|
|
|
10881
11081
|
}
|
|
10882
11082
|
function harnessSandboxConfig(input) {
|
|
10883
11083
|
const { policy } = input;
|
|
10884
|
-
const claudeDir = input.claudeConfigDir ??
|
|
10885
|
-
const codexDir = input.codexHome ??
|
|
11084
|
+
const claudeDir = input.claudeConfigDir ?? join30(input.home, ".claude");
|
|
11085
|
+
const codexDir = input.codexHome ?? join30(input.home, ".codex");
|
|
10886
11086
|
const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
|
|
10887
11087
|
const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
|
|
10888
11088
|
const cabane2 = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
|
|
@@ -10899,7 +11099,7 @@ function harnessSandboxConfig(input) {
|
|
|
10899
11099
|
foundStores: input.foundStores,
|
|
10900
11100
|
ownState: [claudeDir, codexDir]
|
|
10901
11101
|
}),
|
|
10902
|
-
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) =>
|
|
11102
|
+
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join30(home, p))),
|
|
10903
11103
|
...expand(policy.filesystem?.denyRead)
|
|
10904
11104
|
];
|
|
10905
11105
|
const strict = policy.filesystem?.mode === "strict";
|
|
@@ -10907,7 +11107,7 @@ function harnessSandboxConfig(input) {
|
|
|
10907
11107
|
const allowRead = strict ? [
|
|
10908
11108
|
...input.cwd ? [input.cwd] : [],
|
|
10909
11109
|
claudeDir,
|
|
10910
|
-
|
|
11110
|
+
join30(input.home, ".claude.json"),
|
|
10911
11111
|
codexDir,
|
|
10912
11112
|
...input.runtimePaths ?? [],
|
|
10913
11113
|
...expand(policy.filesystem?.allowRead)
|
|
@@ -10947,7 +11147,7 @@ function harnessSandboxConfig(input) {
|
|
|
10947
11147
|
...input.cwd ? [input.cwd] : [],
|
|
10948
11148
|
input.tmp,
|
|
10949
11149
|
claudeDir,
|
|
10950
|
-
|
|
11150
|
+
join30(input.home, ".claude.json"),
|
|
10951
11151
|
codexDir,
|
|
10952
11152
|
...expand(policy.filesystem?.allowWrite)
|
|
10953
11153
|
],
|
|
@@ -10998,7 +11198,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
10998
11198
|
baseUrl: input.baseUrl,
|
|
10999
11199
|
resolved: await resolvedAddresses(input.baseUrl),
|
|
11000
11200
|
ownAddresses: ownAddresses(),
|
|
11001
|
-
home:
|
|
11201
|
+
home: homedir9(),
|
|
11002
11202
|
accountHome: accountHome(),
|
|
11003
11203
|
tmp: tmpdir4(),
|
|
11004
11204
|
cabaneDir: cabaneDir(),
|
|
@@ -11049,9 +11249,9 @@ async function prepareHarnessSandbox(input) {
|
|
|
11049
11249
|
} finally {
|
|
11050
11250
|
for (const name of held) delete process.env[name];
|
|
11051
11251
|
}
|
|
11052
|
-
mkdirSync17(
|
|
11053
|
-
const dir2 = mkdtempSync(
|
|
11054
|
-
const script =
|
|
11252
|
+
mkdirSync17(join30(cabaneDir(), "sandbox"), { recursive: true });
|
|
11253
|
+
const dir2 = mkdtempSync(join30(cabaneDir(), "sandbox", "turn-"));
|
|
11254
|
+
const script = join30(dir2, "exec-sandboxed");
|
|
11055
11255
|
writeFileSync10(
|
|
11056
11256
|
script,
|
|
11057
11257
|
[
|
|
@@ -11067,7 +11267,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
11067
11267
|
return {
|
|
11068
11268
|
wrap: (command, args) => ({ command: script, args: [command, ...args] }),
|
|
11069
11269
|
executableFor: (real) => {
|
|
11070
|
-
const shim =
|
|
11270
|
+
const shim = join30(dir2, `exec-${++shims}`);
|
|
11071
11271
|
writeFileSync10(shim, `#!/bin/bash
|
|
11072
11272
|
exec ${shellQuote(script)} ${shellQuote(real)} "$@"
|
|
11073
11273
|
`);
|
|
@@ -11160,7 +11360,7 @@ import {
|
|
|
11160
11360
|
rmSync as rmSync15,
|
|
11161
11361
|
writeFileSync as writeFileSync11
|
|
11162
11362
|
} from "fs";
|
|
11163
|
-
import { join as
|
|
11363
|
+
import { join as join31 } from "path";
|
|
11164
11364
|
var MAX_ENTRIES, MAX_AGE_MS, Outbox;
|
|
11165
11365
|
var init_outbox = __esm({
|
|
11166
11366
|
"src/outbox.ts"() {
|
|
@@ -11178,10 +11378,10 @@ var init_outbox = __esm({
|
|
|
11178
11378
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
11179
11379
|
// cases route writes at the right tmpdir.
|
|
11180
11380
|
dir() {
|
|
11181
|
-
return
|
|
11381
|
+
return join31(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
11182
11382
|
}
|
|
11183
11383
|
fileFor(turnId, seq) {
|
|
11184
|
-
return
|
|
11384
|
+
return join31(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
11185
11385
|
}
|
|
11186
11386
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
11187
11387
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -11223,7 +11423,7 @@ var init_outbox = __esm({
|
|
|
11223
11423
|
const entries = [];
|
|
11224
11424
|
for (const name of names) {
|
|
11225
11425
|
if (!name.endsWith(".json")) continue;
|
|
11226
|
-
const full =
|
|
11426
|
+
const full = join31(dir2, name);
|
|
11227
11427
|
try {
|
|
11228
11428
|
const parsed = JSON.parse(readFileSync17(full, "utf8"));
|
|
11229
11429
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -11352,7 +11552,7 @@ var init_image_manifest = __esm({
|
|
|
11352
11552
|
|
|
11353
11553
|
// src/runner/exec-shim.ts
|
|
11354
11554
|
import { chmodSync as chmodSync8, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
11355
|
-
import { join as
|
|
11555
|
+
import { join as join35 } from "path";
|
|
11356
11556
|
function shellQuote2(value) {
|
|
11357
11557
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11358
11558
|
}
|
|
@@ -11362,10 +11562,10 @@ function createShimDir() {
|
|
|
11362
11562
|
return {
|
|
11363
11563
|
write(argv) {
|
|
11364
11564
|
if (!dir2) {
|
|
11365
|
-
mkdirSync20(
|
|
11366
|
-
dir2 = mkdtempSync2(
|
|
11565
|
+
mkdirSync20(join35(cabaneDir(), "runner"), { recursive: true });
|
|
11566
|
+
dir2 = mkdtempSync2(join35(cabaneDir(), "runner", "turn-"));
|
|
11367
11567
|
}
|
|
11368
|
-
const path =
|
|
11568
|
+
const path = join35(dir2, `exec-${++count}`);
|
|
11369
11569
|
writeFileSync13(path, `#!/bin/sh
|
|
11370
11570
|
exec ${argv.map(shellQuote2).join(" ")} "$@"
|
|
11371
11571
|
`);
|
|
@@ -11386,7 +11586,7 @@ var init_exec_shim = __esm({
|
|
|
11386
11586
|
});
|
|
11387
11587
|
|
|
11388
11588
|
// src/runner/launcher-channel.ts
|
|
11389
|
-
import { createInterface as
|
|
11589
|
+
import { createInterface as createInterface3 } from "readline";
|
|
11390
11590
|
function openLauncherChannel(stdin, emit, onLauncherGone) {
|
|
11391
11591
|
const waiting = /* @__PURE__ */ new Map();
|
|
11392
11592
|
const replies = /* @__PURE__ */ new Map();
|
|
@@ -11394,7 +11594,7 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
|
|
|
11394
11594
|
let resolveFirst;
|
|
11395
11595
|
let gotFirst = false;
|
|
11396
11596
|
const first = new Promise((resolve2) => resolveFirst = resolve2);
|
|
11397
|
-
const lines2 =
|
|
11597
|
+
const lines2 = createInterface3({ input: stdin, crlfDelay: Infinity });
|
|
11398
11598
|
lines2.on("line", (line) => {
|
|
11399
11599
|
if (!gotFirst) {
|
|
11400
11600
|
gotFirst = true;
|
|
@@ -15754,7 +15954,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
15754
15954
|
import { createHash as createHash5, randomUUID as randomUUID5 } from "crypto";
|
|
15755
15955
|
import { constants as constants3, tmpdir as tmpdir3 } from "os";
|
|
15756
15956
|
import {
|
|
15757
|
-
createReadStream,
|
|
15957
|
+
createReadStream as createReadStream2,
|
|
15758
15958
|
createWriteStream as createWriteStream2,
|
|
15759
15959
|
existsSync as existsSync18,
|
|
15760
15960
|
mkdirSync as mkdirSync16,
|
|
@@ -15765,7 +15965,7 @@ import {
|
|
|
15765
15965
|
statSync as statSync4,
|
|
15766
15966
|
writeFileSync as writeFileSync9
|
|
15767
15967
|
} from "fs";
|
|
15768
|
-
import { join as
|
|
15968
|
+
import { join as join29 } from "path";
|
|
15769
15969
|
import { Writable } from "stream";
|
|
15770
15970
|
|
|
15771
15971
|
// src/providers/docker-cli.ts
|
|
@@ -15838,7 +16038,7 @@ import {
|
|
|
15838
16038
|
renameSync as renameSync10,
|
|
15839
16039
|
rmSync as rmSync12
|
|
15840
16040
|
} from "fs";
|
|
15841
|
-
import { join as
|
|
16041
|
+
import { join as join27 } from "path";
|
|
15842
16042
|
function routes(t) {
|
|
15843
16043
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
15844
16044
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -15884,7 +16084,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15884
16084
|
const staged = readdirSync4(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
15885
16085
|
let entry = {};
|
|
15886
16086
|
try {
|
|
15887
|
-
entry = JSON.parse(readFileSync14(
|
|
16087
|
+
entry = JSON.parse(readFileSync14(join27(from, name), "utf8"));
|
|
15888
16088
|
} catch {
|
|
15889
16089
|
}
|
|
15890
16090
|
return { name, entry };
|
|
@@ -15897,7 +16097,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15897
16097
|
let published = 0;
|
|
15898
16098
|
for (const { name } of staged) {
|
|
15899
16099
|
try {
|
|
15900
|
-
move(
|
|
16100
|
+
move(join27(from, name), join27(to, name));
|
|
15901
16101
|
} catch {
|
|
15902
16102
|
return { published, complete: false };
|
|
15903
16103
|
}
|
|
@@ -15910,7 +16110,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15910
16110
|
// src/providers/runner-base.ts
|
|
15911
16111
|
import { createHash as createHash4 } from "crypto";
|
|
15912
16112
|
import { existsSync as existsSync17, realpathSync as realpathSync6 } from "fs";
|
|
15913
|
-
import { dirname as dirname13, join as
|
|
16113
|
+
import { dirname as dirname13, join as join28, sep as sep4 } from "path";
|
|
15914
16114
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15915
16115
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
15916
16116
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -15958,11 +16158,11 @@ async function ensureBaseImage(docker, image, log2) {
|
|
|
15958
16158
|
function ownInstall(moduleFile = fileURLToPath6(import.meta.url)) {
|
|
15959
16159
|
let dir2 = dirname13(moduleFile);
|
|
15960
16160
|
for (let i = 0; i < 4; i++) {
|
|
15961
|
-
if (existsSync17(
|
|
16161
|
+
if (existsSync17(join28(dir2, "dist", "cli.js")) && existsSync17(join28(dir2, "package.json"))) break;
|
|
15962
16162
|
dir2 = dirname13(dir2);
|
|
15963
16163
|
}
|
|
15964
|
-
if (!existsSync17(
|
|
15965
|
-
const sdk =
|
|
16164
|
+
if (!existsSync17(join28(dir2, "dist", "cli.js"))) return null;
|
|
16165
|
+
const sdk = join28(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
15966
16166
|
if (!existsSync17(sdk)) return null;
|
|
15967
16167
|
const root = realpathSync6(dir2);
|
|
15968
16168
|
return realpathSync6(sdk).startsWith(root + sep4) ? root : null;
|
|
@@ -16101,39 +16301,39 @@ function networkCreateError(stderr) {
|
|
|
16101
16301
|
}
|
|
16102
16302
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
16103
16303
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
16104
|
-
return
|
|
16304
|
+
return join29(deviceDir, "attempts", attemptId, "session.tar");
|
|
16105
16305
|
}
|
|
16106
16306
|
function pruneOtherAttempts(deviceDir, pair2, keep) {
|
|
16107
|
-
const root =
|
|
16307
|
+
const root = join29(deviceDir, "attempts");
|
|
16108
16308
|
if (!existsSync18(root)) return;
|
|
16109
16309
|
for (const id of readdirSync5(root)) {
|
|
16110
16310
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
16111
16311
|
let pairOf = "";
|
|
16112
16312
|
try {
|
|
16113
|
-
pairOf = readFileSync15(
|
|
16313
|
+
pairOf = readFileSync15(join29(root, id, "pair"), "utf8");
|
|
16114
16314
|
} catch {
|
|
16115
16315
|
continue;
|
|
16116
16316
|
}
|
|
16117
16317
|
if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
|
|
16118
|
-
rmSync13(
|
|
16318
|
+
rmSync13(join29(root, id), { recursive: true, force: true });
|
|
16119
16319
|
}
|
|
16120
16320
|
}
|
|
16121
16321
|
}
|
|
16122
16322
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
16123
16323
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
16124
|
-
const root =
|
|
16324
|
+
const root = join29(deviceDir, "attempts");
|
|
16125
16325
|
if (!existsSync18(root)) return [];
|
|
16126
16326
|
const removed = [];
|
|
16127
16327
|
for (const id of readdirSync5(root)) {
|
|
16128
16328
|
if (!UUID_RE.test(id)) continue;
|
|
16129
16329
|
let kept;
|
|
16130
16330
|
try {
|
|
16131
|
-
kept = statSync4(
|
|
16331
|
+
kept = statSync4(join29(root, id)).mtimeMs;
|
|
16132
16332
|
} catch {
|
|
16133
16333
|
continue;
|
|
16134
16334
|
}
|
|
16135
16335
|
if (now - kept < maxAgeMs) continue;
|
|
16136
|
-
rmSync13(
|
|
16336
|
+
rmSync13(join29(root, id), { recursive: true, force: true });
|
|
16137
16337
|
removed.push(id);
|
|
16138
16338
|
}
|
|
16139
16339
|
return removed;
|
|
@@ -16334,11 +16534,11 @@ function createDockerProvider(opts) {
|
|
|
16334
16534
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
16335
16535
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
16336
16536
|
};
|
|
16337
|
-
const socketRoot = opts.socketRoot ??
|
|
16537
|
+
const socketRoot = opts.socketRoot ?? join29(
|
|
16338
16538
|
process.env.XDG_RUNTIME_DIR && existsSync18(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
16339
16539
|
`cabane-runners-${opts.deviceKey}`
|
|
16340
16540
|
);
|
|
16341
|
-
const socketDir = (name) =>
|
|
16541
|
+
const socketDir = (name) => join29(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
|
|
16342
16542
|
const create = async (name, attempt) => {
|
|
16343
16543
|
mkdirSync16(socketRoot, { recursive: true, mode: 448 });
|
|
16344
16544
|
mkdirSync16(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -16480,7 +16680,7 @@ function createDockerProvider(opts) {
|
|
|
16480
16680
|
}
|
|
16481
16681
|
};
|
|
16482
16682
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
16483
|
-
const pendingPath = (name) =>
|
|
16683
|
+
const pendingPath = (name) => join29(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
16484
16684
|
const readPending = (name) => {
|
|
16485
16685
|
try {
|
|
16486
16686
|
const ids = JSON.parse(readFileSync15(pendingPath(name), "utf8"));
|
|
@@ -16494,12 +16694,12 @@ function createDockerProvider(opts) {
|
|
|
16494
16694
|
rmSync13(pendingPath(name), { force: true });
|
|
16495
16695
|
return;
|
|
16496
16696
|
}
|
|
16497
|
-
mkdirSync16(
|
|
16697
|
+
mkdirSync16(join29(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
16498
16698
|
writeFileSync9(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
16499
16699
|
};
|
|
16500
16700
|
const keepSession = async (name, attempt, attemptId) => {
|
|
16501
16701
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16502
|
-
mkdirSync16(
|
|
16702
|
+
mkdirSync16(join29(archive, ".."), { recursive: true });
|
|
16503
16703
|
const partial = `${archive}.${process.pid}.partial`;
|
|
16504
16704
|
const file = new CappedFile(partial, sessionCap);
|
|
16505
16705
|
const r = await execIn(
|
|
@@ -16516,7 +16716,7 @@ function createDockerProvider(opts) {
|
|
|
16516
16716
|
);
|
|
16517
16717
|
if (r.code === 0 && !file.over) {
|
|
16518
16718
|
renameSync11(partial, archive);
|
|
16519
|
-
writeFileSync9(
|
|
16719
|
+
writeFileSync9(join29(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
16520
16720
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
16521
16721
|
await writeMarker(name, attemptId);
|
|
16522
16722
|
log2.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -16530,7 +16730,7 @@ function createDockerProvider(opts) {
|
|
|
16530
16730
|
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"
|
|
16531
16731
|
);
|
|
16532
16732
|
};
|
|
16533
|
-
const stagingDir = (name) =>
|
|
16733
|
+
const stagingDir = (name) => join29(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
16534
16734
|
const ensureRunning = async (name) => {
|
|
16535
16735
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
16536
16736
|
if (seen.code !== 0) return false;
|
|
@@ -16538,7 +16738,7 @@ function createDockerProvider(opts) {
|
|
|
16538
16738
|
return (await docker.run(["start", name])).code === 0;
|
|
16539
16739
|
};
|
|
16540
16740
|
const dropArchive = (attemptId) => {
|
|
16541
|
-
rmSync13(
|
|
16741
|
+
rmSync13(join29(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
16542
16742
|
recursive: true,
|
|
16543
16743
|
force: true
|
|
16544
16744
|
});
|
|
@@ -16607,7 +16807,7 @@ function createDockerProvider(opts) {
|
|
|
16607
16807
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
16608
16808
|
await must(["start", name], "starting the container");
|
|
16609
16809
|
const git = await serveGitCredentials({
|
|
16610
|
-
socketPath:
|
|
16810
|
+
socketPath: join29(socketDir(name), "git.sock"),
|
|
16611
16811
|
hosts: opts.gitHosts,
|
|
16612
16812
|
fill: opts.gitFill,
|
|
16613
16813
|
log: log2
|
|
@@ -16643,7 +16843,7 @@ function createDockerProvider(opts) {
|
|
|
16643
16843
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16644
16844
|
if (existsSync18(archive)) {
|
|
16645
16845
|
const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
|
|
16646
|
-
input:
|
|
16846
|
+
input: createReadStream2(archive)
|
|
16647
16847
|
});
|
|
16648
16848
|
if (r.code !== 0) {
|
|
16649
16849
|
log2.warn(
|
|
@@ -16756,15 +16956,15 @@ function createDockerProvider(opts) {
|
|
|
16756
16956
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
16757
16957
|
if (transcripts) {
|
|
16758
16958
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
16759
|
-
if (kept.length) mkdirSync16(
|
|
16760
|
-
for (const f of kept) writeFileSync9(
|
|
16959
|
+
if (kept.length) mkdirSync16(join29(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
16960
|
+
for (const f of kept) writeFileSync9(join29(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
16761
16961
|
await removeIn(
|
|
16762
16962
|
name,
|
|
16763
16963
|
`${CONTAINER_STATE}/transcripts`,
|
|
16764
16964
|
kept.map((f) => f.name)
|
|
16765
16965
|
);
|
|
16766
16966
|
}
|
|
16767
|
-
const outboxRel =
|
|
16967
|
+
const outboxRel = join29("outbox", encodeURIComponent(attempt.agentId));
|
|
16768
16968
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
16769
16969
|
let unpublished = false;
|
|
16770
16970
|
if (outbox) {
|
|
@@ -16790,7 +16990,7 @@ function createDockerProvider(opts) {
|
|
|
16790
16990
|
const staged = stagingDir(name);
|
|
16791
16991
|
mkdirSync16(staged, { recursive: true });
|
|
16792
16992
|
writeFileSync9(
|
|
16793
|
-
|
|
16993
|
+
join29(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
16794
16994
|
`${JSON.stringify(entry)}
|
|
16795
16995
|
`
|
|
16796
16996
|
);
|
|
@@ -16803,7 +17003,7 @@ function createDockerProvider(opts) {
|
|
|
16803
17003
|
if (outbox.complete) {
|
|
16804
17004
|
const published = publishStaged(
|
|
16805
17005
|
stagingDir(name),
|
|
16806
|
-
|
|
17006
|
+
join29(opts.hostStateDir, outboxRel),
|
|
16807
17007
|
opts.moveFile
|
|
16808
17008
|
);
|
|
16809
17009
|
queued = published.published;
|
|
@@ -18778,9 +18978,9 @@ function handleUncaught(log2, err, origin) {
|
|
|
18778
18978
|
// src/crash-marker.ts
|
|
18779
18979
|
init_config();
|
|
18780
18980
|
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as readFileSync18, rmSync as rmSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
18781
|
-
import { join as
|
|
18981
|
+
import { join as join32 } from "path";
|
|
18782
18982
|
function crashMarkerPath() {
|
|
18783
|
-
return
|
|
18983
|
+
return join32(cabaneDir(), "last-error.json");
|
|
18784
18984
|
}
|
|
18785
18985
|
function recordCrash(rec2) {
|
|
18786
18986
|
try {
|
|
@@ -19093,8 +19293,8 @@ async function runAttached(runtime) {
|
|
|
19093
19293
|
init_install_kind();
|
|
19094
19294
|
init_cabane_command();
|
|
19095
19295
|
import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync21 } from "fs";
|
|
19096
|
-
import { homedir as
|
|
19097
|
-
import { dirname as dirname15, join as
|
|
19296
|
+
import { homedir as homedir10 } from "os";
|
|
19297
|
+
import { dirname as dirname15, join as join33 } from "path";
|
|
19098
19298
|
init_config();
|
|
19099
19299
|
init_sandbox_policy();
|
|
19100
19300
|
init_secrets();
|
|
@@ -19163,7 +19363,7 @@ function versionLine() {
|
|
|
19163
19363
|
const install = companionInstall();
|
|
19164
19364
|
const own = installedCommandPath({ install });
|
|
19165
19365
|
if (install === "script") {
|
|
19166
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(
|
|
19366
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join33(homedir10(), ".cabane"))})`;
|
|
19167
19367
|
}
|
|
19168
19368
|
if (own)
|
|
19169
19369
|
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname15(dirname15(own)))})`;
|
|
@@ -19189,8 +19389,8 @@ function otherInstall(install, own) {
|
|
|
19189
19389
|
}
|
|
19190
19390
|
};
|
|
19191
19391
|
if (install === "npm") {
|
|
19192
|
-
const script =
|
|
19193
|
-
|
|
19392
|
+
const script = join33(
|
|
19393
|
+
homedir10(),
|
|
19194
19394
|
".cabane",
|
|
19195
19395
|
"node",
|
|
19196
19396
|
"lib",
|
|
@@ -19198,11 +19398,11 @@ function otherInstall(install, own) {
|
|
|
19198
19398
|
"@cabane",
|
|
19199
19399
|
"companion"
|
|
19200
19400
|
);
|
|
19201
|
-
return existsSync21(script) ?
|
|
19401
|
+
return existsSync21(script) ? join33(homedir10(), ".cabane") : null;
|
|
19202
19402
|
}
|
|
19203
19403
|
const found = firstOnPath("cabane-companion", process.env.PATH);
|
|
19204
19404
|
if (!found) return null;
|
|
19205
|
-
const ownAlias = own ? real(
|
|
19405
|
+
const ownAlias = own ? real(join33(dirname15(own), "cabane-companion")) : null;
|
|
19206
19406
|
return real(found) === ownAlias ? null : dirname15(found);
|
|
19207
19407
|
}
|
|
19208
19408
|
async function liveState() {
|
|
@@ -19429,7 +19629,7 @@ init_errors();
|
|
|
19429
19629
|
init_src();
|
|
19430
19630
|
init_transcript_writer();
|
|
19431
19631
|
import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "fs";
|
|
19432
|
-
import { isAbsolute, join as
|
|
19632
|
+
import { isAbsolute, join as join34 } from "path";
|
|
19433
19633
|
async function transcript(opts = {}) {
|
|
19434
19634
|
const dir2 = transcriptsDir();
|
|
19435
19635
|
if (opts.follow) {
|
|
@@ -19446,7 +19646,7 @@ async function transcript(opts = {}) {
|
|
|
19446
19646
|
process.stdout.write(emptyMessage(dir2));
|
|
19447
19647
|
return;
|
|
19448
19648
|
}
|
|
19449
|
-
process.stdout.write(renderFile(
|
|
19649
|
+
process.stdout.write(renderFile(join34(dir2, newest)) + "\n");
|
|
19450
19650
|
return;
|
|
19451
19651
|
}
|
|
19452
19652
|
printList(dir2);
|
|
@@ -19529,7 +19729,7 @@ function isComplete(content) {
|
|
|
19529
19729
|
async function followTranscripts(dir2) {
|
|
19530
19730
|
const follower = new TranscriptFollower({
|
|
19531
19731
|
listFiles: () => listFiles(dir2),
|
|
19532
|
-
read: (f) => readFileSync19(
|
|
19732
|
+
read: (f) => readFileSync19(join34(dir2, f), "utf8"),
|
|
19533
19733
|
write: (s) => process.stdout.write(s),
|
|
19534
19734
|
// CSI: cursor up `n` lines, then erase from cursor to end of screen.
|
|
19535
19735
|
clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
|
|
@@ -19569,7 +19769,7 @@ function printList(dir2) {
|
|
|
19569
19769
|
|
|
19570
19770
|
`);
|
|
19571
19771
|
for (const f of files.slice(0, 20)) {
|
|
19572
|
-
const { meta, outcome } = peek(
|
|
19772
|
+
const { meta, outcome } = peek(join34(dir2, f));
|
|
19573
19773
|
const when = fmtTime(rec(meta)?.ts);
|
|
19574
19774
|
const ws = str2(rec(meta)?.workspaceSlug);
|
|
19575
19775
|
const o = rec(outcome);
|
|
@@ -19607,10 +19807,10 @@ function resolveTarget(dir2, target) {
|
|
|
19607
19807
|
if (existsSync22(target)) return target;
|
|
19608
19808
|
throw new CompanionError(`no transcript at ${target}.`);
|
|
19609
19809
|
}
|
|
19610
|
-
const exact =
|
|
19810
|
+
const exact = join34(dir2, target);
|
|
19611
19811
|
if (existsSync22(exact)) return exact;
|
|
19612
19812
|
const matches = listFiles(dir2).filter((f) => f.includes(target));
|
|
19613
|
-
if (matches.length === 1) return
|
|
19813
|
+
if (matches.length === 1) return join34(dir2, matches[0]);
|
|
19614
19814
|
if (matches.length === 0) {
|
|
19615
19815
|
throw new CompanionError(
|
|
19616
19816
|
`no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`
|