@cabane/companion 0.6.179 → 0.6.181

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 CHANGED
@@ -9367,7 +9367,7 @@ function fingerprintSessionState(state) {
9367
9367
  }
9368
9368
  return createHash2("sha256").update(opaqueId).digest("hex").slice(0, 16);
9369
9369
  }
9370
- var PREPARING_TOOL_NAME, RUNTIME_UNAVAILABLE_PREFIX, NO_TURN_TOKEN_REASON, UNEXPECTED_ROLE_REASON, DEFAULT_PREPARING_ROW_DELAY_MS, DEFAULT_AGENT_IDLE_TIMEOUT_MS, DEFAULT_AGENT_TOTAL_TIMEOUT_MS, DEFAULT_LEASE_RENEWAL_MS, TurnConcluded, TurnExecution;
9370
+ var PREPARING_TOOL_NAME, HARNESS_UNAVAILABLE_NOTICE, NO_TURN_TOKEN_REASON, UNEXPECTED_ROLE_REASON, DEFAULT_PREPARING_ROW_DELAY_MS, DEFAULT_AGENT_IDLE_TIMEOUT_MS, DEFAULT_AGENT_TOTAL_TIMEOUT_MS, DEFAULT_LEASE_RENEWAL_MS, TurnConcluded, TurnExecution;
9371
9371
  var init_turn_execution = __esm({
9372
9372
  "src/turn-execution.ts"() {
9373
9373
  "use strict";
@@ -9396,7 +9396,7 @@ var init_turn_execution = __esm({
9396
9396
  init_dispatcher();
9397
9397
  init_sandbox_policy();
9398
9398
  PREPARING_TOOL_NAME = "preparing";
9399
- RUNTIME_UNAVAILABLE_PREFIX = "**This agent's runtime isn't available on this companion.** The model this agent uses needs a runtime this device isn't running, so I can't run this turn here. Details:";
9399
+ HARNESS_UNAVAILABLE_NOTICE = "**This harness isn't available on the companion.** Check the harness on the device, or choose another harness in the agent's settings. Then reply to try again.";
9400
9400
  NO_TURN_TOKEN_REASON = "no_turn_token: the server issued no turn credential for this turn";
9401
9401
  UNEXPECTED_ROLE_REASON = `refused the wake trigger (role "system" isn't dispatchable on this companion) \u2014 the companion is likely running outdated code; refresh it, then re-address the agent`;
9402
9402
  DEFAULT_PREPARING_ROW_DELAY_MS = 1500;
@@ -10082,7 +10082,7 @@ var init_turn_execution = __esm({
10082
10082
  );
10083
10083
  try {
10084
10084
  await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
10085
- body: `${RUNTIME_UNAVAILABLE_PREFIX} ${err.message}`,
10085
+ body: HARNESS_UNAVAILABLE_NOTICE,
10086
10086
  kind: "runtime_notice",
10087
10087
  turnId,
10088
10088
  parentMessageId: payload.messageId,
@@ -12148,7 +12148,7 @@ var HARNESS_STATE_LABEL = {
12148
12148
  needs_sign_in: "needs sign-in",
12149
12149
  limited: "limited",
12150
12150
  problem: "problem",
12151
- off: "off",
12151
+ off: "disabled",
12152
12152
  gone: "gone"
12153
12153
  };
12154
12154
 
@@ -12260,7 +12260,7 @@ function agentRowText(runtime, state, opts = {}) {
12260
12260
  case "not_running":
12261
12261
  return `${word} \u2014 not running`;
12262
12262
  case "off":
12263
- return `${word} \u2014 turned off in config`;
12263
+ return `${word} \u2014 disabled in config`;
12264
12264
  case "problem":
12265
12265
  return runtime === "opencode" ? `${word} \u2014 not running` : `${word} \u2014 ${unusableReason(runtime, state.check) ?? "could not be checked"}`;
12266
12266
  }
@@ -13533,7 +13533,7 @@ function turnFailureCopy(reason, runtime) {
13533
13533
  if (reason?.startsWith("runtime_incomplete:"))
13534
13534
  return `The agent installation is incomplete. ${repairAction()} Then restart the companion.`;
13535
13535
  if (/^(runtime|codex|opencode)_unavailable:/.test(reason ?? ""))
13536
- return "The harness is off on this device. Enable it here, or choose another harness or device in Cabane.";
13536
+ return "This harness isn't available on the companion. Check the harness on this device, or choose another harness in the agent's settings in Cabane. Then reply in Cabane to try again.";
13537
13537
  if (reason?.startsWith("model_unavailable:"))
13538
13538
  return "The selected model is unavailable. Choose a different model in the agent settings in Cabane.";
13539
13539
  if (reason?.startsWith("prepare_failed:"))
@@ -16164,6 +16164,9 @@ var CompanionSupervisor = class {
16164
16164
  // the first detection; a heartbeat spawns nothing and reads this.
16165
16165
  detection = null;
16166
16166
  detecting = null;
16167
+ // Liveness may be acknowledged before discovery. Execution waits on this
16168
+ // first look only, never on a periodic refresh of an established inventory.
16169
+ initialHarnessReady = null;
16167
16170
  detectTimer = null;
16168
16171
  runHarnessCheck;
16169
16172
  probeOpencode;
@@ -16315,8 +16318,17 @@ var CompanionSupervisor = class {
16315
16318
  this.trackHeartbeat(this.sendHeartbeat(true));
16316
16319
  }
16317
16320
  const firstHeartbeat = this.inFlightHeartbeat;
16318
- const startupHarnessRefresh = this.detect();
16319
- void startupHarnessRefresh.then(async () => {
16321
+ this.initialHarnessReady = this.detect().then(
16322
+ () => {
16323
+ },
16324
+ (err) => {
16325
+ this.log.warn(
16326
+ { err: err instanceof Error ? err.message : String(err) },
16327
+ "Couldn't check harnesses at startup. Turns may fail until the next check."
16328
+ );
16329
+ }
16330
+ );
16331
+ void this.initialHarnessReady.then(async () => {
16320
16332
  await firstHeartbeat;
16321
16333
  if (this.stopped) return;
16322
16334
  await this.heartbeatNow();
@@ -17173,6 +17185,8 @@ var CompanionSupervisor = class {
17173
17185
  return;
17174
17186
  this.recovering = true;
17175
17187
  try {
17188
+ await this.initialHarnessReady;
17189
+ if (this.stopped) return;
17176
17190
  const page = await this.deviceApi.getRuns(this.recoveryCursor);
17177
17191
  if (this.stopped) return;
17178
17192
  let assignmentsMissing = false;
@@ -17224,6 +17238,8 @@ var CompanionSupervisor = class {
17224
17238
  }
17225
17239
  // One turn: the skip-vs-resume dedupe, the dispatch, then the console summary.
17226
17240
  async runDispatch(wr, dispatchId, payload, agent, recoveredTurnId) {
17241
+ await this.initialHarnessReady;
17242
+ if (this.stopped) return;
17227
17243
  const workspaceId = wr.workspaceId;
17228
17244
  if (dispatchId && hasDispatched(workspaceId, dispatchId)) {
17229
17245
  if (hasCompleted(workspaceId, dispatchId)) {
@@ -17383,7 +17399,7 @@ var CompanionSupervisor = class {
17383
17399
  async detect() {
17384
17400
  if (this.detecting) return this.detecting;
17385
17401
  this.detecting = (async () => {
17386
- const previous = this.detection;
17402
+ const previousOpencodeUrl = this.opencodeUrl();
17387
17403
  const detection = await detectHarnesses(this.config, {
17388
17404
  check: this.runHarnessCheck,
17389
17405
  ...this.probeOpencode ? { probeOpencode: this.probeOpencode } : {}
@@ -17395,7 +17411,7 @@ var CompanionSupervisor = class {
17395
17411
  opencode: detectedVersion(detection.opencode),
17396
17412
  codex: detectedVersion(detection.codex)
17397
17413
  };
17398
- if (previous && opencodeAddress(previous) !== opencodeAddress(detection)) {
17414
+ if (previousOpencodeUrl !== this.opencodeUrl()) {
17399
17415
  this.rebuildDispatchers();
17400
17416
  }
17401
17417
  this.hub.setHarnesses(deriveHarnessSnapshot(detection).harnesses);
@@ -17587,10 +17603,6 @@ function defaultReexec() {
17587
17603
  }
17588
17604
  });
17589
17605
  }
17590
- function opencodeAddress(detection) {
17591
- const found = detection.opencode;
17592
- return found.state === "usable" ? found.url ?? DEFAULT_OPENCODE_SERVER_URL : null;
17593
- }
17594
17606
 
17595
17607
  // src/runtime.ts
17596
17608
  init_version2();
package/dist/runtime.js CHANGED
@@ -1893,7 +1893,7 @@ function turnFailureCopy(reason, runtime) {
1893
1893
  if (reason?.startsWith("runtime_incomplete:"))
1894
1894
  return `The agent installation is incomplete. ${repairAction()} Then restart the companion.`;
1895
1895
  if (/^(runtime|codex|opencode)_unavailable:/.test(reason ?? ""))
1896
- return "The harness is off on this device. Enable it here, or choose another harness or device in Cabane.";
1896
+ return "This harness isn't available on the companion. Check the harness on this device, or choose another harness in the agent's settings in Cabane. Then reply in Cabane to try again.";
1897
1897
  if (reason?.startsWith("model_unavailable:"))
1898
1898
  return "The selected model is unavailable. Choose a different model in the agent settings in Cabane.";
1899
1899
  if (reason?.startsWith("prepare_failed:"))
@@ -9850,7 +9850,7 @@ async function absorbBundledLoss(ctx, aborted, outcome) {
9850
9850
 
9851
9851
  // src/turn-execution.ts
9852
9852
  var PREPARING_TOOL_NAME = "preparing";
9853
- var RUNTIME_UNAVAILABLE_PREFIX = "**This agent's runtime isn't available on this companion.** The model this agent uses needs a runtime this device isn't running, so I can't run this turn here. Details:";
9853
+ var HARNESS_UNAVAILABLE_NOTICE = "**This harness isn't available on the companion.** Check the harness on the device, or choose another harness in the agent's settings. Then reply to try again.";
9854
9854
  var NO_TURN_TOKEN_REASON = "no_turn_token: the server issued no turn credential for this turn";
9855
9855
  var UNEXPECTED_ROLE_REASON = `refused the wake trigger (role "system" isn't dispatchable on this companion) \u2014 the companion is likely running outdated code; refresh it, then re-address the agent`;
9856
9856
  var DEFAULT_PREPARING_ROW_DELAY_MS = 1500;
@@ -10561,7 +10561,7 @@ var TurnExecution = class {
10561
10561
  );
10562
10562
  try {
10563
10563
  await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
10564
- body: `${RUNTIME_UNAVAILABLE_PREFIX} ${err.message}`,
10564
+ body: HARNESS_UNAVAILABLE_NOTICE,
10565
10565
  kind: "runtime_notice",
10566
10566
  turnId,
10567
10567
  parentMessageId: payload.messageId,
@@ -14096,6 +14096,9 @@ var CompanionSupervisor = class {
14096
14096
  // the first detection; a heartbeat spawns nothing and reads this.
14097
14097
  detection = null;
14098
14098
  detecting = null;
14099
+ // Liveness may be acknowledged before discovery. Execution waits on this
14100
+ // first look only, never on a periodic refresh of an established inventory.
14101
+ initialHarnessReady = null;
14099
14102
  detectTimer = null;
14100
14103
  runHarnessCheck;
14101
14104
  probeOpencode;
@@ -14247,8 +14250,17 @@ var CompanionSupervisor = class {
14247
14250
  this.trackHeartbeat(this.sendHeartbeat(true));
14248
14251
  }
14249
14252
  const firstHeartbeat = this.inFlightHeartbeat;
14250
- const startupHarnessRefresh = this.detect();
14251
- void startupHarnessRefresh.then(async () => {
14253
+ this.initialHarnessReady = this.detect().then(
14254
+ () => {
14255
+ },
14256
+ (err) => {
14257
+ this.log.warn(
14258
+ { err: err instanceof Error ? err.message : String(err) },
14259
+ "Couldn't check harnesses at startup. Turns may fail until the next check."
14260
+ );
14261
+ }
14262
+ );
14263
+ void this.initialHarnessReady.then(async () => {
14252
14264
  await firstHeartbeat;
14253
14265
  if (this.stopped) return;
14254
14266
  await this.heartbeatNow();
@@ -15105,6 +15117,8 @@ var CompanionSupervisor = class {
15105
15117
  return;
15106
15118
  this.recovering = true;
15107
15119
  try {
15120
+ await this.initialHarnessReady;
15121
+ if (this.stopped) return;
15108
15122
  const page = await this.deviceApi.getRuns(this.recoveryCursor);
15109
15123
  if (this.stopped) return;
15110
15124
  let assignmentsMissing = false;
@@ -15156,6 +15170,8 @@ var CompanionSupervisor = class {
15156
15170
  }
15157
15171
  // One turn: the skip-vs-resume dedupe, the dispatch, then the console summary.
15158
15172
  async runDispatch(wr, dispatchId, payload, agent, recoveredTurnId) {
15173
+ await this.initialHarnessReady;
15174
+ if (this.stopped) return;
15159
15175
  const workspaceId = wr.workspaceId;
15160
15176
  if (dispatchId && hasDispatched(workspaceId, dispatchId)) {
15161
15177
  if (hasCompleted(workspaceId, dispatchId)) {
@@ -15315,7 +15331,7 @@ var CompanionSupervisor = class {
15315
15331
  async detect() {
15316
15332
  if (this.detecting) return this.detecting;
15317
15333
  this.detecting = (async () => {
15318
- const previous = this.detection;
15334
+ const previousOpencodeUrl = this.opencodeUrl();
15319
15335
  const detection = await detectHarnesses(this.config, {
15320
15336
  check: this.runHarnessCheck,
15321
15337
  ...this.probeOpencode ? { probeOpencode: this.probeOpencode } : {}
@@ -15327,7 +15343,7 @@ var CompanionSupervisor = class {
15327
15343
  opencode: detectedVersion(detection.opencode),
15328
15344
  codex: detectedVersion(detection.codex)
15329
15345
  };
15330
- if (previous && opencodeAddress(previous) !== opencodeAddress(detection)) {
15346
+ if (previousOpencodeUrl !== this.opencodeUrl()) {
15331
15347
  this.rebuildDispatchers();
15332
15348
  }
15333
15349
  this.hub.setHarnesses(deriveHarnessSnapshot(detection).harnesses);
@@ -15519,10 +15535,6 @@ function defaultReexec() {
15519
15535
  }
15520
15536
  });
15521
15537
  }
15522
- function opencodeAddress(detection) {
15523
- const found = detection.opencode;
15524
- return found.state === "usable" ? found.url ?? DEFAULT_OPENCODE_SERVER_URL : null;
15525
- }
15526
15538
 
15527
15539
  // src/safety.ts
15528
15540
  var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.179",
3
+ "version": "0.6.181",
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",