@cabane/companion 0.6.104 → 0.6.106
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 +89 -7
- package/dist/runtime.js +89 -7
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -2858,7 +2858,11 @@ function turnFailureCopy(reason, runtime) {
|
|
|
2858
2858
|
var LEASE_REFUSALS = /* @__PURE__ */ new Set([
|
|
2859
2859
|
"dispatch_not_admitted",
|
|
2860
2860
|
"turn_already_ended",
|
|
2861
|
-
"turn_belongs_elsewhere"
|
|
2861
|
+
"turn_belongs_elsewhere",
|
|
2862
|
+
// Rooms: the context this start was composed from named an attempt the
|
|
2863
|
+
// lease no longer picks (midnight rolled it, a degrade retired it). Nothing
|
|
2864
|
+
// was granted; the turn recomposes its context and starts again, once.
|
|
2865
|
+
"dispatch_context_stale"
|
|
2862
2866
|
]);
|
|
2863
2867
|
function apiErrorCode(err) {
|
|
2864
2868
|
if (!(err instanceof ApiError)) return null;
|
|
@@ -7135,6 +7139,67 @@ function isStringRecord2(v) {
|
|
|
7135
7139
|
import {
|
|
7136
7140
|
Codex
|
|
7137
7141
|
} from "@openai/codex-sdk";
|
|
7142
|
+
|
|
7143
|
+
// packages/agent-runtime/src/codex/config-overrides.ts
|
|
7144
|
+
var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
|
|
7145
|
+
function formatKey(key) {
|
|
7146
|
+
return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
|
|
7147
|
+
}
|
|
7148
|
+
function isTable(value) {
|
|
7149
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7150
|
+
}
|
|
7151
|
+
function tomlValue(value, path) {
|
|
7152
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
7153
|
+
if (typeof value === "number") {
|
|
7154
|
+
if (!Number.isFinite(value)) {
|
|
7155
|
+
throw new Error(`Codex config override at ${path} must be a finite number`);
|
|
7156
|
+
}
|
|
7157
|
+
return `${value}`;
|
|
7158
|
+
}
|
|
7159
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
7160
|
+
if (Array.isArray(value)) {
|
|
7161
|
+
return `[${value.map((item, i) => tomlValue(item, `${path}[${i}]`)).join(", ")}]`;
|
|
7162
|
+
}
|
|
7163
|
+
if (isTable(value)) {
|
|
7164
|
+
const parts = [];
|
|
7165
|
+
for (const [key, child] of Object.entries(value)) {
|
|
7166
|
+
if (!key) throw new Error("Codex config override keys must be non-empty strings");
|
|
7167
|
+
if (child === void 0) continue;
|
|
7168
|
+
parts.push(`${formatKey(key)} = ${tomlValue(child, `${path}.${key}`)}`);
|
|
7169
|
+
}
|
|
7170
|
+
return `{${parts.join(", ")}}`;
|
|
7171
|
+
}
|
|
7172
|
+
throw new Error(`Codex config override at ${path} has an unsupported type`);
|
|
7173
|
+
}
|
|
7174
|
+
function walk(table, prefix, out) {
|
|
7175
|
+
const entries = Object.entries(table).filter(([, child]) => child !== void 0);
|
|
7176
|
+
if (entries.length === 0) {
|
|
7177
|
+
if (prefix) out.push(`${prefix}={}`);
|
|
7178
|
+
return;
|
|
7179
|
+
}
|
|
7180
|
+
for (const [key] of entries) {
|
|
7181
|
+
if (!key) throw new Error("Codex config override keys must be non-empty strings");
|
|
7182
|
+
}
|
|
7183
|
+
if (entries.some(([key]) => !TOML_BARE_KEY.test(key))) {
|
|
7184
|
+
if (!prefix) {
|
|
7185
|
+
throw new Error("Codex config override has a non-bare key at the root");
|
|
7186
|
+
}
|
|
7187
|
+
out.push(`${prefix}=${tomlValue(Object.fromEntries(entries), prefix)}`);
|
|
7188
|
+
return;
|
|
7189
|
+
}
|
|
7190
|
+
for (const [key, child] of entries) {
|
|
7191
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
7192
|
+
if (isTable(child)) walk(child, path, out);
|
|
7193
|
+
else out.push(`${path}=${tomlValue(child, path)}`);
|
|
7194
|
+
}
|
|
7195
|
+
}
|
|
7196
|
+
function buildConfigOverrides(config) {
|
|
7197
|
+
const out = [];
|
|
7198
|
+
walk(config, "", out);
|
|
7199
|
+
return out;
|
|
7200
|
+
}
|
|
7201
|
+
|
|
7202
|
+
// packages/agent-runtime/src/codex/transport.ts
|
|
7138
7203
|
function buildSdkThreadOptions(spec) {
|
|
7139
7204
|
return {
|
|
7140
7205
|
...spec.model ? { model: spec.model } : {},
|
|
@@ -7151,11 +7216,17 @@ function createSdkCodexTransport(opts = {}) {
|
|
|
7151
7216
|
const codexOptions = {
|
|
7152
7217
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
7153
7218
|
...opts.codexPathOverride ? { codexPathOverride: opts.codexPathOverride } : {},
|
|
7154
|
-
// The `--config` overrides (MCP servers + rmcp flag).
|
|
7155
|
-
//
|
|
7156
|
-
//
|
|
7157
|
-
// `
|
|
7158
|
-
|
|
7219
|
+
// The `--config` overrides (MCP servers + rmcp flag). CT1501: rendered to
|
|
7220
|
+
// finished `--config` strings by US and passed through `configOverrides`,
|
|
7221
|
+
// the SDK's raw passthrough — NOT through `config`, which would flatten
|
|
7222
|
+
// them itself. A user MCP server named `my.server` cannot go in an
|
|
7223
|
+
// override's dotted path at all (the CLI splits the path on `.` and
|
|
7224
|
+
// ignores TOML quoting), so it has to ride inside an inline-table value,
|
|
7225
|
+
// and the SDK's flattener cannot be steered into producing one. This is
|
|
7226
|
+
// also what makes the PUBLISHED companion correct, where the vendored
|
|
7227
|
+
// patch never reached — and why that patch is gone. See
|
|
7228
|
+
// `config-overrides.ts`.
|
|
7229
|
+
configOverrides: buildConfigOverrides(spec.config)
|
|
7159
7230
|
};
|
|
7160
7231
|
const codex = new Codex(codexOptions);
|
|
7161
7232
|
const threadOptions = buildSdkThreadOptions(spec);
|
|
@@ -9371,6 +9442,8 @@ var TurnExecution = class {
|
|
|
9371
9442
|
// so every exit after this point must settle THE SPAN, not just clear the
|
|
9372
9443
|
// participant flag.
|
|
9373
9444
|
admitted = false;
|
|
9445
|
+
// Rooms: one recompose per turn when the lease refuses a stale context.
|
|
9446
|
+
recomposedForLease = false;
|
|
9374
9447
|
concluded(reason, errorReason) {
|
|
9375
9448
|
return new TurnConcluded(reason, errorReason);
|
|
9376
9449
|
}
|
|
@@ -9689,9 +9762,18 @@ var TurnExecution = class {
|
|
|
9689
9762
|
// and settle moves the cursor to it. Omitted when the server sent
|
|
9690
9763
|
// none (an older API), so the cursor stays where it was.
|
|
9691
9764
|
...this.turnContext.readThrough !== void 0 ? { readThrough: this.turnContext.readThrough } : {},
|
|
9692
|
-
...this.turnContext.readRevision !== void 0 ? { readRevision: this.turnContext.readRevision } : {}
|
|
9765
|
+
...this.turnContext.readRevision !== void 0 ? { readRevision: this.turnContext.readRevision } : {},
|
|
9766
|
+
// Rooms: the attempt this request's session belongs to (null = fresh).
|
|
9767
|
+
...this.turnContext.attemptId !== void 0 ? { attemptId: this.turnContext.attemptId } : {}
|
|
9693
9768
|
});
|
|
9694
9769
|
} catch (err) {
|
|
9770
|
+
if (leaseRefusal(err) === "dispatch_context_stale" && !this.recomposedForLease) {
|
|
9771
|
+
this.recomposedForLease = true;
|
|
9772
|
+
turnLog.info({ turnId }, "dispatcher: lease found the context stale; recomposing");
|
|
9773
|
+
await this.fetchContext();
|
|
9774
|
+
this.buildRequest();
|
|
9775
|
+
return this.acquireLease();
|
|
9776
|
+
}
|
|
9695
9777
|
const refusal = leaseRefusal(err);
|
|
9696
9778
|
if (refusal === "dispatch_not_admitted" || refusal === "turn_already_ended") {
|
|
9697
9779
|
turnLog.debug(
|
package/dist/runtime.js
CHANGED
|
@@ -2192,7 +2192,11 @@ function turnFailureCopy(reason, runtime) {
|
|
|
2192
2192
|
var LEASE_REFUSALS = /* @__PURE__ */ new Set([
|
|
2193
2193
|
"dispatch_not_admitted",
|
|
2194
2194
|
"turn_already_ended",
|
|
2195
|
-
"turn_belongs_elsewhere"
|
|
2195
|
+
"turn_belongs_elsewhere",
|
|
2196
|
+
// Rooms: the context this start was composed from named an attempt the
|
|
2197
|
+
// lease no longer picks (midnight rolled it, a degrade retired it). Nothing
|
|
2198
|
+
// was granted; the turn recomposes its context and starts again, once.
|
|
2199
|
+
"dispatch_context_stale"
|
|
2196
2200
|
]);
|
|
2197
2201
|
function apiErrorCode(err) {
|
|
2198
2202
|
if (!(err instanceof ApiError)) return null;
|
|
@@ -6548,6 +6552,67 @@ function isStringRecord2(v) {
|
|
|
6548
6552
|
import {
|
|
6549
6553
|
Codex
|
|
6550
6554
|
} from "@openai/codex-sdk";
|
|
6555
|
+
|
|
6556
|
+
// packages/agent-runtime/src/codex/config-overrides.ts
|
|
6557
|
+
var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
|
|
6558
|
+
function formatKey(key) {
|
|
6559
|
+
return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
|
|
6560
|
+
}
|
|
6561
|
+
function isTable(value) {
|
|
6562
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6563
|
+
}
|
|
6564
|
+
function tomlValue(value, path) {
|
|
6565
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
6566
|
+
if (typeof value === "number") {
|
|
6567
|
+
if (!Number.isFinite(value)) {
|
|
6568
|
+
throw new Error(`Codex config override at ${path} must be a finite number`);
|
|
6569
|
+
}
|
|
6570
|
+
return `${value}`;
|
|
6571
|
+
}
|
|
6572
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
6573
|
+
if (Array.isArray(value)) {
|
|
6574
|
+
return `[${value.map((item, i) => tomlValue(item, `${path}[${i}]`)).join(", ")}]`;
|
|
6575
|
+
}
|
|
6576
|
+
if (isTable(value)) {
|
|
6577
|
+
const parts = [];
|
|
6578
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6579
|
+
if (!key) throw new Error("Codex config override keys must be non-empty strings");
|
|
6580
|
+
if (child === void 0) continue;
|
|
6581
|
+
parts.push(`${formatKey(key)} = ${tomlValue(child, `${path}.${key}`)}`);
|
|
6582
|
+
}
|
|
6583
|
+
return `{${parts.join(", ")}}`;
|
|
6584
|
+
}
|
|
6585
|
+
throw new Error(`Codex config override at ${path} has an unsupported type`);
|
|
6586
|
+
}
|
|
6587
|
+
function walk(table, prefix, out) {
|
|
6588
|
+
const entries = Object.entries(table).filter(([, child]) => child !== void 0);
|
|
6589
|
+
if (entries.length === 0) {
|
|
6590
|
+
if (prefix) out.push(`${prefix}={}`);
|
|
6591
|
+
return;
|
|
6592
|
+
}
|
|
6593
|
+
for (const [key] of entries) {
|
|
6594
|
+
if (!key) throw new Error("Codex config override keys must be non-empty strings");
|
|
6595
|
+
}
|
|
6596
|
+
if (entries.some(([key]) => !TOML_BARE_KEY.test(key))) {
|
|
6597
|
+
if (!prefix) {
|
|
6598
|
+
throw new Error("Codex config override has a non-bare key at the root");
|
|
6599
|
+
}
|
|
6600
|
+
out.push(`${prefix}=${tomlValue(Object.fromEntries(entries), prefix)}`);
|
|
6601
|
+
return;
|
|
6602
|
+
}
|
|
6603
|
+
for (const [key, child] of entries) {
|
|
6604
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
6605
|
+
if (isTable(child)) walk(child, path, out);
|
|
6606
|
+
else out.push(`${path}=${tomlValue(child, path)}`);
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6609
|
+
function buildConfigOverrides(config) {
|
|
6610
|
+
const out = [];
|
|
6611
|
+
walk(config, "", out);
|
|
6612
|
+
return out;
|
|
6613
|
+
}
|
|
6614
|
+
|
|
6615
|
+
// packages/agent-runtime/src/codex/transport.ts
|
|
6551
6616
|
function buildSdkThreadOptions(spec) {
|
|
6552
6617
|
return {
|
|
6553
6618
|
...spec.model ? { model: spec.model } : {},
|
|
@@ -6564,11 +6629,17 @@ function createSdkCodexTransport(opts = {}) {
|
|
|
6564
6629
|
const codexOptions = {
|
|
6565
6630
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
6566
6631
|
...opts.codexPathOverride ? { codexPathOverride: opts.codexPathOverride } : {},
|
|
6567
|
-
// The `--config` overrides (MCP servers + rmcp flag).
|
|
6568
|
-
//
|
|
6569
|
-
//
|
|
6570
|
-
// `
|
|
6571
|
-
|
|
6632
|
+
// The `--config` overrides (MCP servers + rmcp flag). CT1501: rendered to
|
|
6633
|
+
// finished `--config` strings by US and passed through `configOverrides`,
|
|
6634
|
+
// the SDK's raw passthrough — NOT through `config`, which would flatten
|
|
6635
|
+
// them itself. A user MCP server named `my.server` cannot go in an
|
|
6636
|
+
// override's dotted path at all (the CLI splits the path on `.` and
|
|
6637
|
+
// ignores TOML quoting), so it has to ride inside an inline-table value,
|
|
6638
|
+
// and the SDK's flattener cannot be steered into producing one. This is
|
|
6639
|
+
// also what makes the PUBLISHED companion correct, where the vendored
|
|
6640
|
+
// patch never reached — and why that patch is gone. See
|
|
6641
|
+
// `config-overrides.ts`.
|
|
6642
|
+
configOverrides: buildConfigOverrides(spec.config)
|
|
6572
6643
|
};
|
|
6573
6644
|
const codex = new Codex(codexOptions);
|
|
6574
6645
|
const threadOptions = buildSdkThreadOptions(spec);
|
|
@@ -8794,6 +8865,8 @@ var TurnExecution = class {
|
|
|
8794
8865
|
// so every exit after this point must settle THE SPAN, not just clear the
|
|
8795
8866
|
// participant flag.
|
|
8796
8867
|
admitted = false;
|
|
8868
|
+
// Rooms: one recompose per turn when the lease refuses a stale context.
|
|
8869
|
+
recomposedForLease = false;
|
|
8797
8870
|
concluded(reason, errorReason) {
|
|
8798
8871
|
return new TurnConcluded(reason, errorReason);
|
|
8799
8872
|
}
|
|
@@ -9112,9 +9185,18 @@ var TurnExecution = class {
|
|
|
9112
9185
|
// and settle moves the cursor to it. Omitted when the server sent
|
|
9113
9186
|
// none (an older API), so the cursor stays where it was.
|
|
9114
9187
|
...this.turnContext.readThrough !== void 0 ? { readThrough: this.turnContext.readThrough } : {},
|
|
9115
|
-
...this.turnContext.readRevision !== void 0 ? { readRevision: this.turnContext.readRevision } : {}
|
|
9188
|
+
...this.turnContext.readRevision !== void 0 ? { readRevision: this.turnContext.readRevision } : {},
|
|
9189
|
+
// Rooms: the attempt this request's session belongs to (null = fresh).
|
|
9190
|
+
...this.turnContext.attemptId !== void 0 ? { attemptId: this.turnContext.attemptId } : {}
|
|
9116
9191
|
});
|
|
9117
9192
|
} catch (err) {
|
|
9193
|
+
if (leaseRefusal(err) === "dispatch_context_stale" && !this.recomposedForLease) {
|
|
9194
|
+
this.recomposedForLease = true;
|
|
9195
|
+
turnLog.info({ turnId }, "dispatcher: lease found the context stale; recomposing");
|
|
9196
|
+
await this.fetchContext();
|
|
9197
|
+
this.buildRequest();
|
|
9198
|
+
return this.acquireLease();
|
|
9199
|
+
}
|
|
9118
9200
|
const refusal = leaseRefusal(err);
|
|
9119
9201
|
if (refusal === "dispatch_not_admitted" || refusal === "turn_already_ended") {
|
|
9120
9202
|
turnLog.debug(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.106",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect a coding 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",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"prepublishOnly": "pnpm build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
33
|
-
"@openai/codex-sdk": "0.
|
|
32
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.270",
|
|
33
|
+
"@openai/codex-sdk": "0.154.0",
|
|
34
34
|
"@hono/node-server": "1.19.14",
|
|
35
35
|
"@inquirer/prompts": "7.10.1",
|
|
36
36
|
"commander": "12.1.0",
|