@cabane/companion 0.6.189 → 0.6.191
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 +79 -43
- package/dist/runtime.js +69 -38
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5845,8 +5845,6 @@ var init_run_spec2 = __esm({
|
|
|
5845
5845
|
TURN_CONTROL_MCP_SERVER2 = "cabane_companion";
|
|
5846
5846
|
ACTIVE_CONVERSATION_HEADER3 = "x-cabane-active-conversation";
|
|
5847
5847
|
ENV_ENVELOPE_KEYS = [
|
|
5848
|
-
"CABANE_PLAYGROUND",
|
|
5849
|
-
"CABANE_PLAYGROUND_BIN",
|
|
5850
5848
|
"CABANE_CONVERSATION_ID",
|
|
5851
5849
|
"CABANE_AGENT_ID",
|
|
5852
5850
|
"CABANE_CONVERSATION_TITLE"
|
|
@@ -5972,6 +5970,10 @@ var init_transport2 = __esm({
|
|
|
5972
5970
|
});
|
|
5973
5971
|
|
|
5974
5972
|
// packages/agent-runtime/src/codex/index.ts
|
|
5973
|
+
function isMissingRollout(err) {
|
|
5974
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
5975
|
+
return /no rollout found for thread id/i.test(message2);
|
|
5976
|
+
}
|
|
5975
5977
|
function createCodexAdapter(deps = {}) {
|
|
5976
5978
|
const transport = deps.transport ?? (deps.enabled ? createSdkCodexTransport({ ...deps.apiKey ? { apiKey: deps.apiKey } : {} }) : null);
|
|
5977
5979
|
return {
|
|
@@ -5990,8 +5992,8 @@ function createCodexAdapter(deps = {}) {
|
|
|
5990
5992
|
}
|
|
5991
5993
|
const directory = req.local.cwd ?? "";
|
|
5992
5994
|
const decision = decideResume3(req.session, directory);
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
+
let resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
5996
|
+
let threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
5995
5997
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
5996
5998
|
deps.onWarn?.(
|
|
5997
5999
|
"codex adapter: stored session directory no longer matches the current environment \u2014 starting a fresh thread (SJ527-analogue guard)",
|
|
@@ -6056,38 +6058,69 @@ function createCodexAdapter(deps = {}) {
|
|
|
6056
6058
|
}
|
|
6057
6059
|
if (signal.aborted) return;
|
|
6058
6060
|
}
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
model
|
|
6063
|
-
|
|
6061
|
+
for (; ; ) {
|
|
6062
|
+
const spec = buildRunSpec2(req, resumeThreadId, threadStanding, images?.paths ?? []);
|
|
6063
|
+
if (spec.model === null) {
|
|
6064
|
+
deps.onWarn?.("Codex is using its default model because no model was resolved", {
|
|
6065
|
+
model: req.config.model
|
|
6066
|
+
});
|
|
6067
|
+
}
|
|
6068
|
+
let emitted = false;
|
|
6069
|
+
try {
|
|
6070
|
+
const result = await transport.run(spec, signal);
|
|
6071
|
+
if (signal.aborted) return;
|
|
6072
|
+
for await (const event of decodeCodexStream(result.events, {
|
|
6073
|
+
signal,
|
|
6074
|
+
cwd: req.local.cwd,
|
|
6075
|
+
resumedThreadId: resumeThreadId,
|
|
6076
|
+
degraded,
|
|
6077
|
+
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6078
|
+
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6079
|
+
// the spec, so the record can't disagree with what was sent.
|
|
6080
|
+
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6081
|
+
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6082
|
+
compactions,
|
|
6083
|
+
// What the thread held before this turn, for the write at `thread.started`.
|
|
6084
|
+
heldStanding: {
|
|
6085
|
+
promptFingerprint: threadStanding?.core ?? null,
|
|
6086
|
+
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6087
|
+
compactions: threadStanding ? compactions : null
|
|
6088
|
+
},
|
|
6089
|
+
storedState: req.session,
|
|
6090
|
+
usageBaseline: threadUsageBaseline,
|
|
6091
|
+
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
6092
|
+
// default token). Same for the reasoning effort.
|
|
6093
|
+
resolvedModel: spec.model,
|
|
6094
|
+
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
6095
|
+
})) {
|
|
6096
|
+
emitted = true;
|
|
6097
|
+
yield event;
|
|
6098
|
+
}
|
|
6099
|
+
return;
|
|
6100
|
+
} catch (err) {
|
|
6101
|
+
if (resumeThreadId === null || emitted || signal.aborted || !isMissingRollout(err)) {
|
|
6102
|
+
throw err;
|
|
6103
|
+
}
|
|
6104
|
+
deps.onWarn?.(
|
|
6105
|
+
"codex adapter: the stored thread has no rollout on this host \u2014 starting a fresh thread",
|
|
6106
|
+
{ threadId: resumeThreadId }
|
|
6107
|
+
);
|
|
6108
|
+
resumeThreadId = null;
|
|
6109
|
+
threadUsageBaseline = null;
|
|
6110
|
+
threadStanding = null;
|
|
6111
|
+
compactions = 0;
|
|
6112
|
+
req = { ...req, session: null };
|
|
6113
|
+
degraded = true;
|
|
6114
|
+
if (hooks?.fresh) {
|
|
6115
|
+
const recomposed = await hooks.fresh("session_start_failed");
|
|
6116
|
+
if (signal.aborted) return;
|
|
6117
|
+
if (recomposed) {
|
|
6118
|
+
req = { ...req, ...recomposed, session: null };
|
|
6119
|
+
degraded = false;
|
|
6120
|
+
}
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6064
6123
|
}
|
|
6065
|
-
const result = await transport.run(spec, signal);
|
|
6066
|
-
if (signal.aborted) return;
|
|
6067
|
-
yield* decodeCodexStream(result.events, {
|
|
6068
|
-
signal,
|
|
6069
|
-
cwd: req.local.cwd,
|
|
6070
|
-
resumedThreadId: resumeThreadId,
|
|
6071
|
-
degraded,
|
|
6072
|
-
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6073
|
-
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6074
|
-
// the spec, so the record can't disagree with what was sent.
|
|
6075
|
-
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6076
|
-
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6077
|
-
compactions,
|
|
6078
|
-
// What the thread held before this turn, for the write at `thread.started`.
|
|
6079
|
-
heldStanding: {
|
|
6080
|
-
promptFingerprint: threadStanding?.core ?? null,
|
|
6081
|
-
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6082
|
-
compactions: threadStanding ? compactions : null
|
|
6083
|
-
},
|
|
6084
|
-
storedState: req.session,
|
|
6085
|
-
usageBaseline: threadUsageBaseline,
|
|
6086
|
-
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
6087
|
-
// default token). Same for the reasoning effort.
|
|
6088
|
-
resolvedModel: spec.model,
|
|
6089
|
-
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
6090
|
-
});
|
|
6091
6124
|
} finally {
|
|
6092
6125
|
if (images) {
|
|
6093
6126
|
try {
|
|
@@ -7512,9 +7545,11 @@ function required(env, name) {
|
|
|
7512
7545
|
function runnerPlace(env) {
|
|
7513
7546
|
const workRoot2 = env[PLACE_ENV.workRoot];
|
|
7514
7547
|
const transcriptDir = env[PLACE_ENV.transcriptDir];
|
|
7548
|
+
const box = env[PLACE_ENV.box];
|
|
7515
7549
|
return {
|
|
7516
7550
|
...workRoot2 ? { workRoot: workRoot2 } : {},
|
|
7517
|
-
...transcriptDir ? { transcriptDir } : {}
|
|
7551
|
+
...transcriptDir ? { transcriptDir } : {},
|
|
7552
|
+
...box ? { box } : {}
|
|
7518
7553
|
};
|
|
7519
7554
|
}
|
|
7520
7555
|
function parseRunnerInput(env, stdin) {
|
|
@@ -7596,7 +7631,8 @@ var init_protocol = __esm({
|
|
|
7596
7631
|
};
|
|
7597
7632
|
PLACE_ENV = {
|
|
7598
7633
|
workRoot: "CABANE_WORK_ROOT",
|
|
7599
|
-
transcriptDir: "CABANE_TRANSCRIPT_DIR"
|
|
7634
|
+
transcriptDir: "CABANE_TRANSCRIPT_DIR",
|
|
7635
|
+
box: "CABANE_BOX"
|
|
7600
7636
|
};
|
|
7601
7637
|
runtimeHealthSchema = z14.object({
|
|
7602
7638
|
quotaState: z14.enum(["ok", "limited", "auth_expired", "unknown"]),
|
|
@@ -7667,8 +7703,7 @@ function runnerExecEnv(base, boxed) {
|
|
|
7667
7703
|
const env = {};
|
|
7668
7704
|
for (const [k, v] of Object.entries(base)) {
|
|
7669
7705
|
if (v === void 0) continue;
|
|
7670
|
-
if (boxed &&
|
|
7671
|
-
continue;
|
|
7706
|
+
if (boxed && BOXED_ENV_DROPPED.includes(k)) continue;
|
|
7672
7707
|
env[k] = v;
|
|
7673
7708
|
}
|
|
7674
7709
|
return env;
|
|
@@ -8006,7 +8041,7 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
8006
8041
|
duties.notifyEnd({ id: payload.messageId, ok: false, durationMs, reason });
|
|
8007
8042
|
return { ok: false, durationMs, reason };
|
|
8008
8043
|
}
|
|
8009
|
-
var BOXED_ENV_DROPPED,
|
|
8044
|
+
var BOXED_ENV_DROPPED, PINO_LEVELS;
|
|
8010
8045
|
var init_launch = __esm({
|
|
8011
8046
|
"src/runner/launch.ts"() {
|
|
8012
8047
|
"use strict";
|
|
@@ -8026,7 +8061,6 @@ var init_launch = __esm({
|
|
|
8026
8061
|
"DBUS_SESSION_BUS_ADDRESS",
|
|
8027
8062
|
"XDG_RUNTIME_DIR"
|
|
8028
8063
|
];
|
|
8029
|
-
BOXED_ENV_DROPPED_PREFIX = "CABANE_GATE_";
|
|
8030
8064
|
PINO_LEVELS = {
|
|
8031
8065
|
10: "trace",
|
|
8032
8066
|
20: "debug",
|
|
@@ -9547,7 +9581,7 @@ var init_turn_execution = __esm({
|
|
|
9547
9581
|
async prepareEnvironment() {
|
|
9548
9582
|
const { payload, workspaceId, turnId, turnLog } = this;
|
|
9549
9583
|
const prepareHook = this.opts.local.prepareHook;
|
|
9550
|
-
const pairDir = pairDirectory(this.opts.workRoot, {
|
|
9584
|
+
const pairDir = this.opts.boxed ? this.opts.workRoot : pairDirectory(this.opts.workRoot, {
|
|
9551
9585
|
workspaceSlug: this.opts.workspaceSlug,
|
|
9552
9586
|
agentUsername: this.opts.agentUsername,
|
|
9553
9587
|
conversationId: payload.conversationId
|
|
@@ -10773,8 +10807,10 @@ function runnerTurnOptions(input, log2, emit, extras = {}) {
|
|
|
10773
10807
|
local: spec.local,
|
|
10774
10808
|
// The place the runner stands in names its own root and transcript
|
|
10775
10809
|
// directory when it has them (`PLACE_ENV`); the launcher's host-computed
|
|
10776
|
-
// values are what a runner on the person's own machine gets.
|
|
10810
|
+
// values are what a runner on the person's own machine gets. A box is one
|
|
10811
|
+
// conversation × agent's, so the turn stands in its root itself.
|
|
10777
10812
|
workRoot: input.place?.workRoot ?? spec.workRoot,
|
|
10813
|
+
...input.place?.box ? { boxed: true } : {},
|
|
10778
10814
|
runConfig: spec.runConfig,
|
|
10779
10815
|
// The launcher's copy of the device's secrets, when it sent one: a runner
|
|
10780
10816
|
// in a box has no `secrets.json` to read.
|
package/dist/runtime.js
CHANGED
|
@@ -5898,8 +5898,6 @@ var CABANE_MCP_SERVER3 = "cabane";
|
|
|
5898
5898
|
var TURN_CONTROL_MCP_SERVER2 = "cabane_companion";
|
|
5899
5899
|
var ACTIVE_CONVERSATION_HEADER3 = "x-cabane-active-conversation";
|
|
5900
5900
|
var ENV_ENVELOPE_KEYS = [
|
|
5901
|
-
"CABANE_PLAYGROUND",
|
|
5902
|
-
"CABANE_PLAYGROUND_BIN",
|
|
5903
5901
|
"CABANE_CONVERSATION_ID",
|
|
5904
5902
|
"CABANE_AGENT_ID",
|
|
5905
5903
|
"CABANE_CONVERSATION_TITLE"
|
|
@@ -6158,6 +6156,10 @@ function createSdkCodexTransport(opts = {}) {
|
|
|
6158
6156
|
}
|
|
6159
6157
|
|
|
6160
6158
|
// packages/agent-runtime/src/codex/index.ts
|
|
6159
|
+
function isMissingRollout(err) {
|
|
6160
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6161
|
+
return /no rollout found for thread id/i.test(message);
|
|
6162
|
+
}
|
|
6161
6163
|
function createCodexAdapter(deps = {}) {
|
|
6162
6164
|
const transport = deps.transport ?? (deps.enabled ? createSdkCodexTransport({ ...deps.apiKey ? { apiKey: deps.apiKey } : {} }) : null);
|
|
6163
6165
|
return {
|
|
@@ -6176,8 +6178,8 @@ function createCodexAdapter(deps = {}) {
|
|
|
6176
6178
|
}
|
|
6177
6179
|
const directory = req.local.cwd ?? "";
|
|
6178
6180
|
const decision = decideResume3(req.session, directory);
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
+
let resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
6182
|
+
let threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
|
|
6181
6183
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
6182
6184
|
deps.onWarn?.(
|
|
6183
6185
|
"codex adapter: stored session directory no longer matches the current environment \u2014 starting a fresh thread (SJ527-analogue guard)",
|
|
@@ -6242,38 +6244,69 @@ function createCodexAdapter(deps = {}) {
|
|
|
6242
6244
|
}
|
|
6243
6245
|
if (signal.aborted) return;
|
|
6244
6246
|
}
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
model
|
|
6249
|
-
|
|
6247
|
+
for (; ; ) {
|
|
6248
|
+
const spec = buildRunSpec2(req, resumeThreadId, threadStanding, images?.paths ?? []);
|
|
6249
|
+
if (spec.model === null) {
|
|
6250
|
+
deps.onWarn?.("Codex is using its default model because no model was resolved", {
|
|
6251
|
+
model: req.config.model
|
|
6252
|
+
});
|
|
6253
|
+
}
|
|
6254
|
+
let emitted = false;
|
|
6255
|
+
try {
|
|
6256
|
+
const result = await transport.run(spec, signal);
|
|
6257
|
+
if (signal.aborted) return;
|
|
6258
|
+
for await (const event of decodeCodexStream(result.events, {
|
|
6259
|
+
signal,
|
|
6260
|
+
cwd: req.local.cwd,
|
|
6261
|
+
resumedThreadId: resumeThreadId,
|
|
6262
|
+
degraded,
|
|
6263
|
+
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6264
|
+
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6265
|
+
// the spec, so the record can't disagree with what was sent.
|
|
6266
|
+
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6267
|
+
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6268
|
+
compactions,
|
|
6269
|
+
// What the thread held before this turn, for the write at `thread.started`.
|
|
6270
|
+
heldStanding: {
|
|
6271
|
+
promptFingerprint: threadStanding?.core ?? null,
|
|
6272
|
+
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6273
|
+
compactions: threadStanding ? compactions : null
|
|
6274
|
+
},
|
|
6275
|
+
storedState: req.session,
|
|
6276
|
+
usageBaseline: threadUsageBaseline,
|
|
6277
|
+
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
6278
|
+
// default token). Same for the reasoning effort.
|
|
6279
|
+
resolvedModel: spec.model,
|
|
6280
|
+
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
6281
|
+
})) {
|
|
6282
|
+
emitted = true;
|
|
6283
|
+
yield event;
|
|
6284
|
+
}
|
|
6285
|
+
return;
|
|
6286
|
+
} catch (err) {
|
|
6287
|
+
if (resumeThreadId === null || emitted || signal.aborted || !isMissingRollout(err)) {
|
|
6288
|
+
throw err;
|
|
6289
|
+
}
|
|
6290
|
+
deps.onWarn?.(
|
|
6291
|
+
"codex adapter: the stored thread has no rollout on this host \u2014 starting a fresh thread",
|
|
6292
|
+
{ threadId: resumeThreadId }
|
|
6293
|
+
);
|
|
6294
|
+
resumeThreadId = null;
|
|
6295
|
+
threadUsageBaseline = null;
|
|
6296
|
+
threadStanding = null;
|
|
6297
|
+
compactions = 0;
|
|
6298
|
+
req = { ...req, session: null };
|
|
6299
|
+
degraded = true;
|
|
6300
|
+
if (hooks?.fresh) {
|
|
6301
|
+
const recomposed = await hooks.fresh("session_start_failed");
|
|
6302
|
+
if (signal.aborted) return;
|
|
6303
|
+
if (recomposed) {
|
|
6304
|
+
req = { ...req, ...recomposed, session: null };
|
|
6305
|
+
degraded = false;
|
|
6306
|
+
}
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6250
6309
|
}
|
|
6251
|
-
const result = await transport.run(spec, signal);
|
|
6252
|
-
if (signal.aborted) return;
|
|
6253
|
-
yield* decodeCodexStream(result.events, {
|
|
6254
|
-
signal,
|
|
6255
|
-
cwd: req.local.cwd,
|
|
6256
|
-
resumedThreadId: resumeThreadId,
|
|
6257
|
-
degraded,
|
|
6258
|
-
// Which segments the thread carries once this turn lands. A segment sent is that one —
|
|
6259
|
-
// the later copy is what the model reads. One withheld is what the thread had. Read off
|
|
6260
|
-
// the spec, so the record can't disagree with what was sent.
|
|
6261
|
-
promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
|
|
6262
|
-
conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
|
|
6263
|
-
compactions,
|
|
6264
|
-
// What the thread held before this turn, for the write at `thread.started`.
|
|
6265
|
-
heldStanding: {
|
|
6266
|
-
promptFingerprint: threadStanding?.core ?? null,
|
|
6267
|
-
conversationFingerprint: threadStanding?.conversation ?? null,
|
|
6268
|
-
compactions: threadStanding ? compactions : null
|
|
6269
|
-
},
|
|
6270
|
-
storedState: req.session,
|
|
6271
|
-
usageBaseline: threadUsageBaseline,
|
|
6272
|
-
// Codex reports no model in-stream, so record what the thread was started with (null on the
|
|
6273
|
-
// default token). Same for the reasoning effort.
|
|
6274
|
-
resolvedModel: spec.model,
|
|
6275
|
-
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
6276
|
-
});
|
|
6277
6310
|
} finally {
|
|
6278
6311
|
if (images) {
|
|
6279
6312
|
try {
|
|
@@ -7506,13 +7539,11 @@ var BOXED_ENV_DROPPED = [
|
|
|
7506
7539
|
"DBUS_SESSION_BUS_ADDRESS",
|
|
7507
7540
|
"XDG_RUNTIME_DIR"
|
|
7508
7541
|
];
|
|
7509
|
-
var BOXED_ENV_DROPPED_PREFIX = "CABANE_GATE_";
|
|
7510
7542
|
function runnerExecEnv(base, boxed) {
|
|
7511
7543
|
const env = {};
|
|
7512
7544
|
for (const [k, v] of Object.entries(base)) {
|
|
7513
7545
|
if (v === void 0) continue;
|
|
7514
|
-
if (boxed &&
|
|
7515
|
-
continue;
|
|
7546
|
+
if (boxed && BOXED_ENV_DROPPED.includes(k)) continue;
|
|
7516
7547
|
env[k] = v;
|
|
7517
7548
|
}
|
|
7518
7549
|
return env;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.191",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect an agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
|
|
6
6
|
"license": "UNLICENSED",
|