@cabane/companion 0.6.169 → 0.6.171
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 +368 -161
- package/dist/runtime.js +339 -147
- 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;
|
|
@@ -2520,8 +2542,15 @@ var init_descriptions = __esm({
|
|
|
2520
2542
|
"withdraw_ask"
|
|
2521
2543
|
];
|
|
2522
2544
|
TURN_CONTROL_TOOL_DESCRIPTIONS = {
|
|
2523
|
-
ask: `Ask a
|
|
2524
|
-
|
|
2545
|
+
ask: `Ask a person a structured question you need answered to continue, then end your turn; don't wait for the reply. \`targetUserId\` is a person on your roster; to bring in an agent, name it with \`@handle\` in your reply.
|
|
2546
|
+
- Form: one question (\`headline\`, optional \`question\` body, \`options\`) or \`questions\`, 1\u2013${MAX_ASK_ITEMS} \`{ headline, body?, options? }\`; never both. Decisions that arise together go in ONE call, in the order you want them answered. A second \`ask\` is a separate item, never an edit; raise one only for a genuinely new question.
|
|
2547
|
+
- \`headline\`: one sentence ending in \`?\`. Body (\`question\` or each item's \`body\`): a sentence or two of framing, not a report; inline Markdown only, no lists or headings. \`options\`: ${MIN_ASK_OPTIONS}\u2013${MAX_ASK_OPTIONS} one-click answers (always for a yes/no go-ahead), each a distinct course of action you can carry out without asking back.
|
|
2548
|
+
- Voice, in headline, body and options: "I" for yourself, never your own name; name everyone else who decides or acts, as on your roster. Never "you", "your", "we", "us", "our" or their contractions, nor "he", "she" or "they" for a named actor. Each option reads correctly alone. A quotation stays accurate: say who said it.
|
|
2549
|
+
- It must stand alone, since it can arrive first: the reply carries the findings, the ask the question; don't repeat the reply in it.
|
|
2550
|
+
- Recorded when you call this; delivered when your turn ends, or after 3 minutes if it is still running. Ask as soon as you know. The answer reaches you on a later turn.
|
|
2551
|
+
- The person sends one response; unanswered questions end as Unanswered. A person can dismiss an ask; you aren't told and nothing wakes you. Check an ask's status before raising it again.
|
|
2552
|
+
- To replace a stale ask (yours or anyone's here), pass \`replaces: { askId, reason }\`: both land in one write and the person sees the reason. To withdraw without a replacement, use \`withdraw_ask\`.`,
|
|
2553
|
+
cancel_wake: `Stand down your own wake \u2014 the inverse of \`wake_me\`. No arguments: it cancels the wake you have armed in this conversation, NOW, when you call it. Reach for it when the thing you armed a wake to check has already happened, or the work it was watching is over \u2014 an armed wake you no longer need fires into a turn with nothing to do, and a supervision loop with no off switch is one you can only end by leaving it running. Whether you have one armed is printed in your turn context ("Wake armed: \u2026"); a wake you armed that is no longer printed there has fired or been dismissed. Calling this with nothing armed is a clean no-op, no error. Each call acts as it arrives: \`cancel_wake\` then \`wake_me\` leaves the NEW wake armed; \`wake_me\` then \`cancel_wake\` leaves nothing armed. It reaches only your own wake in this conversation \u2014 never a peer's, and never a recurring schedule (those are \`schedules.*\`).`,
|
|
2525
2554
|
skip_turn: `End your current turn WITHOUT posting a reply. Call this when you've been woken to read a message that genuinely doesn't need a response from you \u2014 a thanks/aside, a question already answered, chatter outside your lane, or something another participant clearly owns. Your turn ends silently: no message bubble is posted. The \`reason\` is a short free-text note for telemetry (e.g. "already answered by cabane", "thanks, nothing to add"). Prefer this over posting a low-value "ok!"/"got it" reply. Don't also write a reply when you skip \u2014 skipping IS the whole turn.`,
|
|
2526
2555
|
wake_me: `Wake yourself later \u2014 end this turn now and be re-dispatched at a time you pick, with a note you write to yourself. Use it for "wait until X": when the thing you need hasn't happened yet (a PR isn't merged, a human hasn't answered), arm a wake, end your turn, and you're woken later to CHECK \u2014 read the workspace, and either act or re-arm. Ground the delay before you arm it. Almost every wake is short \u2014 seconds to a couple of hours \u2014 waiting on a condition you can name: a session limit resetting, a PR merging. Reach past a few hours only when (a) a human asked for that timing, or (b) the wait is pinned to a real external event you can name \u2014 a report that only runs Mondays, a known reset time. A speculative far-future check-in you invented yourself is the one thing not to arm: if no one asked and you can't name both what clears the wait and why it takes that long, don't arm it \u2014 finish now, or raise an \`ask\`. Pass EXACTLY ONE of \`afterSeconds\` (a relative delay \u2014 \`300\` for five minutes) or \`at\` (an absolute ISO-8601 timestamp WITH a zone, e.g. \`2026-07-16T09:00:00-07:00\` \u2014 YOU compute it; the system never parses natural-language time). \`note\` is a message to your future self \u2014 it becomes the body of the wake message that re-dispatches you, so write the condition to re-check ("check whether CT441 merged yet"). The wake arms NOW, when you call this, and the delay counts from this call; a later \`wake_me\` in the same turn replaces it, and \`cancel_wake\` stands it down. A wake that comes due while your turn is still running waits and is delivered after your reply. This is the ONLY sanctioned way to schedule your own continuation; never reach for a host cron/scheduler. Guardrails: at least 60s out, at most 14 days \u2014 a wake that can't be armed returns an error here, in this call, so you can recompute; widen the interval as a loop ages (5m \u2192 15m \u2192 1h\u2026) rather than hammering; after many consecutive re-arms with no other activity you'll be steered to raise an \`ask\` to the human instead.`,
|
|
2527
2556
|
withdraw_ask: `Withdraw an open ask \u2014 yours or anyone's in this conversation \u2014 because the question is moot, was wrong, or has been overtaken. \`reason\` is required and is shown to the person and, if it wasn't your ask, to the agent who asked it. If there is a current question to put in its place, don't call this \u2014 call \`ask\` with \`replaces\` so the two land as one and the person never sees neither or both. Never withdraw to hurry an answer; to act on a deadline, arm a \`wake_me\` and decide then.`
|
|
@@ -2529,13 +2558,13 @@ var init_descriptions = __esm({
|
|
|
2529
2558
|
TURN_CONTROL_FIELD_DESCRIPTIONS = {
|
|
2530
2559
|
ask: {
|
|
2531
2560
|
targetUserId: "The workspace member (human) to ask \u2014 a user id, from your turn context's roster.",
|
|
2532
|
-
question: `SINGLE-question form: a short body \u2014 one or two sentences of framing the headline can't hold. NOT a report (up to ${MAX_ASK_QUESTION_BYTES} characters, inline markdown only). Provide EITHER this or \`questions\`, not both. Put the crisp one-sentence question in \`headline
|
|
2533
|
-
headline: `SINGLE-question form: the question itself as ONE clear, capitalized sentence ending in \`?\` ("Should Ledger deploy once Mia approves?"), up to ${MAX_ASK_HEADLINE_BYTES} characters. What the
|
|
2534
|
-
options: `SINGLE-question form: optional ${MIN_ASK_OPTIONS}\u2013${MAX_ASK_OPTIONS} suggested one-click answers, up to ${MAX_ASK_OPTION_BYTES} characters each. Each option must
|
|
2561
|
+
question: `SINGLE-question form: a short body \u2014 one or two sentences of framing the headline can't hold. NOT a report (up to ${MAX_ASK_QUESTION_BYTES} characters, inline markdown only). Provide EITHER this or \`questions\`, not both. Put the crisp one-sentence question in \`headline\`.`,
|
|
2562
|
+
headline: `SINGLE-question form: the question itself as ONE clear, capitalized sentence ending in \`?\` ("Should Ledger deploy once Mia approves?"), up to ${MAX_ASK_HEADLINE_BYTES} characters. What the person reads first in For you and on the chip \u2014 one scannable question, no elaboration (that goes in \`question\`). Strongly encouraged.`,
|
|
2563
|
+
options: `SINGLE-question form: optional ${MIN_ASK_OPTIONS}\u2013${MAX_ASK_OPTIONS} suggested one-click answers, up to ${MAX_ASK_OPTION_BYTES} characters each. Each option must make sense on its own.`,
|
|
2535
2564
|
questions: `MULTI-question form: 1\u2013${MAX_ASK_ITEMS} questions to ask at once, when a plan ends with several bounded decisions. Provide EITHER this or \`question\`/\`headline\`/\`options\`, not both.`,
|
|
2536
|
-
"questions.headline": `The one-sentence question ("Should Ledger deploy once Mia approves?") \u2014 required for each item, up to ${MAX_ASK_HEADLINE_BYTES} characters
|
|
2537
|
-
"questions.body": `Optional short framing beneath the headline, up to ${MAX_ASK_QUESTION_BYTES} characters. NOT a report
|
|
2538
|
-
"questions.options": `Optional ${MIN_ASK_OPTIONS}\u2013${MAX_ASK_OPTIONS} one-click answers for this question, up to ${MAX_ASK_OPTION_BYTES} characters each. Each option must
|
|
2565
|
+
"questions.headline": `The one-sentence question ("Should Ledger deploy once Mia approves?") \u2014 required for each item, up to ${MAX_ASK_HEADLINE_BYTES} characters.`,
|
|
2566
|
+
"questions.body": `Optional short framing beneath the headline, up to ${MAX_ASK_QUESTION_BYTES} characters, inline markdown only. NOT a report.`,
|
|
2567
|
+
"questions.options": `Optional ${MIN_ASK_OPTIONS}\u2013${MAX_ASK_OPTIONS} one-click answers for this question, up to ${MAX_ASK_OPTION_BYTES} characters each. Each option must make sense on its own.`,
|
|
2539
2568
|
replaces: "Withdraw a stale ask in this conversation as part of raising this one. Both land in a single write, so the person goes from the old question to the new one and never sees neither or both.",
|
|
2540
2569
|
"replaces.askId": "The id of the open ask to withdraw.",
|
|
2541
2570
|
"replaces.reason": `Why the old question is no longer the question \u2014 shown to the person on their Done row. Up to ${MAX_WITHDRAW_REASON_BYTES} characters.`
|
|
@@ -5374,7 +5403,14 @@ function fingerprintPrompt(prompt) {
|
|
|
5374
5403
|
return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
|
|
5375
5404
|
}
|
|
5376
5405
|
function encodeSession3(state) {
|
|
5377
|
-
return JSON.stringify(
|
|
5406
|
+
return JSON.stringify({
|
|
5407
|
+
threadId: state.threadId,
|
|
5408
|
+
cwd: state.cwd,
|
|
5409
|
+
promptFingerprint: state.promptFingerprint,
|
|
5410
|
+
conversationFingerprint: state.conversationFingerprint ?? null,
|
|
5411
|
+
compactions: state.compactions ?? null,
|
|
5412
|
+
usageBaseline: state.usageBaseline
|
|
5413
|
+
});
|
|
5378
5414
|
}
|
|
5379
5415
|
function decideResume3(stored, currentCwd) {
|
|
5380
5416
|
if (!stored) return { fresh: true, reason: "no_session" };
|
|
@@ -5395,6 +5431,8 @@ function decideResume3(stored, currentCwd) {
|
|
|
5395
5431
|
return {
|
|
5396
5432
|
resume: state.threadId,
|
|
5397
5433
|
promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
|
|
5434
|
+
conversationFingerprint: typeof state.conversationFingerprint === "string" ? state.conversationFingerprint : null,
|
|
5435
|
+
compactions: typeof state.compactions === "number" && Number.isInteger(state.compactions) && state.compactions >= 0 ? state.compactions : null,
|
|
5398
5436
|
usageBaseline: readUsageBaseline(state.usageBaseline)
|
|
5399
5437
|
};
|
|
5400
5438
|
}
|
|
@@ -5432,6 +5470,7 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5432
5470
|
let usage;
|
|
5433
5471
|
let cumulativeUsage;
|
|
5434
5472
|
let threadIdForSession = ctx2.resumedThreadId;
|
|
5473
|
+
let lastState = ctx2.storedState ?? null;
|
|
5435
5474
|
let settled = false;
|
|
5436
5475
|
function* flushInterim() {
|
|
5437
5476
|
const sealed = sealHeld2(held, false);
|
|
@@ -5447,14 +5486,14 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5447
5486
|
const state = encodeSession3({
|
|
5448
5487
|
threadId,
|
|
5449
5488
|
cwd: ctx2.cwd ?? "",
|
|
5450
|
-
promptFingerprint: ctx2.promptFingerprint ?? null,
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
// closing write records the new baseline.
|
|
5489
|
+
promptFingerprint: ctx2.heldStanding?.promptFingerprint ?? null,
|
|
5490
|
+
conversationFingerprint: ctx2.heldStanding?.conversationFingerprint ?? null,
|
|
5491
|
+
compactions: ctx2.heldStanding?.compactions ?? null,
|
|
5454
5492
|
usageBaseline: ctx2.usageBaseline ?? null
|
|
5455
5493
|
});
|
|
5456
5494
|
if (threadId !== ctx2.resumedThreadId || state !== ctx2.storedState) {
|
|
5457
5495
|
sessionEmitted = true;
|
|
5496
|
+
lastState = state;
|
|
5458
5497
|
yield {
|
|
5459
5498
|
type: "session",
|
|
5460
5499
|
state,
|
|
@@ -5553,14 +5592,16 @@ async function* decodeCodexStream(events, ctx2) {
|
|
|
5553
5592
|
if (sealed) yield sealed;
|
|
5554
5593
|
reason = "no_terminal";
|
|
5555
5594
|
}
|
|
5556
|
-
if (ok &&
|
|
5595
|
+
if (ok && threadIdForSession) {
|
|
5557
5596
|
const closing = encodeSession3({
|
|
5558
5597
|
threadId: threadIdForSession,
|
|
5559
5598
|
cwd: ctx2.cwd ?? "",
|
|
5560
5599
|
promptFingerprint: ctx2.promptFingerprint ?? null,
|
|
5561
|
-
|
|
5600
|
+
conversationFingerprint: ctx2.conversationFingerprint ?? null,
|
|
5601
|
+
compactions: ctx2.compactions ?? null,
|
|
5602
|
+
usageBaseline: cumulativeUsage ?? ctx2.usageBaseline ?? null
|
|
5562
5603
|
});
|
|
5563
|
-
yield { type: "session", state: closing };
|
|
5604
|
+
if (closing !== lastState) yield { type: "session", state: closing };
|
|
5564
5605
|
}
|
|
5565
5606
|
const resolvedConfig = ctx2.resolvedReasoningEffort ? { reasoningEffort: ctx2.resolvedReasoningEffort } : void 0;
|
|
5566
5607
|
yield {
|
|
@@ -5640,15 +5681,27 @@ var init_model3 = __esm({
|
|
|
5640
5681
|
});
|
|
5641
5682
|
|
|
5642
5683
|
// packages/agent-runtime/src/codex/run-spec.ts
|
|
5643
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null,
|
|
5684
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
|
|
5644
5685
|
const { policy, config } = req;
|
|
5645
5686
|
const directory = req.local.cwd ?? "";
|
|
5646
5687
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
5647
5688
|
const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
|
|
5648
5689
|
const model = config.model ? parseCodexModel(config.model) : null;
|
|
5649
|
-
const
|
|
5650
|
-
const
|
|
5651
|
-
const
|
|
5690
|
+
const segments = standingSegments(req);
|
|
5691
|
+
const conversationText = segments.conversation.trim();
|
|
5692
|
+
const fingerprints = {
|
|
5693
|
+
core: fingerprintPrompt(segments.core),
|
|
5694
|
+
conversation: conversationText.length > 0 ? fingerprintPrompt(conversationText) : null
|
|
5695
|
+
};
|
|
5696
|
+
const resumed = resumeThreadId !== null;
|
|
5697
|
+
const held = resumed ? threadStanding : null;
|
|
5698
|
+
const viaInput = baseInstructionsFile === null;
|
|
5699
|
+
const preamble = {
|
|
5700
|
+
core: viaInput && (held?.core ?? null) !== fingerprints.core,
|
|
5701
|
+
// Compared both ways: a held segment that is now empty differs from it, and nothing held
|
|
5702
|
+
// equals nothing to send.
|
|
5703
|
+
conversation: viaInput && (held?.conversation ?? null) !== fingerprints.conversation
|
|
5704
|
+
};
|
|
5652
5705
|
return {
|
|
5653
5706
|
resumeThreadId,
|
|
5654
5707
|
directory,
|
|
@@ -5657,9 +5710,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
5657
5710
|
skipGitRepoCheck: true,
|
|
5658
5711
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
5659
5712
|
baseInstructionsFile,
|
|
5660
|
-
input: withImages(buildInput(req,
|
|
5661
|
-
|
|
5662
|
-
|
|
5713
|
+
input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
|
|
5714
|
+
preamble,
|
|
5715
|
+
fingerprints,
|
|
5663
5716
|
config: buildConfig(req, baseInstructionsFile),
|
|
5664
5717
|
secretEnv: codexSecretEnv(req)
|
|
5665
5718
|
};
|
|
@@ -5675,12 +5728,24 @@ function codexSecretEnv(req) {
|
|
|
5675
5728
|
}
|
|
5676
5729
|
return Object.keys(env).length > 0 ? env : null;
|
|
5677
5730
|
}
|
|
5678
|
-
function buildInput(req,
|
|
5731
|
+
function buildInput(req, segments, preamble, resumed) {
|
|
5679
5732
|
const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
|
|
5680
5733
|
const body = userText.trim().length > 0 ? userText : req.prompt;
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5734
|
+
const standing = [];
|
|
5735
|
+
if (!resumed) {
|
|
5736
|
+
if (preamble.core) standing.push(joinStanding(segments).trim());
|
|
5737
|
+
} else {
|
|
5738
|
+
if (preamble.core) standing.push(`${CORE_REPLACES}
|
|
5739
|
+
|
|
5740
|
+
${segments.core.trim()}`);
|
|
5741
|
+
if (preamble.conversation) {
|
|
5742
|
+
standing.push(`${CONVERSATION_REPLACES}
|
|
5743
|
+
|
|
5744
|
+
${segments.conversation.trim()}`.trim());
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5747
|
+
const lead = standing.filter((s) => s.length > 0);
|
|
5748
|
+
return lead.length > 0 ? `${lead.join("\n\n---\n\n")}
|
|
5684
5749
|
|
|
5685
5750
|
${body}` : body;
|
|
5686
5751
|
}
|
|
@@ -5766,10 +5831,11 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
5766
5831
|
function isStringRecord2(v) {
|
|
5767
5832
|
return !!v && typeof v === "object" && Object.values(v).every((x) => typeof x === "string");
|
|
5768
5833
|
}
|
|
5769
|
-
var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS;
|
|
5834
|
+
var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS, CORE_REPLACES, CONVERSATION_REPLACES;
|
|
5770
5835
|
var init_run_spec2 = __esm({
|
|
5771
5836
|
"packages/agent-runtime/src/codex/run-spec.ts"() {
|
|
5772
5837
|
"use strict";
|
|
5838
|
+
init_standing();
|
|
5773
5839
|
init_policy3();
|
|
5774
5840
|
init_model3();
|
|
5775
5841
|
init_session3();
|
|
@@ -5784,6 +5850,8 @@ var init_run_spec2 = __esm({
|
|
|
5784
5850
|
"CABANE_CONVERSATION_TITLE",
|
|
5785
5851
|
"CABANE_HOST_ACCESS"
|
|
5786
5852
|
];
|
|
5853
|
+
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.";
|
|
5854
|
+
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
5855
|
}
|
|
5788
5856
|
});
|
|
5789
5857
|
|
|
@@ -5922,7 +5990,6 @@ function createCodexAdapter(deps = {}) {
|
|
|
5922
5990
|
const directory = req.local.cwd ?? "";
|
|
5923
5991
|
const decision = decideResume3(req.session, directory);
|
|
5924
5992
|
const resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
5925
|
-
const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
|
|
5926
5993
|
const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
5927
5994
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
5928
5995
|
deps.onWarn?.(
|
|
@@ -5939,6 +6006,32 @@ function createCodexAdapter(deps = {}) {
|
|
|
5939
6006
|
degraded = false;
|
|
5940
6007
|
}
|
|
5941
6008
|
}
|
|
6009
|
+
let compactions = 0;
|
|
6010
|
+
let threadStanding = null;
|
|
6011
|
+
if ("resume" in decision) {
|
|
6012
|
+
compactions = deps.countCompactions ? await deps.countCompactions(decision.resume) : null;
|
|
6013
|
+
if (signal.aborted) return;
|
|
6014
|
+
if (compactions === null) {
|
|
6015
|
+
deps.onWarn?.(
|
|
6016
|
+
"codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions (ENG210)",
|
|
6017
|
+
{ threadId: decision.resume }
|
|
6018
|
+
);
|
|
6019
|
+
} else if (decision.compactions === compactions) {
|
|
6020
|
+
threadStanding = {
|
|
6021
|
+
core: decision.promptFingerprint,
|
|
6022
|
+
conversation: decision.conversationFingerprint
|
|
6023
|
+
};
|
|
6024
|
+
} else if (decision.compactions !== null && compactions > decision.compactions) {
|
|
6025
|
+
deps.onWarn?.(
|
|
6026
|
+
"codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions (ENG210)",
|
|
6027
|
+
{
|
|
6028
|
+
threadId: decision.resume,
|
|
6029
|
+
compactions,
|
|
6030
|
+
compactionsSince: compactions - decision.compactions
|
|
6031
|
+
}
|
|
6032
|
+
);
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
5942
6035
|
let instructions = null;
|
|
5943
6036
|
if (!req.policy.hostFs) {
|
|
5944
6037
|
if (!deps.writeInstructionsFile) {
|
|
@@ -5983,7 +6076,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
5983
6076
|
req,
|
|
5984
6077
|
resumeThreadId,
|
|
5985
6078
|
instructions?.path ?? null,
|
|
5986
|
-
|
|
6079
|
+
threadStanding,
|
|
5987
6080
|
images?.paths ?? []
|
|
5988
6081
|
);
|
|
5989
6082
|
if (spec.model === null) {
|
|
@@ -5998,10 +6091,18 @@ function createCodexAdapter(deps = {}) {
|
|
|
5998
6091
|
cwd: req.local.cwd,
|
|
5999
6092
|
resumedThreadId: resumeThreadId,
|
|
6000
6093
|
degraded,
|
|
6001
|
-
// Which
|
|
6002
|
-
// later copy is what the model reads.
|
|
6003
|
-
//
|
|
6004
|
-
promptFingerprint: spec.
|
|
6094
|
+
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6095
|
+
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6096
|
+
// the spec, so the record can't disagree with what was sent.
|
|
6097
|
+
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6098
|
+
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6099
|
+
compactions,
|
|
6100
|
+
// What the thread held before this turn, for the write at `thread.started`.
|
|
6101
|
+
heldStanding: {
|
|
6102
|
+
promptFingerprint: threadStanding?.core ?? null,
|
|
6103
|
+
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6104
|
+
compactions: threadStanding ? compactions : null
|
|
6105
|
+
},
|
|
6005
6106
|
storedState: req.session,
|
|
6006
6107
|
usageBaseline: threadUsageBaseline,
|
|
6007
6108
|
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
@@ -6072,7 +6173,7 @@ function makeRequest3(overrides2 = {}) {
|
|
|
6072
6173
|
...overrides2
|
|
6073
6174
|
};
|
|
6074
6175
|
}
|
|
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,
|
|
6176
|
+
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
6177
|
var init_conformance4 = __esm({
|
|
6077
6178
|
"packages/agent-runtime/src/codex/conformance.ts"() {
|
|
6078
6179
|
"use strict";
|
|
@@ -6127,11 +6228,21 @@ var init_conformance4 = __esm({
|
|
|
6127
6228
|
message: "files.read returned more than the result ceiling"
|
|
6128
6229
|
}
|
|
6129
6230
|
};
|
|
6130
|
-
|
|
6231
|
+
startedSession = (threadId, cwd = DIR2, degraded = false) => ({
|
|
6131
6232
|
type: "session",
|
|
6132
|
-
state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
|
|
6233
|
+
state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
|
|
6133
6234
|
...degraded ? { degraded: true } : {}
|
|
6134
6235
|
});
|
|
6236
|
+
closingSession = (threadId, usageBaseline = null) => ({
|
|
6237
|
+
type: "session",
|
|
6238
|
+
state: encodeSession3({
|
|
6239
|
+
threadId,
|
|
6240
|
+
cwd: DIR2,
|
|
6241
|
+
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
6242
|
+
usageBaseline,
|
|
6243
|
+
compactions: 0
|
|
6244
|
+
})
|
|
6245
|
+
});
|
|
6135
6246
|
CODEX_CONFORMANCE_FIXTURES = [
|
|
6136
6247
|
{
|
|
6137
6248
|
// A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
|
|
@@ -6154,11 +6265,11 @@ var init_conformance4 = __esm({
|
|
|
6154
6265
|
})
|
|
6155
6266
|
],
|
|
6156
6267
|
expected: [
|
|
6157
|
-
|
|
6268
|
+
startedSession(NEW_THREAD_ID),
|
|
6158
6269
|
{ type: "text", body: "Hello there.", terminal: true },
|
|
6159
6270
|
// The closing session write — the thread's ending totals, recorded as the baseline the next turn
|
|
6160
6271
|
// subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
|
|
6161
|
-
|
|
6272
|
+
closingSession(NEW_THREAD_ID, {
|
|
6162
6273
|
inputTokens: 100,
|
|
6163
6274
|
outputTokens: 50,
|
|
6164
6275
|
cacheReadTokens: 20,
|
|
@@ -6205,7 +6316,7 @@ var init_conformance4 = __esm({
|
|
|
6205
6316
|
turnCompleted()
|
|
6206
6317
|
],
|
|
6207
6318
|
expected: [
|
|
6208
|
-
|
|
6319
|
+
startedSession(NEW_THREAD_ID),
|
|
6209
6320
|
{ type: "text", body: "Reading your notes.", terminal: false },
|
|
6210
6321
|
{
|
|
6211
6322
|
type: "tool",
|
|
@@ -6225,6 +6336,7 @@ var init_conformance4 = __esm({
|
|
|
6225
6336
|
result: [{ type: "text", text: "sleep early" }]
|
|
6226
6337
|
},
|
|
6227
6338
|
{ type: "text", body: "Here's the summary.", terminal: true },
|
|
6339
|
+
closingSession(NEW_THREAD_ID),
|
|
6228
6340
|
{ type: "result", ok: true }
|
|
6229
6341
|
]
|
|
6230
6342
|
},
|
|
@@ -6258,7 +6370,7 @@ var init_conformance4 = __esm({
|
|
|
6258
6370
|
turnCompleted()
|
|
6259
6371
|
],
|
|
6260
6372
|
expected: [
|
|
6261
|
-
|
|
6373
|
+
startedSession(NEW_THREAD_ID),
|
|
6262
6374
|
{
|
|
6263
6375
|
type: "tool",
|
|
6264
6376
|
id: "query-success",
|
|
@@ -6276,6 +6388,7 @@ var init_conformance4 = __esm({
|
|
|
6276
6388
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6277
6389
|
result: OP_SUCCESS
|
|
6278
6390
|
},
|
|
6391
|
+
closingSession(NEW_THREAD_ID),
|
|
6279
6392
|
{ type: "result", ok: true }
|
|
6280
6393
|
]
|
|
6281
6394
|
},
|
|
@@ -6311,7 +6424,7 @@ var init_conformance4 = __esm({
|
|
|
6311
6424
|
turnCompleted()
|
|
6312
6425
|
],
|
|
6313
6426
|
expected: [
|
|
6314
|
-
|
|
6427
|
+
startedSession(NEW_THREAD_ID),
|
|
6315
6428
|
{
|
|
6316
6429
|
type: "tool",
|
|
6317
6430
|
id: "query-failed",
|
|
@@ -6329,6 +6442,7 @@ var init_conformance4 = __esm({
|
|
|
6329
6442
|
input: { op: "files.read", args: { path: "/ABOUT.md" } },
|
|
6330
6443
|
result: OP_FAILURE
|
|
6331
6444
|
},
|
|
6445
|
+
closingSession(NEW_THREAD_ID),
|
|
6332
6446
|
{ type: "result", ok: true }
|
|
6333
6447
|
]
|
|
6334
6448
|
},
|
|
@@ -6352,7 +6466,7 @@ var init_conformance4 = __esm({
|
|
|
6352
6466
|
turnCompleted()
|
|
6353
6467
|
],
|
|
6354
6468
|
expected: [
|
|
6355
|
-
|
|
6469
|
+
startedSession(NEW_THREAD_ID),
|
|
6356
6470
|
{
|
|
6357
6471
|
type: "tool",
|
|
6358
6472
|
id: "mcp-failed",
|
|
@@ -6370,6 +6484,7 @@ var init_conformance4 = __esm({
|
|
|
6370
6484
|
input: { q: "receipt" },
|
|
6371
6485
|
result: "connection closed"
|
|
6372
6486
|
},
|
|
6487
|
+
closingSession(NEW_THREAD_ID),
|
|
6373
6488
|
{ type: "result", ok: true }
|
|
6374
6489
|
]
|
|
6375
6490
|
},
|
|
@@ -6395,7 +6510,7 @@ var init_conformance4 = __esm({
|
|
|
6395
6510
|
turnCompleted()
|
|
6396
6511
|
],
|
|
6397
6512
|
expected: [
|
|
6398
|
-
|
|
6513
|
+
startedSession(NEW_THREAD_ID),
|
|
6399
6514
|
{
|
|
6400
6515
|
type: "tool",
|
|
6401
6516
|
id: "mcp-image",
|
|
@@ -6413,6 +6528,7 @@ var init_conformance4 = __esm({
|
|
|
6413
6528
|
input: {},
|
|
6414
6529
|
result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
|
|
6415
6530
|
},
|
|
6531
|
+
closingSession(NEW_THREAD_ID),
|
|
6416
6532
|
{ type: "result", ok: true }
|
|
6417
6533
|
]
|
|
6418
6534
|
},
|
|
@@ -6453,7 +6569,7 @@ var init_conformance4 = __esm({
|
|
|
6453
6569
|
turnCompleted()
|
|
6454
6570
|
],
|
|
6455
6571
|
expected: [
|
|
6456
|
-
|
|
6572
|
+
startedSession(NEW_THREAD_ID),
|
|
6457
6573
|
{ type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
|
|
6458
6574
|
{
|
|
6459
6575
|
type: "tool",
|
|
@@ -6464,6 +6580,7 @@ var init_conformance4 = __esm({
|
|
|
6464
6580
|
input: { path: "/product/tasks" },
|
|
6465
6581
|
result: [{ type: "text", text: "CT481.md" }]
|
|
6466
6582
|
},
|
|
6583
|
+
closingSession(NEW_THREAD_ID),
|
|
6467
6584
|
{ type: "result", ok: true }
|
|
6468
6585
|
]
|
|
6469
6586
|
},
|
|
@@ -6481,7 +6598,7 @@ var init_conformance4 = __esm({
|
|
|
6481
6598
|
turnCompleted()
|
|
6482
6599
|
],
|
|
6483
6600
|
expected: [
|
|
6484
|
-
|
|
6601
|
+
startedSession(NEW_THREAD_ID),
|
|
6485
6602
|
{
|
|
6486
6603
|
type: "tool",
|
|
6487
6604
|
id: "ws1",
|
|
@@ -6498,6 +6615,7 @@ var init_conformance4 = __esm({
|
|
|
6498
6615
|
summary: "best pizza in nyc",
|
|
6499
6616
|
input: { query: "best pizza in nyc" }
|
|
6500
6617
|
},
|
|
6618
|
+
closingSession(NEW_THREAD_ID),
|
|
6501
6619
|
{ type: "result", ok: true }
|
|
6502
6620
|
]
|
|
6503
6621
|
},
|
|
@@ -6513,7 +6631,7 @@ var init_conformance4 = __esm({
|
|
|
6513
6631
|
turnCompleted()
|
|
6514
6632
|
],
|
|
6515
6633
|
expected: [
|
|
6516
|
-
|
|
6634
|
+
startedSession(NEW_THREAD_ID),
|
|
6517
6635
|
{
|
|
6518
6636
|
type: "tool",
|
|
6519
6637
|
id: "ws2",
|
|
@@ -6530,6 +6648,7 @@ var init_conformance4 = __esm({
|
|
|
6530
6648
|
summary: "",
|
|
6531
6649
|
input: { query: "" }
|
|
6532
6650
|
},
|
|
6651
|
+
closingSession(NEW_THREAD_ID),
|
|
6533
6652
|
{ type: "result", ok: true }
|
|
6534
6653
|
]
|
|
6535
6654
|
},
|
|
@@ -6560,7 +6679,7 @@ var init_conformance4 = __esm({
|
|
|
6560
6679
|
turnCompleted()
|
|
6561
6680
|
],
|
|
6562
6681
|
expected: [
|
|
6563
|
-
|
|
6682
|
+
startedSession(NEW_THREAD_ID),
|
|
6564
6683
|
{ type: "text", body: "Working on it.", terminal: false },
|
|
6565
6684
|
{
|
|
6566
6685
|
type: "tool",
|
|
@@ -6592,7 +6711,7 @@ var init_conformance4 = __esm({
|
|
|
6592
6711
|
turnFailed("provider exploded")
|
|
6593
6712
|
],
|
|
6594
6713
|
expected: [
|
|
6595
|
-
|
|
6714
|
+
startedSession(NEW_THREAD_ID),
|
|
6596
6715
|
{ type: "text", body: "Partial work.", terminal: false },
|
|
6597
6716
|
{ type: "runtime_notice", body: "provider exploded" },
|
|
6598
6717
|
{ type: "result", ok: false, reason: "error:provider exploded" }
|
|
@@ -6609,7 +6728,7 @@ var init_conformance4 = __esm({
|
|
|
6609
6728
|
fatalError("stream died")
|
|
6610
6729
|
],
|
|
6611
6730
|
expected: [
|
|
6612
|
-
|
|
6731
|
+
startedSession(NEW_THREAD_ID),
|
|
6613
6732
|
{ type: "text", body: "Starting.", terminal: false },
|
|
6614
6733
|
{ type: "runtime_notice", body: "stream died" },
|
|
6615
6734
|
{ type: "result", ok: false, reason: "error:stream died" }
|
|
@@ -6622,7 +6741,7 @@ var init_conformance4 = __esm({
|
|
|
6622
6741
|
request: makeRequest3(),
|
|
6623
6742
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
|
|
6624
6743
|
expected: [
|
|
6625
|
-
|
|
6744
|
+
startedSession(NEW_THREAD_ID),
|
|
6626
6745
|
{ type: "runtime_notice", body: "429 Too Many Requests" },
|
|
6627
6746
|
{ type: "result", ok: false, reason: "rate_limited" }
|
|
6628
6747
|
]
|
|
@@ -6632,7 +6751,7 @@ var init_conformance4 = __esm({
|
|
|
6632
6751
|
request: makeRequest3(),
|
|
6633
6752
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
|
|
6634
6753
|
expected: [
|
|
6635
|
-
|
|
6754
|
+
startedSession(NEW_THREAD_ID),
|
|
6636
6755
|
{ type: "runtime_notice", body: "503 Service Unavailable" },
|
|
6637
6756
|
{ type: "result", ok: false, reason: "server_error" }
|
|
6638
6757
|
]
|
|
@@ -6645,7 +6764,7 @@ var init_conformance4 = __esm({
|
|
|
6645
6764
|
turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
|
|
6646
6765
|
],
|
|
6647
6766
|
expected: [
|
|
6648
|
-
|
|
6767
|
+
startedSession(NEW_THREAD_ID),
|
|
6649
6768
|
{
|
|
6650
6769
|
type: "runtime_notice",
|
|
6651
6770
|
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
@@ -6658,7 +6777,7 @@ var init_conformance4 = __esm({
|
|
|
6658
6777
|
request: makeRequest3(),
|
|
6659
6778
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
|
|
6660
6779
|
expected: [
|
|
6661
|
-
|
|
6780
|
+
startedSession(NEW_THREAD_ID),
|
|
6662
6781
|
{ type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
|
|
6663
6782
|
{ type: "result", ok: false, reason: "auth_expired" }
|
|
6664
6783
|
]
|
|
@@ -6670,7 +6789,7 @@ var init_conformance4 = __esm({
|
|
|
6670
6789
|
request: makeRequest3(),
|
|
6671
6790
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
|
|
6672
6791
|
expected: [
|
|
6673
|
-
|
|
6792
|
+
startedSession(NEW_THREAD_ID),
|
|
6674
6793
|
{ type: "runtime_notice", body: "something local broke" },
|
|
6675
6794
|
{ type: "result", ok: false, reason: "error:something local broke" }
|
|
6676
6795
|
]
|
|
@@ -6683,7 +6802,7 @@ var init_conformance4 = __esm({
|
|
|
6683
6802
|
turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
|
|
6684
6803
|
],
|
|
6685
6804
|
expected: [
|
|
6686
|
-
|
|
6805
|
+
startedSession(NEW_THREAD_ID),
|
|
6687
6806
|
{ type: "runtime_notice", body: BOUNDED_NOTICE2 },
|
|
6688
6807
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
6689
6808
|
]
|
|
@@ -6694,26 +6813,32 @@ var init_conformance4 = __esm({
|
|
|
6694
6813
|
name: "empty failure text emits no runtime notice",
|
|
6695
6814
|
request: makeRequest3(),
|
|
6696
6815
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
|
|
6697
|
-
expected: [
|
|
6816
|
+
expected: [
|
|
6817
|
+
startedSession(NEW_THREAD_ID),
|
|
6818
|
+
{ type: "result", ok: false, reason: "error:unknown" }
|
|
6819
|
+
]
|
|
6698
6820
|
},
|
|
6699
6821
|
{
|
|
6700
6822
|
name: "whitespace-only failure text emits no runtime notice",
|
|
6701
6823
|
request: makeRequest3(),
|
|
6702
6824
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
|
|
6703
|
-
expected: [
|
|
6825
|
+
expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
|
|
6704
6826
|
},
|
|
6705
6827
|
{
|
|
6706
6828
|
name: "bare status token emits no runtime notice",
|
|
6707
6829
|
request: makeRequest3(),
|
|
6708
6830
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
|
|
6709
|
-
expected: [
|
|
6831
|
+
expected: [
|
|
6832
|
+
startedSession(NEW_THREAD_ID),
|
|
6833
|
+
{ type: "result", ok: false, reason: "rate_limited" }
|
|
6834
|
+
]
|
|
6710
6835
|
},
|
|
6711
6836
|
{
|
|
6712
6837
|
name: "single-word provider sentence emits a runtime notice",
|
|
6713
6838
|
request: makeRequest3(),
|
|
6714
6839
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
|
|
6715
6840
|
expected: [
|
|
6716
|
-
|
|
6841
|
+
startedSession(NEW_THREAD_ID),
|
|
6717
6842
|
{ type: "runtime_notice", body: "Unavailable." },
|
|
6718
6843
|
{ type: "result", ok: false, reason: "error:Unavailable." }
|
|
6719
6844
|
]
|
|
@@ -6741,7 +6866,7 @@ var init_conformance4 = __esm({
|
|
|
6741
6866
|
turnCompleted()
|
|
6742
6867
|
],
|
|
6743
6868
|
expected: [
|
|
6744
|
-
|
|
6869
|
+
startedSession(NEW_THREAD_ID),
|
|
6745
6870
|
{
|
|
6746
6871
|
type: "result",
|
|
6747
6872
|
ok: false,
|
|
@@ -6761,8 +6886,9 @@ var init_conformance4 = __esm({
|
|
|
6761
6886
|
turnCompleted()
|
|
6762
6887
|
],
|
|
6763
6888
|
expected: [
|
|
6764
|
-
|
|
6889
|
+
startedSession(NEW_THREAD_ID),
|
|
6765
6890
|
{ type: "text", body: "All good now.", terminal: true },
|
|
6891
|
+
closingSession(NEW_THREAD_ID),
|
|
6766
6892
|
{ type: "result", ok: true }
|
|
6767
6893
|
]
|
|
6768
6894
|
},
|
|
@@ -6774,7 +6900,11 @@ var init_conformance4 = __esm({
|
|
|
6774
6900
|
name: "zero-content success (empty runtime result)",
|
|
6775
6901
|
request: makeRequest3(),
|
|
6776
6902
|
nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
|
|
6777
|
-
expected: [
|
|
6903
|
+
expected: [
|
|
6904
|
+
startedSession(NEW_THREAD_ID),
|
|
6905
|
+
closingSession(NEW_THREAD_ID),
|
|
6906
|
+
{ type: "result", ok: true }
|
|
6907
|
+
]
|
|
6778
6908
|
},
|
|
6779
6909
|
{
|
|
6780
6910
|
// Empty-final: a clean turn that ended on a tool call with no closing message.
|
|
@@ -6799,7 +6929,7 @@ var init_conformance4 = __esm({
|
|
|
6799
6929
|
turnCompleted()
|
|
6800
6930
|
],
|
|
6801
6931
|
expected: [
|
|
6802
|
-
|
|
6932
|
+
startedSession(NEW_THREAD_ID),
|
|
6803
6933
|
{
|
|
6804
6934
|
type: "tool",
|
|
6805
6935
|
id: "t1",
|
|
@@ -6817,6 +6947,7 @@ var init_conformance4 = __esm({
|
|
|
6817
6947
|
input: { changes: [{ path: "a.md", kind: "add" }] },
|
|
6818
6948
|
result: [{ path: "a.md", kind: "add" }]
|
|
6819
6949
|
},
|
|
6950
|
+
closingSession(NEW_THREAD_ID),
|
|
6820
6951
|
{ type: "result", ok: true }
|
|
6821
6952
|
]
|
|
6822
6953
|
},
|
|
@@ -6832,9 +6963,10 @@ var init_conformance4 = __esm({
|
|
|
6832
6963
|
turnCompleted()
|
|
6833
6964
|
],
|
|
6834
6965
|
expected: [
|
|
6835
|
-
|
|
6966
|
+
startedSession(NEW_THREAD_ID),
|
|
6836
6967
|
{ type: "thinking", text: "Let me check the notes." },
|
|
6837
6968
|
{ type: "text", body: "All set.", terminal: true },
|
|
6969
|
+
closingSession(NEW_THREAD_ID),
|
|
6838
6970
|
{ type: "result", ok: true }
|
|
6839
6971
|
]
|
|
6840
6972
|
},
|
|
@@ -6852,8 +6984,9 @@ var init_conformance4 = __esm({
|
|
|
6852
6984
|
}),
|
|
6853
6985
|
nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
|
|
6854
6986
|
expected: [
|
|
6855
|
-
|
|
6987
|
+
startedSession(NEW_THREAD_ID),
|
|
6856
6988
|
{ type: "text", body: "Done.", terminal: true },
|
|
6989
|
+
closingSession(NEW_THREAD_ID),
|
|
6857
6990
|
{
|
|
6858
6991
|
type: "result",
|
|
6859
6992
|
ok: true,
|
|
@@ -6875,7 +7008,8 @@ var init_conformance4 = __esm({
|
|
|
6875
7008
|
threadId: "th1",
|
|
6876
7009
|
cwd: DIR2,
|
|
6877
7010
|
promptFingerprint: fingerprintPrompt(PROMPT),
|
|
6878
|
-
usageBaseline: null
|
|
7011
|
+
usageBaseline: null,
|
|
7012
|
+
compactions: 0
|
|
6879
7013
|
})
|
|
6880
7014
|
}),
|
|
6881
7015
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
@@ -6897,8 +7031,9 @@ var init_conformance4 = __esm({
|
|
|
6897
7031
|
}),
|
|
6898
7032
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
6899
7033
|
expected: [
|
|
6900
|
-
|
|
7034
|
+
startedSession("th1"),
|
|
6901
7035
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7036
|
+
closingSession("th1"),
|
|
6902
7037
|
{ type: "result", ok: true }
|
|
6903
7038
|
]
|
|
6904
7039
|
},
|
|
@@ -6920,8 +7055,9 @@ var init_conformance4 = __esm({
|
|
|
6920
7055
|
}),
|
|
6921
7056
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
6922
7057
|
expected: [
|
|
6923
|
-
|
|
7058
|
+
startedSession("th1"),
|
|
6924
7059
|
{ type: "text", body: "Back again.", terminal: true },
|
|
7060
|
+
closingSession("th1"),
|
|
6925
7061
|
{ type: "result", ok: true }
|
|
6926
7062
|
]
|
|
6927
7063
|
},
|
|
@@ -6947,8 +7083,9 @@ var init_conformance4 = __esm({
|
|
|
6947
7083
|
turnCompleted()
|
|
6948
7084
|
],
|
|
6949
7085
|
expected: [
|
|
6950
|
-
|
|
7086
|
+
startedSession(NEW_THREAD_ID, DIR2, true),
|
|
6951
7087
|
{ type: "text", body: "Fresh start.", terminal: true },
|
|
7088
|
+
closingSession(NEW_THREAD_ID),
|
|
6952
7089
|
{ type: "result", ok: true }
|
|
6953
7090
|
]
|
|
6954
7091
|
}
|
|
@@ -6976,6 +7113,7 @@ var init_src = __esm({
|
|
|
6976
7113
|
init_empty_result();
|
|
6977
7114
|
init_failure();
|
|
6978
7115
|
init_turn_request();
|
|
7116
|
+
init_standing();
|
|
6979
7117
|
init_conformance();
|
|
6980
7118
|
init_transcript();
|
|
6981
7119
|
init_transcript_sink();
|
|
@@ -8529,6 +8667,7 @@ function buildCompanionTurnRequest(params) {
|
|
|
8529
8667
|
const { turnContext: t } = params;
|
|
8530
8668
|
return {
|
|
8531
8669
|
systemPrompt: t.systemPrompt,
|
|
8670
|
+
...t.standing ? { standing: t.standing } : {},
|
|
8532
8671
|
prompt: t.prompt,
|
|
8533
8672
|
content: t.content,
|
|
8534
8673
|
config: t.config,
|
|
@@ -8658,6 +8797,68 @@ var init_codex_images = __esm({
|
|
|
8658
8797
|
}
|
|
8659
8798
|
});
|
|
8660
8799
|
|
|
8800
|
+
// src/codex-compactions.ts
|
|
8801
|
+
import { createReadStream } from "fs";
|
|
8802
|
+
import { readdir } from "fs/promises";
|
|
8803
|
+
import { homedir as homedir8 } from "os";
|
|
8804
|
+
import { join as join23 } from "path";
|
|
8805
|
+
import { createInterface as createInterface2 } from "readline";
|
|
8806
|
+
function createCodexCompactionCounter(codexHome = process.env.CODEX_HOME || join23(homedir8(), ".codex")) {
|
|
8807
|
+
return async (threadId) => {
|
|
8808
|
+
try {
|
|
8809
|
+
const rollout = await findRollout(codexHome, threadId);
|
|
8810
|
+
return rollout ? await countCompactions(rollout) : null;
|
|
8811
|
+
} catch {
|
|
8812
|
+
return null;
|
|
8813
|
+
}
|
|
8814
|
+
};
|
|
8815
|
+
}
|
|
8816
|
+
async function findRollout(codexHome, threadId) {
|
|
8817
|
+
const id = UUID_V7.exec(threadId);
|
|
8818
|
+
if (!id) return null;
|
|
8819
|
+
const createdAt = parseInt(`${id[1]}${id[2]}`, 16);
|
|
8820
|
+
const suffix = `-${threadId}.jsonl`;
|
|
8821
|
+
for (const offset of [0, -DAY_MS, DAY_MS]) {
|
|
8822
|
+
const day = new Date(createdAt + offset);
|
|
8823
|
+
const dir2 = join23(
|
|
8824
|
+
codexHome,
|
|
8825
|
+
"sessions",
|
|
8826
|
+
String(day.getUTCFullYear()),
|
|
8827
|
+
String(day.getUTCMonth() + 1).padStart(2, "0"),
|
|
8828
|
+
String(day.getUTCDate()).padStart(2, "0")
|
|
8829
|
+
);
|
|
8830
|
+
let names;
|
|
8831
|
+
try {
|
|
8832
|
+
names = await readdir(dir2);
|
|
8833
|
+
} catch {
|
|
8834
|
+
continue;
|
|
8835
|
+
}
|
|
8836
|
+
const name = names.find((n) => n.startsWith("rollout-") && n.endsWith(suffix));
|
|
8837
|
+
if (name) return join23(dir2, name);
|
|
8838
|
+
}
|
|
8839
|
+
return null;
|
|
8840
|
+
}
|
|
8841
|
+
async function countCompactions(rollout) {
|
|
8842
|
+
const lines2 = createInterface2({
|
|
8843
|
+
input: createReadStream(rollout, { encoding: "utf8" }),
|
|
8844
|
+
crlfDelay: Infinity
|
|
8845
|
+
});
|
|
8846
|
+
let count = 0;
|
|
8847
|
+
for await (const line of lines2) {
|
|
8848
|
+
if (line.slice(0, RECORD_HEAD_CHARS).includes('"type":"compacted"')) count += 1;
|
|
8849
|
+
}
|
|
8850
|
+
return count;
|
|
8851
|
+
}
|
|
8852
|
+
var DAY_MS, UUID_V7, RECORD_HEAD_CHARS;
|
|
8853
|
+
var init_codex_compactions = __esm({
|
|
8854
|
+
"src/codex-compactions.ts"() {
|
|
8855
|
+
"use strict";
|
|
8856
|
+
DAY_MS = 864e5;
|
|
8857
|
+
UUID_V7 = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8858
|
+
RECORD_HEAD_CHARS = 200;
|
|
8859
|
+
}
|
|
8860
|
+
});
|
|
8861
|
+
|
|
8661
8862
|
// src/turn-seq-floor.ts
|
|
8662
8863
|
function resolveSeqFloor(sources, ctx2) {
|
|
8663
8864
|
const { serverFloor, outboxFloor } = sources;
|
|
@@ -8766,15 +8967,15 @@ var init_prepare_failure = __esm({
|
|
|
8766
8967
|
|
|
8767
8968
|
// src/prepared.ts
|
|
8768
8969
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync13, rmSync as rmSync9, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
|
|
8769
|
-
import { join as
|
|
8970
|
+
import { join as join24 } from "path";
|
|
8770
8971
|
function dirFor(workspaceId) {
|
|
8771
|
-
return
|
|
8972
|
+
return join24(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
8772
8973
|
}
|
|
8773
8974
|
function conversationDir(workspaceId, conversationId) {
|
|
8774
|
-
return
|
|
8975
|
+
return join24(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
8775
8976
|
}
|
|
8776
8977
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
8777
|
-
return
|
|
8978
|
+
return join24(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
8778
8979
|
}
|
|
8779
8980
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
8780
8981
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -8812,9 +9013,9 @@ var init_prepared = __esm({
|
|
|
8812
9013
|
|
|
8813
9014
|
// src/transcript-writer.ts
|
|
8814
9015
|
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
|
|
9016
|
+
import { basename as basename3, dirname as dirname12, join as join25 } from "path";
|
|
8816
9017
|
function transcriptsDir() {
|
|
8817
|
-
return
|
|
9018
|
+
return join25(runnerStateDir(), "transcripts");
|
|
8818
9019
|
}
|
|
8819
9020
|
function fileName(meta) {
|
|
8820
9021
|
const d = new Date(meta.ts);
|
|
@@ -8840,7 +9041,7 @@ function pruneOld(dir2, retain) {
|
|
|
8840
9041
|
const drop = files.sort().slice(0, files.length - retain);
|
|
8841
9042
|
for (const f of drop) {
|
|
8842
9043
|
try {
|
|
8843
|
-
rmSync10(
|
|
9044
|
+
rmSync10(join25(dir2, f), { force: true });
|
|
8844
9045
|
} catch {
|
|
8845
9046
|
}
|
|
8846
9047
|
}
|
|
@@ -8858,7 +9059,7 @@ var init_transcript_writer = __esm({
|
|
|
8858
9059
|
onWarn;
|
|
8859
9060
|
constructor(dir2, meta, onWarn) {
|
|
8860
9061
|
this.onWarn = onWarn;
|
|
8861
|
-
this.path =
|
|
9062
|
+
this.path = join25(dir2, fileName(meta));
|
|
8862
9063
|
try {
|
|
8863
9064
|
mkdirSync14(dir2, { recursive: true });
|
|
8864
9065
|
try {
|
|
@@ -8887,10 +9088,10 @@ var init_transcript_writer = __esm({
|
|
|
8887
9088
|
preserveAnomaly() {
|
|
8888
9089
|
if (this.broken) return;
|
|
8889
9090
|
try {
|
|
8890
|
-
const dir2 =
|
|
9091
|
+
const dir2 = join25(dirname12(this.path), "anomalies");
|
|
8891
9092
|
mkdirSync14(dir2, { recursive: true, mode: 448 });
|
|
8892
9093
|
chmodSync5(dir2, 448);
|
|
8893
|
-
const target =
|
|
9094
|
+
const target = join25(dir2, basename3(this.path));
|
|
8894
9095
|
copyFileSync2(this.path, target);
|
|
8895
9096
|
chmodSync5(target, 384);
|
|
8896
9097
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9190,6 +9391,7 @@ var init_turn_execution = __esm({
|
|
|
9190
9391
|
init_bundled_binaries();
|
|
9191
9392
|
init_codex_instructions();
|
|
9192
9393
|
init_codex_images();
|
|
9394
|
+
init_codex_compactions();
|
|
9193
9395
|
init_config();
|
|
9194
9396
|
init_api_error_shape();
|
|
9195
9397
|
init_errors();
|
|
@@ -9875,6 +10077,8 @@ var init_turn_execution = __esm({
|
|
|
9875
10077
|
turnId: this.turnId,
|
|
9876
10078
|
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
9877
10079
|
}),
|
|
10080
|
+
// Whether a resumed thread compacted since its standing preamble was sent.
|
|
10081
|
+
countCompactions: createCodexCompactionCounter(),
|
|
9878
10082
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
9879
10083
|
})
|
|
9880
10084
|
);
|
|
@@ -9935,6 +10139,9 @@ var init_turn_execution = __esm({
|
|
|
9935
10139
|
);
|
|
9936
10140
|
return {
|
|
9937
10141
|
systemPrompt: context.systemPrompt,
|
|
10142
|
+
// Replaced even when absent, so the segments can't outlive the
|
|
10143
|
+
// `systemPrompt` they were split from.
|
|
10144
|
+
standing: context.standing,
|
|
9938
10145
|
prompt: context.prompt,
|
|
9939
10146
|
content: context.content
|
|
9940
10147
|
};
|
|
@@ -10327,7 +10534,7 @@ var init_turn_execution = __esm({
|
|
|
10327
10534
|
// src/dispatcher.ts
|
|
10328
10535
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10329
10536
|
import { existsSync as existsSync15, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
10330
|
-
import { join as
|
|
10537
|
+
import { join as join26 } from "path";
|
|
10331
10538
|
function checkoutState(cwd) {
|
|
10332
10539
|
if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
|
|
10333
10540
|
if (!existsSync15(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
|
|
@@ -10340,7 +10547,7 @@ function checkoutState(cwd) {
|
|
|
10340
10547
|
if (entries.length === 0) {
|
|
10341
10548
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10342
10549
|
}
|
|
10343
|
-
const gitPath =
|
|
10550
|
+
const gitPath = join26(cwd, ".git");
|
|
10344
10551
|
if (!existsSync15(gitPath)) return { ok: true, reason: "usable" };
|
|
10345
10552
|
let stat;
|
|
10346
10553
|
try {
|
|
@@ -10348,7 +10555,7 @@ function checkoutState(cwd) {
|
|
|
10348
10555
|
} catch (error) {
|
|
10349
10556
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10350
10557
|
}
|
|
10351
|
-
if (stat.isDirectory() && !existsSync15(
|
|
10558
|
+
if (stat.isDirectory() && !existsSync15(join26(gitPath, "HEAD")))
|
|
10352
10559
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10353
10560
|
return { ok: true, reason: "usable" };
|
|
10354
10561
|
}
|
|
@@ -10726,8 +10933,8 @@ import {
|
|
|
10726
10933
|
} from "fs";
|
|
10727
10934
|
import { createRequire as createRequire3 } from "module";
|
|
10728
10935
|
import { isIP } from "net";
|
|
10729
|
-
import { homedir as
|
|
10730
|
-
import { dirname as dirname14, join as
|
|
10936
|
+
import { homedir as homedir9, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo3 } from "os";
|
|
10937
|
+
import { dirname as dirname14, join as join30, sep as sep5 } from "path";
|
|
10731
10938
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
10732
10939
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
10733
10940
|
function accountHome() {
|
|
@@ -10761,7 +10968,7 @@ async function resolvedAddresses(baseUrl) {
|
|
|
10761
10968
|
function companionPackageRoot(modulePath) {
|
|
10762
10969
|
for (let dir2 = dirname14(modulePath); ; dir2 = dirname14(dir2)) {
|
|
10763
10970
|
try {
|
|
10764
|
-
const manifest = JSON.parse(readFileSync16(
|
|
10971
|
+
const manifest = JSON.parse(readFileSync16(join30(dir2, "package.json"), "utf8"));
|
|
10765
10972
|
if (manifest.name === "@cabane/companion") return dir2;
|
|
10766
10973
|
} catch {
|
|
10767
10974
|
}
|
|
@@ -10838,22 +11045,22 @@ function companionStoreDenies(input) {
|
|
|
10838
11045
|
const always = [
|
|
10839
11046
|
cabaneDir2,
|
|
10840
11047
|
...homes.flatMap((home) => [
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
11048
|
+
join30(home, ".cabane"),
|
|
11049
|
+
join30(home, "*", ".cabane"),
|
|
11050
|
+
join30(home, "*", "*", ".cabane")
|
|
10844
11051
|
]),
|
|
10845
11052
|
// A glob anchored at the root has no directory to start from.
|
|
10846
|
-
...dirname14(parent) === parent ? [] : [
|
|
11053
|
+
...dirname14(parent) === parent ? [] : [join30(parent, "*", ".cabane")]
|
|
10847
11054
|
];
|
|
10848
11055
|
const companionHome = dirname14(cabaneDir2);
|
|
10849
|
-
const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [
|
|
11056
|
+
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
11057
|
return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
|
|
10851
11058
|
}
|
|
10852
11059
|
function companionStoreWatch(log2) {
|
|
10853
11060
|
return new CompanionStoreWatch({
|
|
10854
11061
|
log: log2,
|
|
10855
11062
|
roots: () => {
|
|
10856
|
-
const home =
|
|
11063
|
+
const home = homedir9();
|
|
10857
11064
|
return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
|
|
10858
11065
|
}
|
|
10859
11066
|
});
|
|
@@ -10863,7 +11070,7 @@ function isAtOrUnder(path, dir2) {
|
|
|
10863
11070
|
}
|
|
10864
11071
|
function expandHome(path, home) {
|
|
10865
11072
|
if (path === "~") return home;
|
|
10866
|
-
return path.startsWith("~/") ?
|
|
11073
|
+
return path.startsWith("~/") ? join30(home, path.slice(2)) : path;
|
|
10867
11074
|
}
|
|
10868
11075
|
function stripPort(pattern) {
|
|
10869
11076
|
return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
|
|
@@ -10881,8 +11088,8 @@ function patternsOverlap(a, b) {
|
|
|
10881
11088
|
}
|
|
10882
11089
|
function harnessSandboxConfig(input) {
|
|
10883
11090
|
const { policy } = input;
|
|
10884
|
-
const claudeDir = input.claudeConfigDir ??
|
|
10885
|
-
const codexDir = input.codexHome ??
|
|
11091
|
+
const claudeDir = input.claudeConfigDir ?? join30(input.home, ".claude");
|
|
11092
|
+
const codexDir = input.codexHome ?? join30(input.home, ".codex");
|
|
10886
11093
|
const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
|
|
10887
11094
|
const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
|
|
10888
11095
|
const cabane2 = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
|
|
@@ -10899,7 +11106,7 @@ function harnessSandboxConfig(input) {
|
|
|
10899
11106
|
foundStores: input.foundStores,
|
|
10900
11107
|
ownState: [claudeDir, codexDir]
|
|
10901
11108
|
}),
|
|
10902
|
-
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) =>
|
|
11109
|
+
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join30(home, p))),
|
|
10903
11110
|
...expand(policy.filesystem?.denyRead)
|
|
10904
11111
|
];
|
|
10905
11112
|
const strict = policy.filesystem?.mode === "strict";
|
|
@@ -10907,7 +11114,7 @@ function harnessSandboxConfig(input) {
|
|
|
10907
11114
|
const allowRead = strict ? [
|
|
10908
11115
|
...input.cwd ? [input.cwd] : [],
|
|
10909
11116
|
claudeDir,
|
|
10910
|
-
|
|
11117
|
+
join30(input.home, ".claude.json"),
|
|
10911
11118
|
codexDir,
|
|
10912
11119
|
...input.runtimePaths ?? [],
|
|
10913
11120
|
...expand(policy.filesystem?.allowRead)
|
|
@@ -10947,7 +11154,7 @@ function harnessSandboxConfig(input) {
|
|
|
10947
11154
|
...input.cwd ? [input.cwd] : [],
|
|
10948
11155
|
input.tmp,
|
|
10949
11156
|
claudeDir,
|
|
10950
|
-
|
|
11157
|
+
join30(input.home, ".claude.json"),
|
|
10951
11158
|
codexDir,
|
|
10952
11159
|
...expand(policy.filesystem?.allowWrite)
|
|
10953
11160
|
],
|
|
@@ -10998,7 +11205,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
10998
11205
|
baseUrl: input.baseUrl,
|
|
10999
11206
|
resolved: await resolvedAddresses(input.baseUrl),
|
|
11000
11207
|
ownAddresses: ownAddresses(),
|
|
11001
|
-
home:
|
|
11208
|
+
home: homedir9(),
|
|
11002
11209
|
accountHome: accountHome(),
|
|
11003
11210
|
tmp: tmpdir4(),
|
|
11004
11211
|
cabaneDir: cabaneDir(),
|
|
@@ -11049,9 +11256,9 @@ async function prepareHarnessSandbox(input) {
|
|
|
11049
11256
|
} finally {
|
|
11050
11257
|
for (const name of held) delete process.env[name];
|
|
11051
11258
|
}
|
|
11052
|
-
mkdirSync17(
|
|
11053
|
-
const dir2 = mkdtempSync(
|
|
11054
|
-
const script =
|
|
11259
|
+
mkdirSync17(join30(cabaneDir(), "sandbox"), { recursive: true });
|
|
11260
|
+
const dir2 = mkdtempSync(join30(cabaneDir(), "sandbox", "turn-"));
|
|
11261
|
+
const script = join30(dir2, "exec-sandboxed");
|
|
11055
11262
|
writeFileSync10(
|
|
11056
11263
|
script,
|
|
11057
11264
|
[
|
|
@@ -11067,7 +11274,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
11067
11274
|
return {
|
|
11068
11275
|
wrap: (command, args) => ({ command: script, args: [command, ...args] }),
|
|
11069
11276
|
executableFor: (real) => {
|
|
11070
|
-
const shim =
|
|
11277
|
+
const shim = join30(dir2, `exec-${++shims}`);
|
|
11071
11278
|
writeFileSync10(shim, `#!/bin/bash
|
|
11072
11279
|
exec ${shellQuote(script)} ${shellQuote(real)} "$@"
|
|
11073
11280
|
`);
|
|
@@ -11160,7 +11367,7 @@ import {
|
|
|
11160
11367
|
rmSync as rmSync15,
|
|
11161
11368
|
writeFileSync as writeFileSync11
|
|
11162
11369
|
} from "fs";
|
|
11163
|
-
import { join as
|
|
11370
|
+
import { join as join31 } from "path";
|
|
11164
11371
|
var MAX_ENTRIES, MAX_AGE_MS, Outbox;
|
|
11165
11372
|
var init_outbox = __esm({
|
|
11166
11373
|
"src/outbox.ts"() {
|
|
@@ -11178,10 +11385,10 @@ var init_outbox = __esm({
|
|
|
11178
11385
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
11179
11386
|
// cases route writes at the right tmpdir.
|
|
11180
11387
|
dir() {
|
|
11181
|
-
return
|
|
11388
|
+
return join31(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
11182
11389
|
}
|
|
11183
11390
|
fileFor(turnId, seq) {
|
|
11184
|
-
return
|
|
11391
|
+
return join31(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
11185
11392
|
}
|
|
11186
11393
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
11187
11394
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -11223,7 +11430,7 @@ var init_outbox = __esm({
|
|
|
11223
11430
|
const entries = [];
|
|
11224
11431
|
for (const name of names) {
|
|
11225
11432
|
if (!name.endsWith(".json")) continue;
|
|
11226
|
-
const full =
|
|
11433
|
+
const full = join31(dir2, name);
|
|
11227
11434
|
try {
|
|
11228
11435
|
const parsed = JSON.parse(readFileSync17(full, "utf8"));
|
|
11229
11436
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -11352,7 +11559,7 @@ var init_image_manifest = __esm({
|
|
|
11352
11559
|
|
|
11353
11560
|
// src/runner/exec-shim.ts
|
|
11354
11561
|
import { chmodSync as chmodSync8, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
11355
|
-
import { join as
|
|
11562
|
+
import { join as join35 } from "path";
|
|
11356
11563
|
function shellQuote2(value) {
|
|
11357
11564
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11358
11565
|
}
|
|
@@ -11362,10 +11569,10 @@ function createShimDir() {
|
|
|
11362
11569
|
return {
|
|
11363
11570
|
write(argv) {
|
|
11364
11571
|
if (!dir2) {
|
|
11365
|
-
mkdirSync20(
|
|
11366
|
-
dir2 = mkdtempSync2(
|
|
11572
|
+
mkdirSync20(join35(cabaneDir(), "runner"), { recursive: true });
|
|
11573
|
+
dir2 = mkdtempSync2(join35(cabaneDir(), "runner", "turn-"));
|
|
11367
11574
|
}
|
|
11368
|
-
const path =
|
|
11575
|
+
const path = join35(dir2, `exec-${++count}`);
|
|
11369
11576
|
writeFileSync13(path, `#!/bin/sh
|
|
11370
11577
|
exec ${argv.map(shellQuote2).join(" ")} "$@"
|
|
11371
11578
|
`);
|
|
@@ -11386,7 +11593,7 @@ var init_exec_shim = __esm({
|
|
|
11386
11593
|
});
|
|
11387
11594
|
|
|
11388
11595
|
// src/runner/launcher-channel.ts
|
|
11389
|
-
import { createInterface as
|
|
11596
|
+
import { createInterface as createInterface3 } from "readline";
|
|
11390
11597
|
function openLauncherChannel(stdin, emit, onLauncherGone) {
|
|
11391
11598
|
const waiting = /* @__PURE__ */ new Map();
|
|
11392
11599
|
const replies = /* @__PURE__ */ new Map();
|
|
@@ -11394,7 +11601,7 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
|
|
|
11394
11601
|
let resolveFirst;
|
|
11395
11602
|
let gotFirst = false;
|
|
11396
11603
|
const first = new Promise((resolve2) => resolveFirst = resolve2);
|
|
11397
|
-
const lines2 =
|
|
11604
|
+
const lines2 = createInterface3({ input: stdin, crlfDelay: Infinity });
|
|
11398
11605
|
lines2.on("line", (line) => {
|
|
11399
11606
|
if (!gotFirst) {
|
|
11400
11607
|
gotFirst = true;
|
|
@@ -15754,7 +15961,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
15754
15961
|
import { createHash as createHash5, randomUUID as randomUUID5 } from "crypto";
|
|
15755
15962
|
import { constants as constants3, tmpdir as tmpdir3 } from "os";
|
|
15756
15963
|
import {
|
|
15757
|
-
createReadStream,
|
|
15964
|
+
createReadStream as createReadStream2,
|
|
15758
15965
|
createWriteStream as createWriteStream2,
|
|
15759
15966
|
existsSync as existsSync18,
|
|
15760
15967
|
mkdirSync as mkdirSync16,
|
|
@@ -15765,7 +15972,7 @@ import {
|
|
|
15765
15972
|
statSync as statSync4,
|
|
15766
15973
|
writeFileSync as writeFileSync9
|
|
15767
15974
|
} from "fs";
|
|
15768
|
-
import { join as
|
|
15975
|
+
import { join as join29 } from "path";
|
|
15769
15976
|
import { Writable } from "stream";
|
|
15770
15977
|
|
|
15771
15978
|
// src/providers/docker-cli.ts
|
|
@@ -15838,7 +16045,7 @@ import {
|
|
|
15838
16045
|
renameSync as renameSync10,
|
|
15839
16046
|
rmSync as rmSync12
|
|
15840
16047
|
} from "fs";
|
|
15841
|
-
import { join as
|
|
16048
|
+
import { join as join27 } from "path";
|
|
15842
16049
|
function routes(t) {
|
|
15843
16050
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
15844
16051
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -15884,7 +16091,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15884
16091
|
const staged = readdirSync4(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
15885
16092
|
let entry = {};
|
|
15886
16093
|
try {
|
|
15887
|
-
entry = JSON.parse(readFileSync14(
|
|
16094
|
+
entry = JSON.parse(readFileSync14(join27(from, name), "utf8"));
|
|
15888
16095
|
} catch {
|
|
15889
16096
|
}
|
|
15890
16097
|
return { name, entry };
|
|
@@ -15897,7 +16104,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15897
16104
|
let published = 0;
|
|
15898
16105
|
for (const { name } of staged) {
|
|
15899
16106
|
try {
|
|
15900
|
-
move(
|
|
16107
|
+
move(join27(from, name), join27(to, name));
|
|
15901
16108
|
} catch {
|
|
15902
16109
|
return { published, complete: false };
|
|
15903
16110
|
}
|
|
@@ -15910,7 +16117,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15910
16117
|
// src/providers/runner-base.ts
|
|
15911
16118
|
import { createHash as createHash4 } from "crypto";
|
|
15912
16119
|
import { existsSync as existsSync17, realpathSync as realpathSync6 } from "fs";
|
|
15913
|
-
import { dirname as dirname13, join as
|
|
16120
|
+
import { dirname as dirname13, join as join28, sep as sep4 } from "path";
|
|
15914
16121
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15915
16122
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
15916
16123
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -15958,11 +16165,11 @@ async function ensureBaseImage(docker, image, log2) {
|
|
|
15958
16165
|
function ownInstall(moduleFile = fileURLToPath6(import.meta.url)) {
|
|
15959
16166
|
let dir2 = dirname13(moduleFile);
|
|
15960
16167
|
for (let i = 0; i < 4; i++) {
|
|
15961
|
-
if (existsSync17(
|
|
16168
|
+
if (existsSync17(join28(dir2, "dist", "cli.js")) && existsSync17(join28(dir2, "package.json"))) break;
|
|
15962
16169
|
dir2 = dirname13(dir2);
|
|
15963
16170
|
}
|
|
15964
|
-
if (!existsSync17(
|
|
15965
|
-
const sdk =
|
|
16171
|
+
if (!existsSync17(join28(dir2, "dist", "cli.js"))) return null;
|
|
16172
|
+
const sdk = join28(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
15966
16173
|
if (!existsSync17(sdk)) return null;
|
|
15967
16174
|
const root = realpathSync6(dir2);
|
|
15968
16175
|
return realpathSync6(sdk).startsWith(root + sep4) ? root : null;
|
|
@@ -16101,39 +16308,39 @@ function networkCreateError(stderr) {
|
|
|
16101
16308
|
}
|
|
16102
16309
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
16103
16310
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
16104
|
-
return
|
|
16311
|
+
return join29(deviceDir, "attempts", attemptId, "session.tar");
|
|
16105
16312
|
}
|
|
16106
16313
|
function pruneOtherAttempts(deviceDir, pair2, keep) {
|
|
16107
|
-
const root =
|
|
16314
|
+
const root = join29(deviceDir, "attempts");
|
|
16108
16315
|
if (!existsSync18(root)) return;
|
|
16109
16316
|
for (const id of readdirSync5(root)) {
|
|
16110
16317
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
16111
16318
|
let pairOf = "";
|
|
16112
16319
|
try {
|
|
16113
|
-
pairOf = readFileSync15(
|
|
16320
|
+
pairOf = readFileSync15(join29(root, id, "pair"), "utf8");
|
|
16114
16321
|
} catch {
|
|
16115
16322
|
continue;
|
|
16116
16323
|
}
|
|
16117
16324
|
if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
|
|
16118
|
-
rmSync13(
|
|
16325
|
+
rmSync13(join29(root, id), { recursive: true, force: true });
|
|
16119
16326
|
}
|
|
16120
16327
|
}
|
|
16121
16328
|
}
|
|
16122
16329
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
16123
16330
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
16124
|
-
const root =
|
|
16331
|
+
const root = join29(deviceDir, "attempts");
|
|
16125
16332
|
if (!existsSync18(root)) return [];
|
|
16126
16333
|
const removed = [];
|
|
16127
16334
|
for (const id of readdirSync5(root)) {
|
|
16128
16335
|
if (!UUID_RE.test(id)) continue;
|
|
16129
16336
|
let kept;
|
|
16130
16337
|
try {
|
|
16131
|
-
kept = statSync4(
|
|
16338
|
+
kept = statSync4(join29(root, id)).mtimeMs;
|
|
16132
16339
|
} catch {
|
|
16133
16340
|
continue;
|
|
16134
16341
|
}
|
|
16135
16342
|
if (now - kept < maxAgeMs) continue;
|
|
16136
|
-
rmSync13(
|
|
16343
|
+
rmSync13(join29(root, id), { recursive: true, force: true });
|
|
16137
16344
|
removed.push(id);
|
|
16138
16345
|
}
|
|
16139
16346
|
return removed;
|
|
@@ -16334,11 +16541,11 @@ function createDockerProvider(opts) {
|
|
|
16334
16541
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
16335
16542
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
16336
16543
|
};
|
|
16337
|
-
const socketRoot = opts.socketRoot ??
|
|
16544
|
+
const socketRoot = opts.socketRoot ?? join29(
|
|
16338
16545
|
process.env.XDG_RUNTIME_DIR && existsSync18(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
16339
16546
|
`cabane-runners-${opts.deviceKey}`
|
|
16340
16547
|
);
|
|
16341
|
-
const socketDir = (name) =>
|
|
16548
|
+
const socketDir = (name) => join29(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
|
|
16342
16549
|
const create = async (name, attempt) => {
|
|
16343
16550
|
mkdirSync16(socketRoot, { recursive: true, mode: 448 });
|
|
16344
16551
|
mkdirSync16(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -16480,7 +16687,7 @@ function createDockerProvider(opts) {
|
|
|
16480
16687
|
}
|
|
16481
16688
|
};
|
|
16482
16689
|
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) =>
|
|
16690
|
+
const pendingPath = (name) => join29(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
16484
16691
|
const readPending = (name) => {
|
|
16485
16692
|
try {
|
|
16486
16693
|
const ids = JSON.parse(readFileSync15(pendingPath(name), "utf8"));
|
|
@@ -16494,12 +16701,12 @@ function createDockerProvider(opts) {
|
|
|
16494
16701
|
rmSync13(pendingPath(name), { force: true });
|
|
16495
16702
|
return;
|
|
16496
16703
|
}
|
|
16497
|
-
mkdirSync16(
|
|
16704
|
+
mkdirSync16(join29(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
16498
16705
|
writeFileSync9(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
16499
16706
|
};
|
|
16500
16707
|
const keepSession = async (name, attempt, attemptId) => {
|
|
16501
16708
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16502
|
-
mkdirSync16(
|
|
16709
|
+
mkdirSync16(join29(archive, ".."), { recursive: true });
|
|
16503
16710
|
const partial = `${archive}.${process.pid}.partial`;
|
|
16504
16711
|
const file = new CappedFile(partial, sessionCap);
|
|
16505
16712
|
const r = await execIn(
|
|
@@ -16516,7 +16723,7 @@ function createDockerProvider(opts) {
|
|
|
16516
16723
|
);
|
|
16517
16724
|
if (r.code === 0 && !file.over) {
|
|
16518
16725
|
renameSync11(partial, archive);
|
|
16519
|
-
writeFileSync9(
|
|
16726
|
+
writeFileSync9(join29(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
16520
16727
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
16521
16728
|
await writeMarker(name, attemptId);
|
|
16522
16729
|
log2.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -16530,7 +16737,7 @@ function createDockerProvider(opts) {
|
|
|
16530
16737
|
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
16738
|
);
|
|
16532
16739
|
};
|
|
16533
|
-
const stagingDir = (name) =>
|
|
16740
|
+
const stagingDir = (name) => join29(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
16534
16741
|
const ensureRunning = async (name) => {
|
|
16535
16742
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
16536
16743
|
if (seen.code !== 0) return false;
|
|
@@ -16538,7 +16745,7 @@ function createDockerProvider(opts) {
|
|
|
16538
16745
|
return (await docker.run(["start", name])).code === 0;
|
|
16539
16746
|
};
|
|
16540
16747
|
const dropArchive = (attemptId) => {
|
|
16541
|
-
rmSync13(
|
|
16748
|
+
rmSync13(join29(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
16542
16749
|
recursive: true,
|
|
16543
16750
|
force: true
|
|
16544
16751
|
});
|
|
@@ -16607,7 +16814,7 @@ function createDockerProvider(opts) {
|
|
|
16607
16814
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
16608
16815
|
await must(["start", name], "starting the container");
|
|
16609
16816
|
const git = await serveGitCredentials({
|
|
16610
|
-
socketPath:
|
|
16817
|
+
socketPath: join29(socketDir(name), "git.sock"),
|
|
16611
16818
|
hosts: opts.gitHosts,
|
|
16612
16819
|
fill: opts.gitFill,
|
|
16613
16820
|
log: log2
|
|
@@ -16643,7 +16850,7 @@ function createDockerProvider(opts) {
|
|
|
16643
16850
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16644
16851
|
if (existsSync18(archive)) {
|
|
16645
16852
|
const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
|
|
16646
|
-
input:
|
|
16853
|
+
input: createReadStream2(archive)
|
|
16647
16854
|
});
|
|
16648
16855
|
if (r.code !== 0) {
|
|
16649
16856
|
log2.warn(
|
|
@@ -16756,15 +16963,15 @@ function createDockerProvider(opts) {
|
|
|
16756
16963
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
16757
16964
|
if (transcripts) {
|
|
16758
16965
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
16759
|
-
if (kept.length) mkdirSync16(
|
|
16760
|
-
for (const f of kept) writeFileSync9(
|
|
16966
|
+
if (kept.length) mkdirSync16(join29(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
16967
|
+
for (const f of kept) writeFileSync9(join29(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
16761
16968
|
await removeIn(
|
|
16762
16969
|
name,
|
|
16763
16970
|
`${CONTAINER_STATE}/transcripts`,
|
|
16764
16971
|
kept.map((f) => f.name)
|
|
16765
16972
|
);
|
|
16766
16973
|
}
|
|
16767
|
-
const outboxRel =
|
|
16974
|
+
const outboxRel = join29("outbox", encodeURIComponent(attempt.agentId));
|
|
16768
16975
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
16769
16976
|
let unpublished = false;
|
|
16770
16977
|
if (outbox) {
|
|
@@ -16790,7 +16997,7 @@ function createDockerProvider(opts) {
|
|
|
16790
16997
|
const staged = stagingDir(name);
|
|
16791
16998
|
mkdirSync16(staged, { recursive: true });
|
|
16792
16999
|
writeFileSync9(
|
|
16793
|
-
|
|
17000
|
+
join29(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
16794
17001
|
`${JSON.stringify(entry)}
|
|
16795
17002
|
`
|
|
16796
17003
|
);
|
|
@@ -16803,7 +17010,7 @@ function createDockerProvider(opts) {
|
|
|
16803
17010
|
if (outbox.complete) {
|
|
16804
17011
|
const published = publishStaged(
|
|
16805
17012
|
stagingDir(name),
|
|
16806
|
-
|
|
17013
|
+
join29(opts.hostStateDir, outboxRel),
|
|
16807
17014
|
opts.moveFile
|
|
16808
17015
|
);
|
|
16809
17016
|
queued = published.published;
|
|
@@ -18778,9 +18985,9 @@ function handleUncaught(log2, err, origin) {
|
|
|
18778
18985
|
// src/crash-marker.ts
|
|
18779
18986
|
init_config();
|
|
18780
18987
|
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as readFileSync18, rmSync as rmSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
18781
|
-
import { join as
|
|
18988
|
+
import { join as join32 } from "path";
|
|
18782
18989
|
function crashMarkerPath() {
|
|
18783
|
-
return
|
|
18990
|
+
return join32(cabaneDir(), "last-error.json");
|
|
18784
18991
|
}
|
|
18785
18992
|
function recordCrash(rec2) {
|
|
18786
18993
|
try {
|
|
@@ -19093,8 +19300,8 @@ async function runAttached(runtime) {
|
|
|
19093
19300
|
init_install_kind();
|
|
19094
19301
|
init_cabane_command();
|
|
19095
19302
|
import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync21 } from "fs";
|
|
19096
|
-
import { homedir as
|
|
19097
|
-
import { dirname as dirname15, join as
|
|
19303
|
+
import { homedir as homedir10 } from "os";
|
|
19304
|
+
import { dirname as dirname15, join as join33 } from "path";
|
|
19098
19305
|
init_config();
|
|
19099
19306
|
init_sandbox_policy();
|
|
19100
19307
|
init_secrets();
|
|
@@ -19163,7 +19370,7 @@ function versionLine() {
|
|
|
19163
19370
|
const install = companionInstall();
|
|
19164
19371
|
const own = installedCommandPath({ install });
|
|
19165
19372
|
if (install === "script") {
|
|
19166
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(
|
|
19373
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join33(homedir10(), ".cabane"))})`;
|
|
19167
19374
|
}
|
|
19168
19375
|
if (own)
|
|
19169
19376
|
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname15(dirname15(own)))})`;
|
|
@@ -19189,8 +19396,8 @@ function otherInstall(install, own) {
|
|
|
19189
19396
|
}
|
|
19190
19397
|
};
|
|
19191
19398
|
if (install === "npm") {
|
|
19192
|
-
const script =
|
|
19193
|
-
|
|
19399
|
+
const script = join33(
|
|
19400
|
+
homedir10(),
|
|
19194
19401
|
".cabane",
|
|
19195
19402
|
"node",
|
|
19196
19403
|
"lib",
|
|
@@ -19198,11 +19405,11 @@ function otherInstall(install, own) {
|
|
|
19198
19405
|
"@cabane",
|
|
19199
19406
|
"companion"
|
|
19200
19407
|
);
|
|
19201
|
-
return existsSync21(script) ?
|
|
19408
|
+
return existsSync21(script) ? join33(homedir10(), ".cabane") : null;
|
|
19202
19409
|
}
|
|
19203
19410
|
const found = firstOnPath("cabane-companion", process.env.PATH);
|
|
19204
19411
|
if (!found) return null;
|
|
19205
|
-
const ownAlias = own ? real(
|
|
19412
|
+
const ownAlias = own ? real(join33(dirname15(own), "cabane-companion")) : null;
|
|
19206
19413
|
return real(found) === ownAlias ? null : dirname15(found);
|
|
19207
19414
|
}
|
|
19208
19415
|
async function liveState() {
|
|
@@ -19429,7 +19636,7 @@ init_errors();
|
|
|
19429
19636
|
init_src();
|
|
19430
19637
|
init_transcript_writer();
|
|
19431
19638
|
import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "fs";
|
|
19432
|
-
import { isAbsolute, join as
|
|
19639
|
+
import { isAbsolute, join as join34 } from "path";
|
|
19433
19640
|
async function transcript(opts = {}) {
|
|
19434
19641
|
const dir2 = transcriptsDir();
|
|
19435
19642
|
if (opts.follow) {
|
|
@@ -19446,7 +19653,7 @@ async function transcript(opts = {}) {
|
|
|
19446
19653
|
process.stdout.write(emptyMessage(dir2));
|
|
19447
19654
|
return;
|
|
19448
19655
|
}
|
|
19449
|
-
process.stdout.write(renderFile(
|
|
19656
|
+
process.stdout.write(renderFile(join34(dir2, newest)) + "\n");
|
|
19450
19657
|
return;
|
|
19451
19658
|
}
|
|
19452
19659
|
printList(dir2);
|
|
@@ -19529,7 +19736,7 @@ function isComplete(content) {
|
|
|
19529
19736
|
async function followTranscripts(dir2) {
|
|
19530
19737
|
const follower = new TranscriptFollower({
|
|
19531
19738
|
listFiles: () => listFiles(dir2),
|
|
19532
|
-
read: (f) => readFileSync19(
|
|
19739
|
+
read: (f) => readFileSync19(join34(dir2, f), "utf8"),
|
|
19533
19740
|
write: (s) => process.stdout.write(s),
|
|
19534
19741
|
// CSI: cursor up `n` lines, then erase from cursor to end of screen.
|
|
19535
19742
|
clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
|
|
@@ -19569,7 +19776,7 @@ function printList(dir2) {
|
|
|
19569
19776
|
|
|
19570
19777
|
`);
|
|
19571
19778
|
for (const f of files.slice(0, 20)) {
|
|
19572
|
-
const { meta, outcome } = peek(
|
|
19779
|
+
const { meta, outcome } = peek(join34(dir2, f));
|
|
19573
19780
|
const when = fmtTime(rec(meta)?.ts);
|
|
19574
19781
|
const ws = str2(rec(meta)?.workspaceSlug);
|
|
19575
19782
|
const o = rec(outcome);
|
|
@@ -19607,10 +19814,10 @@ function resolveTarget(dir2, target) {
|
|
|
19607
19814
|
if (existsSync22(target)) return target;
|
|
19608
19815
|
throw new CompanionError(`no transcript at ${target}.`);
|
|
19609
19816
|
}
|
|
19610
|
-
const exact =
|
|
19817
|
+
const exact = join34(dir2, target);
|
|
19611
19818
|
if (existsSync22(exact)) return exact;
|
|
19612
19819
|
const matches = listFiles(dir2).filter((f) => f.includes(target));
|
|
19613
|
-
if (matches.length === 1) return
|
|
19820
|
+
if (matches.length === 1) return join34(dir2, matches[0]);
|
|
19614
19821
|
if (matches.length === 0) {
|
|
19615
19822
|
throw new CompanionError(
|
|
19616
19823
|
`no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`
|