@cabane/companion 0.6.64 → 0.6.66

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
@@ -2597,15 +2597,16 @@ var CabaneApi = class {
2597
2597
  // (transport errors + 5xx), never a 4xx, so no `(turnId, kind)` row is
2598
2598
  // double-posted. The caller threads its turn abort signal so a cancel
2599
2599
  // mid-commit aborts the in-flight POST rather than letting it land.
2600
- // CT11: `kind` now includes `'stopped'` for the terminal marker the
2601
- // dispatcher writes when a turn is cancelled same wire shape as
2602
- // `progress`/`final`, distinguished only by `kind` so the chat drawer's
2603
- // turn-group renderer treats it as a closing row. `seq` is the companion's
2604
- // per-turn monotonic counter, stamped on the row so the merged timeline
2605
- // orders the commit deterministically against the persisted tool/thinking
2606
- // rows. Both fields are optional on the wire an older companion that didn't
2607
- // mint seq still validates (the server defaults to 0); `stopped` is only
2608
- // emitted by post-CT11 companions.
2600
+ // CT11: `kind` includes `'stopped'` for the terminal cancel marker — same
2601
+ // wire shape as `progress`/`final`, distinguished only by `kind` so the chat
2602
+ // drawer's turn-group renderer treats it as a closing row. CT1295: this
2603
+ // companion no longer WRITES that kind the server does, in the transaction
2604
+ // that terminalizes the stopped turn so the value survives here only as
2605
+ // wire vocabulary the server also speaks. `seq` is the companion's per-turn
2606
+ // monotonic counter, stamped on the row so the merged timeline orders the
2607
+ // commit deterministically against the persisted tool/thinking rows. Both
2608
+ // fields are optional on the wire — an older companion that didn't mint seq
2609
+ // still validates (the server defaults to 0).
2609
2610
  postTurnMessage(workspaceId, conversationId, body, signal) {
2610
2611
  return this.durableCommit(
2611
2612
  "message",
@@ -4379,7 +4380,7 @@ var BASE_CLAUDE_CODE_CONFORMANCE_FIXTURES = [
4379
4380
  {
4380
4381
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result;
4381
4382
  // the adapter stops before the closing reply — no final text, no `result`
4382
- // event. The host writes the `kind:'stopped'` marker off the signal.
4383
+ // event. CT1295: the server writes the `kind:'stopped'` marker.
4383
4384
  name: "cancel mid-stream",
4384
4385
  request: makeRequest(),
4385
4386
  nativeStream: [
@@ -5500,7 +5501,7 @@ var OPENCODE_CONFORMANCE_FIXTURES = [
5500
5501
  {
5501
5502
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
5502
5503
  // adapter stops before the closing reply — no final text, no `result` event.
5503
- // The host writes the `kind:'stopped'` marker off the signal.
5504
+ // CT1295: the server writes the `kind:'stopped'` marker.
5504
5505
  name: "cancel mid-stream",
5505
5506
  request: makeRequest2(),
5506
5507
  nativeStream: [
@@ -6792,7 +6793,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6792
6793
  {
6793
6794
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
6794
6795
  // adapter stops before the closing reply — no final text, no `result` event.
6795
- // The host writes the `kind:'stopped'` marker off the signal.
6796
+ // CT1295: the server writes the `kind:'stopped'` marker.
6796
6797
  name: "cancel mid-stream",
6797
6798
  request: makeRequest3(),
6798
6799
  nativeStream: [
@@ -7253,14 +7254,7 @@ function createSkipState() {
7253
7254
  return { skipped: false, reason: null };
7254
7255
  }
7255
7256
  function createAskState() {
7256
- return {
7257
- targetUserId: null,
7258
- question: null,
7259
- headline: null,
7260
- options: null,
7261
- questions: null,
7262
- order: null
7263
- };
7257
+ return { targetUserId: null, question: null, headline: null, options: null, questions: null };
7264
7258
  }
7265
7259
  function createWakeState() {
7266
7260
  return { afterSeconds: null, at: null, note: null, cancelled: false };
@@ -7406,7 +7400,6 @@ function createTurnControlMcpServer(sendState, skipState, askState, subAgentCrea
7406
7400
  };
7407
7401
  }
7408
7402
  askState.targetUserId = args.targetUserId;
7409
- askState.order = controlOrder?.next() ?? null;
7410
7403
  if (hasArray) {
7411
7404
  askState.questions = args.questions;
7412
7405
  askState.question = null;
@@ -7948,8 +7941,10 @@ var TurnCommitter = class {
7948
7941
  // End-of-turn progress promotion. The held-text flush is now the adapter's
7949
7942
  // job (it emits the closing reply as a `text` event before `result`), so this
7950
7943
  // only asks the pump to promote agent-authored interim text. A wordless turn
7951
- // stays wordless and the dispatcher closes it with a marker. Guards on the
7952
- // abort signal, so on cancel no `final` is forced.
7944
+ // stays wordless and the dispatcher closes it with a `silent`/`skipped`
7945
+ // marker (CT1295: a CANCELLED turn is closed by the SERVER's `stopped`
7946
+ // marker, not from this side). Guards on the abort signal, so on cancel no
7947
+ // `final` is forced.
7953
7948
  async finalize(okResult) {
7954
7949
  await this.pump.finalize(okResult);
7955
7950
  }
@@ -7980,10 +7975,15 @@ var TurnCommitter = class {
7980
7975
  }
7981
7976
  // CT1281: the order the control tools were CALLED, for the addressed rows the
7982
7977
  // server writes from this one commit. It reports a position only for an intent
7983
- // that actually made it into the commit — an ask whose payload was incomplete,
7984
- // or a self-send the committer stripped, contributes no row and so has no place
7985
- // in the line. An intent with no recorded call (the runtime's auto-declared
7986
- // reply) is deliberately absent: the server sorts it last, which is what it is.
7978
+ // that actually made it into the commit — a self-send the committer stripped
7979
+ // contributes no row and so has no place in the line. An intent with no
7980
+ // recorded call (the runtime's auto-declared reply) is deliberately absent, and
7981
+ // the server sorts it after every recorded one it binds the turn's closing
7982
+ // words.
7983
+ //
7984
+ // The ask is absent by design, not by omission: its carrier queues last among
7985
+ // the turn's rows however early the tool fired, so there is no position to
7986
+ // report.
7987
7987
  orderField(fields) {
7988
7988
  const order = {};
7989
7989
  const replyOrder = this.deps.replyState.order;
@@ -7991,9 +7991,6 @@ var TurnCommitter = class {
7991
7991
  if (fields.dispatch !== void 0 && this.deps.sendState.order !== null) {
7992
7992
  order.send = this.deps.sendState.order;
7993
7993
  }
7994
- if (fields.ask !== void 0 && this.deps.askState.order !== null) {
7995
- order.ask = this.deps.askState.order;
7996
- }
7997
7994
  return Object.keys(order).length > 0 ? { turnControlOrder: order } : {};
7998
7995
  }
7999
7996
  // The declared reply: explicit `reply_to` first, else the runtime's own
@@ -8067,7 +8064,6 @@ var TurnCommitter = class {
8067
8064
  var PREPARING_TOOL_NAME = "preparing";
8068
8065
  var PREPARE_FAILED_PREFIX = "**Couldn't prepare your environment.** I wasn't able to provision a working directory for this conversation, so I can't run this turn. The provisioning command reported:";
8069
8066
  var MISSING_SECRET_PREFIX = "**Missing secret on this companion.** This agent's tools need a credential this device hasn't been given, so I can't run this turn safely. Declare it in this companion\u2019s secret store (`~/.cabane/secrets.json`) and try again. Missing:";
8070
- var STOPPED_MARKER_BODY = "(stopped)";
8071
8067
  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:";
8072
8068
  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`;
8073
8069
  var SKIPPED_MARKER_BODY = "(skipped)";
@@ -8685,7 +8681,6 @@ ${reason}`,
8685
8681
  );
8686
8682
  if (intent.ask) {
8687
8683
  askState.targetUserId = intent.ask.targetUserId;
8688
- askState.order = intent.askOrder ?? null;
8689
8684
  if (intent.ask.questions && intent.ask.questions.length > 0) {
8690
8685
  askState.questions = intent.ask.questions;
8691
8686
  askState.question = null;
@@ -8900,23 +8895,6 @@ ${reason}`,
8900
8895
  }
8901
8896
  }
8902
8897
  }
8903
- if (userCancelled) {
8904
- try {
8905
- await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
8906
- body: STOPPED_MARKER_BODY,
8907
- kind: "stopped",
8908
- turnId,
8909
- seq: nextSeq(),
8910
- // CT113: the stopped marker is still "about" the triggering message.
8911
- parentMessageId: payload.messageId
8912
- });
8913
- } catch (err) {
8914
- turnLog.warn(
8915
- { err: err instanceof Error ? err.message : String(err) },
8916
- "dispatcher: stopped-marker commit failed"
8917
- );
8918
- }
8919
- }
8920
8898
  const body = {
8921
8899
  activeRunStartedAt: null,
8922
8900
  // CT277: the server keys the turn-end metadata UPDATE (ended_at + tokens)
package/dist/runtime.js CHANGED
@@ -2017,15 +2017,16 @@ var CabaneApi = class {
2017
2017
  // (transport errors + 5xx), never a 4xx, so no `(turnId, kind)` row is
2018
2018
  // double-posted. The caller threads its turn abort signal so a cancel
2019
2019
  // mid-commit aborts the in-flight POST rather than letting it land.
2020
- // CT11: `kind` now includes `'stopped'` for the terminal marker the
2021
- // dispatcher writes when a turn is cancelled same wire shape as
2022
- // `progress`/`final`, distinguished only by `kind` so the chat drawer's
2023
- // turn-group renderer treats it as a closing row. `seq` is the companion's
2024
- // per-turn monotonic counter, stamped on the row so the merged timeline
2025
- // orders the commit deterministically against the persisted tool/thinking
2026
- // rows. Both fields are optional on the wire an older companion that didn't
2027
- // mint seq still validates (the server defaults to 0); `stopped` is only
2028
- // emitted by post-CT11 companions.
2020
+ // CT11: `kind` includes `'stopped'` for the terminal cancel marker — same
2021
+ // wire shape as `progress`/`final`, distinguished only by `kind` so the chat
2022
+ // drawer's turn-group renderer treats it as a closing row. CT1295: this
2023
+ // companion no longer WRITES that kind the server does, in the transaction
2024
+ // that terminalizes the stopped turn so the value survives here only as
2025
+ // wire vocabulary the server also speaks. `seq` is the companion's per-turn
2026
+ // monotonic counter, stamped on the row so the merged timeline orders the
2027
+ // commit deterministically against the persisted tool/thinking rows. Both
2028
+ // fields are optional on the wire — an older companion that didn't mint seq
2029
+ // still validates (the server defaults to 0).
2029
2030
  postTurnMessage(workspaceId, conversationId, body, signal) {
2030
2031
  return this.durableCommit(
2031
2032
  "message",
@@ -3878,7 +3879,7 @@ var BASE_CLAUDE_CODE_CONFORMANCE_FIXTURES = [
3878
3879
  {
3879
3880
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result;
3880
3881
  // the adapter stops before the closing reply — no final text, no `result`
3881
- // event. The host writes the `kind:'stopped'` marker off the signal.
3882
+ // event. CT1295: the server writes the `kind:'stopped'` marker.
3882
3883
  name: "cancel mid-stream",
3883
3884
  request: makeRequest(),
3884
3885
  nativeStream: [
@@ -4999,7 +5000,7 @@ var OPENCODE_CONFORMANCE_FIXTURES = [
4999
5000
  {
5000
5001
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
5001
5002
  // adapter stops before the closing reply — no final text, no `result` event.
5002
- // The host writes the `kind:'stopped'` marker off the signal.
5003
+ // CT1295: the server writes the `kind:'stopped'` marker.
5003
5004
  name: "cancel mid-stream",
5004
5005
  request: makeRequest2(),
5005
5006
  nativeStream: [
@@ -6291,7 +6292,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6291
6292
  {
6292
6293
  // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
6293
6294
  // adapter stops before the closing reply — no final text, no `result` event.
6294
- // The host writes the `kind:'stopped'` marker off the signal.
6295
+ // CT1295: the server writes the `kind:'stopped'` marker.
6295
6296
  name: "cancel mid-stream",
6296
6297
  request: makeRequest3(),
6297
6298
  nativeStream: [
@@ -6752,14 +6753,7 @@ function createSkipState() {
6752
6753
  return { skipped: false, reason: null };
6753
6754
  }
6754
6755
  function createAskState() {
6755
- return {
6756
- targetUserId: null,
6757
- question: null,
6758
- headline: null,
6759
- options: null,
6760
- questions: null,
6761
- order: null
6762
- };
6756
+ return { targetUserId: null, question: null, headline: null, options: null, questions: null };
6763
6757
  }
6764
6758
  function createWakeState() {
6765
6759
  return { afterSeconds: null, at: null, note: null, cancelled: false };
@@ -6905,7 +6899,6 @@ function createTurnControlMcpServer(sendState, skipState, askState, subAgentCrea
6905
6899
  };
6906
6900
  }
6907
6901
  askState.targetUserId = args.targetUserId;
6908
- askState.order = controlOrder?.next() ?? null;
6909
6902
  if (hasArray) {
6910
6903
  askState.questions = args.questions;
6911
6904
  askState.question = null;
@@ -7447,8 +7440,10 @@ var TurnCommitter = class {
7447
7440
  // End-of-turn progress promotion. The held-text flush is now the adapter's
7448
7441
  // job (it emits the closing reply as a `text` event before `result`), so this
7449
7442
  // only asks the pump to promote agent-authored interim text. A wordless turn
7450
- // stays wordless and the dispatcher closes it with a marker. Guards on the
7451
- // abort signal, so on cancel no `final` is forced.
7443
+ // stays wordless and the dispatcher closes it with a `silent`/`skipped`
7444
+ // marker (CT1295: a CANCELLED turn is closed by the SERVER's `stopped`
7445
+ // marker, not from this side). Guards on the abort signal, so on cancel no
7446
+ // `final` is forced.
7452
7447
  async finalize(okResult) {
7453
7448
  await this.pump.finalize(okResult);
7454
7449
  }
@@ -7479,10 +7474,15 @@ var TurnCommitter = class {
7479
7474
  }
7480
7475
  // CT1281: the order the control tools were CALLED, for the addressed rows the
7481
7476
  // server writes from this one commit. It reports a position only for an intent
7482
- // that actually made it into the commit — an ask whose payload was incomplete,
7483
- // or a self-send the committer stripped, contributes no row and so has no place
7484
- // in the line. An intent with no recorded call (the runtime's auto-declared
7485
- // reply) is deliberately absent: the server sorts it last, which is what it is.
7477
+ // that actually made it into the commit — a self-send the committer stripped
7478
+ // contributes no row and so has no place in the line. An intent with no
7479
+ // recorded call (the runtime's auto-declared reply) is deliberately absent, and
7480
+ // the server sorts it after every recorded one it binds the turn's closing
7481
+ // words.
7482
+ //
7483
+ // The ask is absent by design, not by omission: its carrier queues last among
7484
+ // the turn's rows however early the tool fired, so there is no position to
7485
+ // report.
7486
7486
  orderField(fields) {
7487
7487
  const order = {};
7488
7488
  const replyOrder = this.deps.replyState.order;
@@ -7490,9 +7490,6 @@ var TurnCommitter = class {
7490
7490
  if (fields.dispatch !== void 0 && this.deps.sendState.order !== null) {
7491
7491
  order.send = this.deps.sendState.order;
7492
7492
  }
7493
- if (fields.ask !== void 0 && this.deps.askState.order !== null) {
7494
- order.ask = this.deps.askState.order;
7495
- }
7496
7493
  return Object.keys(order).length > 0 ? { turnControlOrder: order } : {};
7497
7494
  }
7498
7495
  // The declared reply: explicit `reply_to` first, else the runtime's own
@@ -7566,7 +7563,6 @@ var TurnCommitter = class {
7566
7563
  var PREPARING_TOOL_NAME = "preparing";
7567
7564
  var PREPARE_FAILED_PREFIX = "**Couldn't prepare your environment.** I wasn't able to provision a working directory for this conversation, so I can't run this turn. The provisioning command reported:";
7568
7565
  var MISSING_SECRET_PREFIX = "**Missing secret on this companion.** This agent's tools need a credential this device hasn't been given, so I can't run this turn safely. Declare it in this companion\u2019s secret store (`~/.cabane/secrets.json`) and try again. Missing:";
7569
- var STOPPED_MARKER_BODY = "(stopped)";
7570
7566
  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:";
7571
7567
  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`;
7572
7568
  var SKIPPED_MARKER_BODY = "(skipped)";
@@ -8184,7 +8180,6 @@ ${reason}`,
8184
8180
  );
8185
8181
  if (intent.ask) {
8186
8182
  askState.targetUserId = intent.ask.targetUserId;
8187
- askState.order = intent.askOrder ?? null;
8188
8183
  if (intent.ask.questions && intent.ask.questions.length > 0) {
8189
8184
  askState.questions = intent.ask.questions;
8190
8185
  askState.question = null;
@@ -8399,23 +8394,6 @@ ${reason}`,
8399
8394
  }
8400
8395
  }
8401
8396
  }
8402
- if (userCancelled) {
8403
- try {
8404
- await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
8405
- body: STOPPED_MARKER_BODY,
8406
- kind: "stopped",
8407
- turnId,
8408
- seq: nextSeq(),
8409
- // CT113: the stopped marker is still "about" the triggering message.
8410
- parentMessageId: payload.messageId
8411
- });
8412
- } catch (err) {
8413
- turnLog.warn(
8414
- { err: err instanceof Error ? err.message : String(err) },
8415
- "dispatcher: stopped-marker commit failed"
8416
- );
8417
- }
8418
- }
8419
8397
  const body = {
8420
8398
  activeRunStartedAt: null,
8421
8399
  // CT277: the server keys the turn-end metadata UPDATE (ended_at + tokens)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.64",
3
+ "version": "0.6.66",
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",