@cabane/companion 0.6.198 → 0.6.200

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
@@ -2616,7 +2616,7 @@ var init_descriptions = __esm({
2616
2616
  "withdraw_ask"
2617
2617
  ];
2618
2618
  TURN_CONTROL_TOOL_DESCRIPTIONS = {
2619
- 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, by their \`@handle\`; to bring in an agent, name it with \`@handle\` in your reply.
2619
+ 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, by their \`@handle\`; to bring in an agent, name them with \`@handle\` in your reply.
2620
2620
  - 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.
2621
2621
  - \`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.
2622
2622
  - 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.
@@ -12686,7 +12686,7 @@ function turnFailureCopy(reason, runtime) {
12686
12686
  if (reason === "timeout_total")
12687
12687
  return "The turn ran longer than expected and was stopped. Reply in Cabane to continue.";
12688
12688
  if (reason === "result_error:error_max_turns")
12689
- return "The agent reached its step limit. Reply in Cabane to continue.";
12689
+ return "The agent reached their step limit. Reply in Cabane to continue.";
12690
12690
  if (reason === "no_result" || reason === "empty_result" || reason === "empty_result_unverified")
12691
12691
  return "The agent finished without a response. Reply in Cabane to try again.";
12692
12692
  if (reason === "session_start_failed")
@@ -12700,11 +12700,11 @@ function turnFailureCopy(reason, runtime) {
12700
12700
  if (reason?.startsWith("seq_floor_unavailable:"))
12701
12701
  return "The interrupted turn could not safely continue. Reply in Cabane to continue.";
12702
12702
  if (reason === "result_error:error_max_budget_usd")
12703
- return "The agent reached its spending limit. Check its budget settings before trying again in Cabane.";
12703
+ return "The agent reached their spending limit. Check their budget settings before trying again in Cabane.";
12704
12704
  if (reason === "result_error:error_max_structured_output_retries")
12705
12705
  return "The agent could not produce the requested response format. Check the requested format, then reply in Cabane to try again.";
12706
12706
  if (reason?.startsWith("result_error:"))
12707
- return "The agent stopped with an error while running. Check its transcript, then reply in Cabane to try again.";
12707
+ return "The agent stopped with an error while running. Check their transcript, then reply in Cabane to try again.";
12708
12708
  if (reason === "setup_failed")
12709
12709
  return "The turn could not be set up. Check the agent setup on this device, then reply in Cabane to try again.";
12710
12710
  if (reason === "unexpected_role")
@@ -14179,6 +14179,10 @@ var CompanionSupervisor = class {
14179
14179
  // Whether the device stream is open, which is what every workspace's link on
14180
14180
  // the dashboard reports.
14181
14181
  deviceConnected = false;
14182
+ // Set when the device stream ended on a 403: the device was deactivated, not
14183
+ // unpaired. The next accepted heartbeat proves it was reactivated, and
14184
+ // reopens the stream.
14185
+ deviceStreamDeactivated = false;
14182
14186
  recoveryChecked = false;
14183
14187
  recovering = false;
14184
14188
  heartbeated = false;
@@ -14314,7 +14318,15 @@ var CompanionSupervisor = class {
14314
14318
  }
14315
14319
  if (ev.event === "message") await this.handleDeviceMessage(ev);
14316
14320
  },
14317
- onAuthFailure: () => {
14321
+ onAuthFailure: (status2) => {
14322
+ if (status2 === 403) {
14323
+ this.deviceStreamDeactivated = true;
14324
+ this.heartbeatTimer?.ref();
14325
+ this.log.warn(
14326
+ "This device was deactivated in Cabane. The companion will reconnect once it is reactivated."
14327
+ );
14328
+ return;
14329
+ }
14318
14330
  this.log.error(
14319
14331
  `Cabane no longer accepts this device. Run ${cabane("logout")}, then ${cabane("setup")} to pair again.`
14320
14332
  );
@@ -14404,6 +14416,12 @@ var CompanionSupervisor = class {
14404
14416
  lastAcknowledgedDevice: view.success ? view.data : null,
14405
14417
  lastHeartbeatRefused: false
14406
14418
  };
14419
+ if (this.deviceStreamDeactivated && this.deviceSub && !this.stopped) {
14420
+ this.deviceStreamDeactivated = false;
14421
+ this.heartbeatTimer?.unref();
14422
+ this.log.info("This device was reactivated; reconnecting to Cabane.");
14423
+ this.deviceSub.start();
14424
+ }
14407
14425
  void this.recoverRuns();
14408
14426
  void this.updater?.onHeartbeat(res.companion, this.deviceConnected).catch(
14409
14427
  (err) => this.log.warn(
@@ -14586,7 +14604,7 @@ var CompanionSupervisor = class {
14586
14604
  if (!credential) {
14587
14605
  this.log.error(
14588
14606
  { workspaceId, agentId: it.agentId, username: it.agentUsername },
14589
- `Cannot run ${it.agentDisplayName}: its sign-in to Cabane is missing and Cabane didn't send a new one. Asking again on the next check.`
14607
+ `Cannot run ${it.agentDisplayName}: their sign-in to Cabane is missing and Cabane didn't send a new one. Asking again on the next check.`
14590
14608
  );
14591
14609
  this.removeAgent(wr, it.agentId);
14592
14610
  this.hub.setAgent(workspaceId, {
package/dist/runtime.js CHANGED
@@ -1756,7 +1756,7 @@ function turnFailureCopy(reason, runtime) {
1756
1756
  if (reason === "timeout_total")
1757
1757
  return "The turn ran longer than expected and was stopped. Reply in Cabane to continue.";
1758
1758
  if (reason === "result_error:error_max_turns")
1759
- return "The agent reached its step limit. Reply in Cabane to continue.";
1759
+ return "The agent reached their step limit. Reply in Cabane to continue.";
1760
1760
  if (reason === "no_result" || reason === "empty_result" || reason === "empty_result_unverified")
1761
1761
  return "The agent finished without a response. Reply in Cabane to try again.";
1762
1762
  if (reason === "session_start_failed")
@@ -1770,11 +1770,11 @@ function turnFailureCopy(reason, runtime) {
1770
1770
  if (reason?.startsWith("seq_floor_unavailable:"))
1771
1771
  return "The interrupted turn could not safely continue. Reply in Cabane to continue.";
1772
1772
  if (reason === "result_error:error_max_budget_usd")
1773
- return "The agent reached its spending limit. Check its budget settings before trying again in Cabane.";
1773
+ return "The agent reached their spending limit. Check their budget settings before trying again in Cabane.";
1774
1774
  if (reason === "result_error:error_max_structured_output_retries")
1775
1775
  return "The agent could not produce the requested response format. Check the requested format, then reply in Cabane to try again.";
1776
1776
  if (reason?.startsWith("result_error:"))
1777
- return "The agent stopped with an error while running. Check its transcript, then reply in Cabane to try again.";
1777
+ return "The agent stopped with an error while running. Check their transcript, then reply in Cabane to try again.";
1778
1778
  if (reason === "setup_failed")
1779
1779
  return "The turn could not be set up. Check the agent setup on this device, then reply in Cabane to try again.";
1780
1780
  if (reason === "unexpected_role")
@@ -3079,7 +3079,7 @@ var TURN_CONTROL_TOOLS = [
3079
3079
  "withdraw_ask"
3080
3080
  ];
3081
3081
  var TURN_CONTROL_TOOL_DESCRIPTIONS = {
3082
- 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, by their \`@handle\`; to bring in an agent, name it with \`@handle\` in your reply.
3082
+ 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, by their \`@handle\`; to bring in an agent, name them with \`@handle\` in your reply.
3083
3083
  - 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.
3084
3084
  - \`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.
3085
3085
  - 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.
@@ -9789,6 +9789,10 @@ var CompanionSupervisor = class {
9789
9789
  // Whether the device stream is open, which is what every workspace's link on
9790
9790
  // the dashboard reports.
9791
9791
  deviceConnected = false;
9792
+ // Set when the device stream ended on a 403: the device was deactivated, not
9793
+ // unpaired. The next accepted heartbeat proves it was reactivated, and
9794
+ // reopens the stream.
9795
+ deviceStreamDeactivated = false;
9792
9796
  recoveryChecked = false;
9793
9797
  recovering = false;
9794
9798
  heartbeated = false;
@@ -9924,7 +9928,15 @@ var CompanionSupervisor = class {
9924
9928
  }
9925
9929
  if (ev.event === "message") await this.handleDeviceMessage(ev);
9926
9930
  },
9927
- onAuthFailure: () => {
9931
+ onAuthFailure: (status) => {
9932
+ if (status === 403) {
9933
+ this.deviceStreamDeactivated = true;
9934
+ this.heartbeatTimer?.ref();
9935
+ this.log.warn(
9936
+ "This device was deactivated in Cabane. The companion will reconnect once it is reactivated."
9937
+ );
9938
+ return;
9939
+ }
9928
9940
  this.log.error(
9929
9941
  `Cabane no longer accepts this device. Run ${cabane("logout")}, then ${cabane("setup")} to pair again.`
9930
9942
  );
@@ -10014,6 +10026,12 @@ var CompanionSupervisor = class {
10014
10026
  lastAcknowledgedDevice: view.success ? view.data : null,
10015
10027
  lastHeartbeatRefused: false
10016
10028
  };
10029
+ if (this.deviceStreamDeactivated && this.deviceSub && !this.stopped) {
10030
+ this.deviceStreamDeactivated = false;
10031
+ this.heartbeatTimer?.unref();
10032
+ this.log.info("This device was reactivated; reconnecting to Cabane.");
10033
+ this.deviceSub.start();
10034
+ }
10017
10035
  void this.recoverRuns();
10018
10036
  void this.updater?.onHeartbeat(res.companion, this.deviceConnected).catch(
10019
10037
  (err) => this.log.warn(
@@ -10196,7 +10214,7 @@ var CompanionSupervisor = class {
10196
10214
  if (!credential) {
10197
10215
  this.log.error(
10198
10216
  { workspaceId, agentId: it.agentId, username: it.agentUsername },
10199
- `Cannot run ${it.agentDisplayName}: its sign-in to Cabane is missing and Cabane didn't send a new one. Asking again on the next check.`
10217
+ `Cannot run ${it.agentDisplayName}: their sign-in to Cabane is missing and Cabane didn't send a new one. Asking again on the next check.`
10200
10218
  );
10201
10219
  this.removeAgent(wr, it.agentId);
10202
10220
  this.hub.setAgent(workspaceId, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.198",
3
+ "version": "0.6.200",
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",