@cabane/companion 0.6.23 → 0.6.25

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
@@ -6835,6 +6835,22 @@ function checkoutState(cwd) {
6835
6835
  function runKey(conversationId, agentId) {
6836
6836
  return `${conversationId}|${agentId}`;
6837
6837
  }
6838
+ var ERROR_BODY_LOG_CAP = 2e3;
6839
+ function describeErrorBody(body) {
6840
+ if (body === void 0 || body === null) return void 0;
6841
+ let text;
6842
+ if (typeof body === "string") {
6843
+ text = body;
6844
+ } else {
6845
+ try {
6846
+ text = JSON.stringify(body);
6847
+ } catch {
6848
+ text = String(body);
6849
+ }
6850
+ }
6851
+ if (text.length === 0) return void 0;
6852
+ return text.length > ERROR_BODY_LOG_CAP ? `${text.slice(0, ERROR_BODY_LOG_CAP)}\u2026` : text;
6853
+ }
6838
6854
  function describeSubAgentError(status2, body) {
6839
6855
  const code = body && typeof body === "object" && "error" in body ? String(body.error) : void 0;
6840
6856
  switch (code) {
@@ -6860,6 +6876,13 @@ var Dispatcher = class {
6860
6876
  opts;
6861
6877
  // SJ383: per-(conversation, agent) abort registry.
6862
6878
  aborts;
6879
+ // CT1109: pairs already told, in the conversation, that their session state is
6880
+ // being refused. The failure repeats every single turn until someone fixes the
6881
+ // payload, so without this the notice would be a per-turn drumbeat; one notice
6882
+ // is the signal and the rest is noise. Deliberately in-memory and unbounded-free:
6883
+ // a companion restart re-arms it, which is the behaviour we want — a restart is
6884
+ // exactly when it's worth re-stating that memory is still being lost.
6885
+ sessionWriteNotified = /* @__PURE__ */ new Set();
6863
6886
  notifyStart(info) {
6864
6887
  try {
6865
6888
  this.opts.observer?.onStart(info);
@@ -7430,6 +7453,7 @@ ${reason}`,
7430
7453
  ) : null;
7431
7454
  let sessionWritten = false;
7432
7455
  let sessionDegraded = false;
7456
+ let sessionWriteRejected = false;
7433
7457
  let okResult = false;
7434
7458
  let resultReason;
7435
7459
  const turnRuntime = turnContext.runtime;
@@ -7550,10 +7574,25 @@ ${reason}`,
7550
7574
  { agentSessionId: event.state }
7551
7575
  );
7552
7576
  } catch (err) {
7553
- turnLog.warn(
7554
- { err: err instanceof Error ? err.message : String(err) },
7555
- "dispatcher: session-id write failed (will retry next turn)"
7556
- );
7577
+ const status2 = err instanceof ApiError ? err.status : void 0;
7578
+ if (status2 !== void 0 && status2 >= 400 && status2 < 500) {
7579
+ sessionWriteRejected = true;
7580
+ turnLog.error(
7581
+ {
7582
+ err: err instanceof Error ? err.message : String(err),
7583
+ status: status2,
7584
+ responseBody: describeErrorBody(err instanceof ApiError ? err.body : void 0),
7585
+ stateLength: event.state.length,
7586
+ runtime: turnRuntime
7587
+ },
7588
+ "dispatcher: session-state write REJECTED (4xx) \u2014 this turn is not resumable and the next turn starts with no memory of it"
7589
+ );
7590
+ } else {
7591
+ turnLog.warn(
7592
+ { err: err instanceof Error ? err.message : String(err) },
7593
+ "dispatcher: session-id write failed (will retry next turn)"
7594
+ );
7595
+ }
7557
7596
  }
7558
7597
  }
7559
7598
  } else if (event.type === "result") {
@@ -7680,6 +7719,10 @@ ${reason}`,
7680
7719
  if (sessionDegraded) {
7681
7720
  body.degraded = true;
7682
7721
  }
7722
+ if (sessionWriteRejected && !this.sessionWriteNotified.has(key)) {
7723
+ body.sessionWriteRejected = true;
7724
+ this.sessionWriteNotified.add(key);
7725
+ }
7683
7726
  this.opts.connectorHealth?.recordSettle(turnRuntime, {
7684
7727
  ok: okResult,
7685
7728
  errorReason: body.errorReason ?? null
@@ -8235,7 +8278,8 @@ var SseSubscriber = class {
8235
8278
  }
8236
8279
  async connect() {
8237
8280
  const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
8238
- const url = `${base}/api/workspaces/${this.opts.workspaceId}/events`;
8281
+ const query = this.lastEventId ? "" : "?replayPending=1";
8282
+ const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
8239
8283
  this.controller = new AbortController();
8240
8284
  const headers = {
8241
8285
  Authorization: `Bearer ${this.opts.token}`,
package/dist/runtime.js CHANGED
@@ -6454,6 +6454,22 @@ function checkoutState(cwd) {
6454
6454
  function runKey(conversationId, agentId) {
6455
6455
  return `${conversationId}|${agentId}`;
6456
6456
  }
6457
+ var ERROR_BODY_LOG_CAP = 2e3;
6458
+ function describeErrorBody(body) {
6459
+ if (body === void 0 || body === null) return void 0;
6460
+ let text;
6461
+ if (typeof body === "string") {
6462
+ text = body;
6463
+ } else {
6464
+ try {
6465
+ text = JSON.stringify(body);
6466
+ } catch {
6467
+ text = String(body);
6468
+ }
6469
+ }
6470
+ if (text.length === 0) return void 0;
6471
+ return text.length > ERROR_BODY_LOG_CAP ? `${text.slice(0, ERROR_BODY_LOG_CAP)}\u2026` : text;
6472
+ }
6457
6473
  function describeSubAgentError(status, body) {
6458
6474
  const code = body && typeof body === "object" && "error" in body ? String(body.error) : void 0;
6459
6475
  switch (code) {
@@ -6479,6 +6495,13 @@ var Dispatcher = class {
6479
6495
  opts;
6480
6496
  // SJ383: per-(conversation, agent) abort registry.
6481
6497
  aborts;
6498
+ // CT1109: pairs already told, in the conversation, that their session state is
6499
+ // being refused. The failure repeats every single turn until someone fixes the
6500
+ // payload, so without this the notice would be a per-turn drumbeat; one notice
6501
+ // is the signal and the rest is noise. Deliberately in-memory and unbounded-free:
6502
+ // a companion restart re-arms it, which is the behaviour we want — a restart is
6503
+ // exactly when it's worth re-stating that memory is still being lost.
6504
+ sessionWriteNotified = /* @__PURE__ */ new Set();
6482
6505
  notifyStart(info) {
6483
6506
  try {
6484
6507
  this.opts.observer?.onStart(info);
@@ -7049,6 +7072,7 @@ ${reason}`,
7049
7072
  ) : null;
7050
7073
  let sessionWritten = false;
7051
7074
  let sessionDegraded = false;
7075
+ let sessionWriteRejected = false;
7052
7076
  let okResult = false;
7053
7077
  let resultReason;
7054
7078
  const turnRuntime = turnContext.runtime;
@@ -7169,10 +7193,25 @@ ${reason}`,
7169
7193
  { agentSessionId: event.state }
7170
7194
  );
7171
7195
  } catch (err) {
7172
- turnLog.warn(
7173
- { err: err instanceof Error ? err.message : String(err) },
7174
- "dispatcher: session-id write failed (will retry next turn)"
7175
- );
7196
+ const status = err instanceof ApiError ? err.status : void 0;
7197
+ if (status !== void 0 && status >= 400 && status < 500) {
7198
+ sessionWriteRejected = true;
7199
+ turnLog.error(
7200
+ {
7201
+ err: err instanceof Error ? err.message : String(err),
7202
+ status,
7203
+ responseBody: describeErrorBody(err instanceof ApiError ? err.body : void 0),
7204
+ stateLength: event.state.length,
7205
+ runtime: turnRuntime
7206
+ },
7207
+ "dispatcher: session-state write REJECTED (4xx) \u2014 this turn is not resumable and the next turn starts with no memory of it"
7208
+ );
7209
+ } else {
7210
+ turnLog.warn(
7211
+ { err: err instanceof Error ? err.message : String(err) },
7212
+ "dispatcher: session-id write failed (will retry next turn)"
7213
+ );
7214
+ }
7176
7215
  }
7177
7216
  }
7178
7217
  } else if (event.type === "result") {
@@ -7299,6 +7338,10 @@ ${reason}`,
7299
7338
  if (sessionDegraded) {
7300
7339
  body.degraded = true;
7301
7340
  }
7341
+ if (sessionWriteRejected && !this.sessionWriteNotified.has(key)) {
7342
+ body.sessionWriteRejected = true;
7343
+ this.sessionWriteNotified.add(key);
7344
+ }
7302
7345
  this.opts.connectorHealth?.recordSettle(turnRuntime, {
7303
7346
  ok: okResult,
7304
7347
  errorReason: body.errorReason ?? null
@@ -7854,7 +7897,8 @@ var SseSubscriber = class {
7854
7897
  }
7855
7898
  async connect() {
7856
7899
  const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
7857
- const url = `${base}/api/workspaces/${this.opts.workspaceId}/events`;
7900
+ const query = this.lastEventId ? "" : "?replayPending=1";
7901
+ const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
7858
7902
  this.controller = new AbortController();
7859
7903
  const headers = {
7860
7904
  Authorization: `Bearer ${this.opts.token}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.23",
3
+ "version": "0.6.25",
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",