@cabane/companion 0.6.41 → 0.6.43

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
@@ -7444,16 +7444,22 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
7444
7444
  "x-cabane-active-conversation": req.cabane.activeConversationId
7445
7445
  };
7446
7446
  try {
7447
- const initialized = await initializeWithRetry(fetchImpl, req.cabane.mcpUrl, headers, {
7448
- jsonrpc: "2.0",
7449
- id: 1,
7450
- method: "initialize",
7451
- params: {
7452
- protocolVersion: "2025-03-26",
7453
- capabilities: {},
7454
- clientInfo: { name: "cabane-companion-readiness", version: "1" }
7455
- }
7456
- });
7447
+ const initialized = await initializeWithRetry(
7448
+ fetchImpl,
7449
+ req.cabane.mcpUrl,
7450
+ headers,
7451
+ {
7452
+ jsonrpc: "2.0",
7453
+ id: 1,
7454
+ method: "initialize",
7455
+ params: {
7456
+ protocolVersion: "2025-03-26",
7457
+ capabilities: {},
7458
+ clientInfo: { name: "cabane-companion-readiness", version: "1" }
7459
+ }
7460
+ },
7461
+ opts.retryDelaysMs ?? INITIALIZE_RETRY_DELAYS_MS
7462
+ );
7457
7463
  if (initialized.status === 401 || initialized.status === 403)
7458
7464
  return fail(base, "authentication_failed", `initialize returned HTTP ${initialized.status}`);
7459
7465
  if (initialized.transient)
@@ -7497,10 +7503,10 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
7497
7503
  );
7498
7504
  }
7499
7505
  }
7500
- async function initializeWithRetry(fetchImpl, url, headers, body) {
7506
+ async function initializeWithRetry(fetchImpl, url, headers, body, retryDelaysMs) {
7501
7507
  let lastFailure = null;
7502
7508
  const deadline = Date.now() + INITIALIZE_RETRY_BUDGET_MS;
7503
- for (let attempt = 0; attempt <= INITIALIZE_RETRY_DELAYS_MS.length; attempt += 1) {
7509
+ for (let attempt = 0; attempt <= retryDelaysMs.length; attempt += 1) {
7504
7510
  try {
7505
7511
  const remainingMs = deadline - Date.now();
7506
7512
  if (remainingMs <= 0) break;
@@ -7524,7 +7530,7 @@ async function initializeWithRetry(fetchImpl, url, headers, body) {
7524
7530
  transient: true
7525
7531
  };
7526
7532
  }
7527
- const retryDelayMs = INITIALIZE_RETRY_DELAYS_MS[attempt];
7533
+ const retryDelayMs = retryDelaysMs[attempt];
7528
7534
  if (retryDelayMs === void 0 || Date.now() + retryDelayMs >= deadline) break;
7529
7535
  await delay(retryDelayMs);
7530
7536
  }
@@ -8160,6 +8166,7 @@ ${reason}`,
8160
8166
  }
8161
8167
  const proof = await proveWorkspaceTools(request, adapter.name, {
8162
8168
  ...this.opts.workspaceProofFetch ? { fetchImpl: this.opts.workspaceProofFetch } : {},
8169
+ ...this.opts.workspaceProofRetryDelaysMs ? { retryDelaysMs: this.opts.workspaceProofRetryDelaysMs } : {},
8163
8170
  harnessFingerprint: turnContext.runtime
8164
8171
  });
8165
8172
  turnLog[proof.ok ? "info" : "error"](
@@ -8977,6 +8984,7 @@ var ASSIGNMENTS_POLL_MS = 6e4;
8977
8984
  var DRAIN_BASE_MS = 1e3;
8978
8985
  var DRAIN_MAX_MS = 3e4;
8979
8986
  var DRAIN_IDLE_MS = 15e3;
8987
+ var DEFAULT_UNRUNNABLE_RETRY_DELAYS_MS = [0, 1e3, 3e3];
8980
8988
  var CompanionSupervisor = class {
8981
8989
  workspaces = /* @__PURE__ */ new Map();
8982
8990
  config;
@@ -9003,6 +9011,7 @@ var CompanionSupervisor = class {
9003
9011
  exitFn;
9004
9012
  reexecFn;
9005
9013
  dispatcherFactory;
9014
+ unrunnableRetryDelaysMs;
9006
9015
  deviceApi = null;
9007
9016
  deviceSub = null;
9008
9017
  heartbeatTimer = null;
@@ -9035,6 +9044,7 @@ var CompanionSupervisor = class {
9035
9044
  this.exitFn = opts.exit ?? ((code) => process.exit(code));
9036
9045
  this.reexecFn = opts.reexec ?? defaultReexec;
9037
9046
  this.dispatcherFactory = opts.dispatcherFactory;
9047
+ this.unrunnableRetryDelaysMs = opts.unrunnableRetryDelaysMs ?? DEFAULT_UNRUNNABLE_RETRY_DELAYS_MS;
9038
9048
  }
9039
9049
  // Stand up the data plane: pair check, initial assignments pull, then the
9040
9050
  // heartbeat + poll loops. A companion with no device token (logged out) does
@@ -9539,9 +9549,8 @@ var CompanionSupervisor = class {
9539
9549
  // wedge the workspace's whole stream.
9540
9550
  async reportUnrunnableDispatch(wr, payload) {
9541
9551
  if (!this.deviceApi) return false;
9542
- const backoffMs = [0, 1e3, 3e3];
9543
9552
  let lastErr;
9544
- for (const wait of backoffMs) {
9553
+ for (const wait of this.unrunnableRetryDelaysMs) {
9545
9554
  if (this.stopped) return false;
9546
9555
  if (wait > 0) await new Promise((r) => setTimeout(r, wait));
9547
9556
  try {
@@ -9842,12 +9851,14 @@ var CompanionSupervisor = class {
9842
9851
  this.stopped = true;
9843
9852
  if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
9844
9853
  if (this.pollTimer) clearInterval(this.pollTimer);
9854
+ const heartbeat = this.inFlightHeartbeat;
9845
9855
  this.deviceSub?.stop();
9846
9856
  for (const wr of this.workspaces.values()) {
9847
9857
  for (const a of wr.agents.values()) a.cancelDrain();
9848
9858
  if (wr.sub) wr.sub.stop();
9849
9859
  }
9850
9860
  await Promise.all([
9861
+ ...heartbeat ? [heartbeat] : [],
9851
9862
  ...this.deviceSub ? [this.deviceSub.finished] : [],
9852
9863
  ...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
9853
9864
  ]);
package/dist/runtime.js CHANGED
@@ -6953,16 +6953,22 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
6953
6953
  "x-cabane-active-conversation": req.cabane.activeConversationId
6954
6954
  };
6955
6955
  try {
6956
- const initialized = await initializeWithRetry(fetchImpl, req.cabane.mcpUrl, headers, {
6957
- jsonrpc: "2.0",
6958
- id: 1,
6959
- method: "initialize",
6960
- params: {
6961
- protocolVersion: "2025-03-26",
6962
- capabilities: {},
6963
- clientInfo: { name: "cabane-companion-readiness", version: "1" }
6964
- }
6965
- });
6956
+ const initialized = await initializeWithRetry(
6957
+ fetchImpl,
6958
+ req.cabane.mcpUrl,
6959
+ headers,
6960
+ {
6961
+ jsonrpc: "2.0",
6962
+ id: 1,
6963
+ method: "initialize",
6964
+ params: {
6965
+ protocolVersion: "2025-03-26",
6966
+ capabilities: {},
6967
+ clientInfo: { name: "cabane-companion-readiness", version: "1" }
6968
+ }
6969
+ },
6970
+ opts.retryDelaysMs ?? INITIALIZE_RETRY_DELAYS_MS
6971
+ );
6966
6972
  if (initialized.status === 401 || initialized.status === 403)
6967
6973
  return fail(base, "authentication_failed", `initialize returned HTTP ${initialized.status}`);
6968
6974
  if (initialized.transient)
@@ -7006,10 +7012,10 @@ async function proveWorkspaceTools(req, runtime, opts = {}) {
7006
7012
  );
7007
7013
  }
7008
7014
  }
7009
- async function initializeWithRetry(fetchImpl, url, headers, body) {
7015
+ async function initializeWithRetry(fetchImpl, url, headers, body, retryDelaysMs) {
7010
7016
  let lastFailure = null;
7011
7017
  const deadline = Date.now() + INITIALIZE_RETRY_BUDGET_MS;
7012
- for (let attempt = 0; attempt <= INITIALIZE_RETRY_DELAYS_MS.length; attempt += 1) {
7018
+ for (let attempt = 0; attempt <= retryDelaysMs.length; attempt += 1) {
7013
7019
  try {
7014
7020
  const remainingMs = deadline - Date.now();
7015
7021
  if (remainingMs <= 0) break;
@@ -7033,7 +7039,7 @@ async function initializeWithRetry(fetchImpl, url, headers, body) {
7033
7039
  transient: true
7034
7040
  };
7035
7041
  }
7036
- const retryDelayMs = INITIALIZE_RETRY_DELAYS_MS[attempt];
7042
+ const retryDelayMs = retryDelaysMs[attempt];
7037
7043
  if (retryDelayMs === void 0 || Date.now() + retryDelayMs >= deadline) break;
7038
7044
  await delay(retryDelayMs);
7039
7045
  }
@@ -7669,6 +7675,7 @@ ${reason}`,
7669
7675
  }
7670
7676
  const proof = await proveWorkspaceTools(request, adapter.name, {
7671
7677
  ...this.opts.workspaceProofFetch ? { fetchImpl: this.opts.workspaceProofFetch } : {},
7678
+ ...this.opts.workspaceProofRetryDelaysMs ? { retryDelaysMs: this.opts.workspaceProofRetryDelaysMs } : {},
7672
7679
  harnessFingerprint: turnContext.runtime
7673
7680
  });
7674
7681
  turnLog[proof.ok ? "info" : "error"](
@@ -8486,6 +8493,7 @@ var ASSIGNMENTS_POLL_MS = 6e4;
8486
8493
  var DRAIN_BASE_MS = 1e3;
8487
8494
  var DRAIN_MAX_MS = 3e4;
8488
8495
  var DRAIN_IDLE_MS = 15e3;
8496
+ var DEFAULT_UNRUNNABLE_RETRY_DELAYS_MS = [0, 1e3, 3e3];
8489
8497
  var CompanionSupervisor = class {
8490
8498
  workspaces = /* @__PURE__ */ new Map();
8491
8499
  config;
@@ -8512,6 +8520,7 @@ var CompanionSupervisor = class {
8512
8520
  exitFn;
8513
8521
  reexecFn;
8514
8522
  dispatcherFactory;
8523
+ unrunnableRetryDelaysMs;
8515
8524
  deviceApi = null;
8516
8525
  deviceSub = null;
8517
8526
  heartbeatTimer = null;
@@ -8544,6 +8553,7 @@ var CompanionSupervisor = class {
8544
8553
  this.exitFn = opts.exit ?? ((code) => process.exit(code));
8545
8554
  this.reexecFn = opts.reexec ?? defaultReexec;
8546
8555
  this.dispatcherFactory = opts.dispatcherFactory;
8556
+ this.unrunnableRetryDelaysMs = opts.unrunnableRetryDelaysMs ?? DEFAULT_UNRUNNABLE_RETRY_DELAYS_MS;
8547
8557
  }
8548
8558
  // Stand up the data plane: pair check, initial assignments pull, then the
8549
8559
  // heartbeat + poll loops. A companion with no device token (logged out) does
@@ -9048,9 +9058,8 @@ var CompanionSupervisor = class {
9048
9058
  // wedge the workspace's whole stream.
9049
9059
  async reportUnrunnableDispatch(wr, payload) {
9050
9060
  if (!this.deviceApi) return false;
9051
- const backoffMs = [0, 1e3, 3e3];
9052
9061
  let lastErr;
9053
- for (const wait of backoffMs) {
9062
+ for (const wait of this.unrunnableRetryDelaysMs) {
9054
9063
  if (this.stopped) return false;
9055
9064
  if (wait > 0) await new Promise((r) => setTimeout(r, wait));
9056
9065
  try {
@@ -9351,12 +9360,14 @@ var CompanionSupervisor = class {
9351
9360
  this.stopped = true;
9352
9361
  if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
9353
9362
  if (this.pollTimer) clearInterval(this.pollTimer);
9363
+ const heartbeat = this.inFlightHeartbeat;
9354
9364
  this.deviceSub?.stop();
9355
9365
  for (const wr of this.workspaces.values()) {
9356
9366
  for (const a of wr.agents.values()) a.cancelDrain();
9357
9367
  if (wr.sub) wr.sub.stop();
9358
9368
  }
9359
9369
  await Promise.all([
9370
+ ...heartbeat ? [heartbeat] : [],
9360
9371
  ...this.deviceSub ? [this.deviceSub.finished] : [],
9361
9372
  ...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
9362
9373
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.41",
3
+ "version": "0.6.43",
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",