@cabane/companion 0.6.114 → 0.6.116

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
@@ -2877,6 +2877,14 @@ function apiErrorCode(err) {
2877
2877
  const body = err.body;
2878
2878
  return typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : null;
2879
2879
  }
2880
+ function apiErrorMessage(err) {
2881
+ const body = err instanceof ApiError ? err.body : void 0;
2882
+ if (body !== null && typeof body === "object") {
2883
+ const message = body.message;
2884
+ if (typeof message === "string" && message.length > 0) return message;
2885
+ }
2886
+ return err instanceof Error ? err.message : String(err);
2887
+ }
2880
2888
  function leaseRefusal(err) {
2881
2889
  const code = apiErrorCode(err);
2882
2890
  if (code && LEASE_REFUSALS.has(code)) return code;
@@ -6839,6 +6847,16 @@ function readUsage(ev) {
6839
6847
  cacheCreationTokens: num(usage.cache_write_input_tokens)
6840
6848
  };
6841
6849
  }
6850
+ function codexUsageDelta(cumulative, baseline) {
6851
+ if (!baseline) return cumulative;
6852
+ const sub = (a, b) => Math.max(0, a - b);
6853
+ return {
6854
+ inputTokens: sub(cumulative.inputTokens, baseline.inputTokens),
6855
+ outputTokens: sub(cumulative.outputTokens, baseline.outputTokens),
6856
+ cacheReadTokens: sub(cumulative.cacheReadTokens, baseline.cacheReadTokens),
6857
+ cacheCreationTokens: sub(cumulative.cacheCreationTokens, baseline.cacheCreationTokens)
6858
+ };
6859
+ }
6842
6860
  function toStatus(v) {
6843
6861
  return v === "completed" || v === "failed" ? v : "in_progress";
6844
6862
  }
@@ -6913,9 +6931,26 @@ function decideResume3(stored, currentCwd) {
6913
6931
  }
6914
6932
  return {
6915
6933
  resume: state.threadId,
6916
- promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null
6934
+ promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
6935
+ usageBaseline: readUsageBaseline(state.usageBaseline)
6917
6936
  };
6918
6937
  }
6938
+ function readUsageBaseline(raw) {
6939
+ if (!raw || typeof raw !== "object") return null;
6940
+ const rec2 = raw;
6941
+ const read = (key) => {
6942
+ const v = rec2[key];
6943
+ return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
6944
+ };
6945
+ const inputTokens = read("inputTokens");
6946
+ const outputTokens = read("outputTokens");
6947
+ const cacheReadTokens = read("cacheReadTokens");
6948
+ const cacheCreationTokens = read("cacheCreationTokens");
6949
+ if (inputTokens === null || outputTokens === null || cacheReadTokens === null || cacheCreationTokens === null) {
6950
+ return null;
6951
+ }
6952
+ return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens };
6953
+ }
6919
6954
 
6920
6955
  // packages/agent-runtime/src/codex/loop.ts
6921
6956
  async function* decodeCodexStream(events, ctx) {
@@ -6927,6 +6962,8 @@ async function* decodeCodexStream(events, ctx) {
6927
6962
  let ok = false;
6928
6963
  let reason;
6929
6964
  let usage;
6965
+ let cumulativeUsage;
6966
+ let threadIdForSession = ctx.resumedThreadId;
6930
6967
  let settled = false;
6931
6968
  function* flushInterim() {
6932
6969
  const sealed = sealHeld2(held, false);
@@ -6937,11 +6974,17 @@ async function* decodeCodexStream(events, ctx) {
6937
6974
  if (ctx.signal.aborted) return;
6938
6975
  if (ev.type === "thread.started") {
6939
6976
  const threadId = readThreadId(ev);
6977
+ if (threadId) threadIdForSession = threadId;
6940
6978
  if (threadId && !sessionEmitted) {
6941
6979
  const state = encodeSession3({
6942
6980
  threadId,
6943
6981
  cwd: ctx.cwd ?? "",
6944
- promptFingerprint: ctx.promptFingerprint ?? null
6982
+ promptFingerprint: ctx.promptFingerprint ?? null,
6983
+ // CT1645: the baseline this turn INHERITED, carried through unchanged.
6984
+ // This early write exists so a turn that dies mid-stream still leaves
6985
+ // the thread id recorded; it cannot yet know what this turn will spend.
6986
+ // The closing write below records the new baseline.
6987
+ usageBaseline: ctx.usageBaseline ?? null
6945
6988
  });
6946
6989
  if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
6947
6990
  sessionEmitted = true;
@@ -7016,7 +7059,8 @@ async function* decodeCodexStream(events, ctx) {
7016
7059
  const sealed = sealHeld2(held, true);
7017
7060
  held = null;
7018
7061
  if (sealed) yield sealed;
7019
- usage = readUsage(ev);
7062
+ cumulativeUsage = readUsage(ev);
7063
+ usage = cumulativeUsage ? codexUsageDelta(cumulativeUsage, ctx.usageBaseline ?? null) : void 0;
7020
7064
  ok = true;
7021
7065
  settled = true;
7022
7066
  break;
@@ -7042,6 +7086,15 @@ async function* decodeCodexStream(events, ctx) {
7042
7086
  if (sealed) yield sealed;
7043
7087
  reason = "no_terminal";
7044
7088
  }
7089
+ if (ok && cumulativeUsage && threadIdForSession) {
7090
+ const closing = encodeSession3({
7091
+ threadId: threadIdForSession,
7092
+ cwd: ctx.cwd ?? "",
7093
+ promptFingerprint: ctx.promptFingerprint ?? null,
7094
+ usageBaseline: cumulativeUsage
7095
+ });
7096
+ yield { type: "session", state: closing };
7097
+ }
7045
7098
  const resolvedConfig = ctx.resolvedReasoningEffort ? { reasoningEffort: ctx.resolvedReasoningEffort } : void 0;
7046
7099
  yield {
7047
7100
  type: "result",
@@ -7340,6 +7393,7 @@ function createCodexAdapter(deps = {}) {
7340
7393
  const decision = decideResume3(req.session, directory);
7341
7394
  const resumeThreadId = "resume" in decision ? decision.resume : null;
7342
7395
  const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
7396
+ const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
7343
7397
  if ("fresh" in decision && decision.reason === "cwd_mismatch") {
7344
7398
  deps.onWarn?.(
7345
7399
  "codex adapter: stored session directory no longer matches the current environment \u2014 starting a fresh thread (SJ527-analogue guard)",
@@ -7399,6 +7453,7 @@ function createCodexAdapter(deps = {}) {
7399
7453
  // was actually sent.
7400
7454
  promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
7401
7455
  storedState: req.session,
7456
+ usageBaseline: threadUsageBaseline,
7402
7457
  // CT601: Codex reports no model in-stream, so record what the thread was
7403
7458
  // started with (null on the default token). Same for the reasoning effort.
7404
7459
  resolvedModel: spec.model,
@@ -7489,9 +7544,9 @@ var OP_FAILURE = {
7489
7544
  message: "files.read returned more than the result ceiling"
7490
7545
  }
7491
7546
  };
7492
- var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT)) => ({
7547
+ var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT), usageBaseline = null) => ({
7493
7548
  type: "session",
7494
- state: encodeSession3({ threadId, cwd, promptFingerprint }),
7549
+ state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
7495
7550
  ...degraded ? { degraded: true } : {}
7496
7551
  });
7497
7552
  var CODEX_CONFORMANCE_FIXTURES = [
@@ -7519,6 +7574,16 @@ var CODEX_CONFORMANCE_FIXTURES = [
7519
7574
  expected: [
7520
7575
  sessionEvent3(NEW_THREAD_ID),
7521
7576
  { type: "text", body: "Hello there.", terminal: true },
7577
+ // CT1645: the closing session write — the thread's ending totals, recorded
7578
+ // as the baseline the NEXT turn subtracts. This is a fresh thread, so the
7579
+ // `result` usage below is the frame verbatim (nothing to subtract yet);
7580
+ // what makes it per-turn is this figure being carried into turn two.
7581
+ sessionEvent3(NEW_THREAD_ID, DIR2, false, fingerprintPrompt(PROMPT), {
7582
+ inputTokens: 100,
7583
+ outputTokens: 50,
7584
+ cacheReadTokens: 20,
7585
+ cacheCreationTokens: 5
7586
+ }),
7522
7587
  {
7523
7588
  type: "result",
7524
7589
  ok: true,
@@ -8244,7 +8309,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
8244
8309
  session: encodeSession3({
8245
8310
  threadId: "th1",
8246
8311
  cwd: DIR2,
8247
- promptFingerprint: fingerprintPrompt(PROMPT)
8312
+ promptFingerprint: fingerprintPrompt(PROMPT),
8313
+ usageBaseline: null
8248
8314
  })
8249
8315
  }),
8250
8316
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -8289,7 +8355,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
8289
8355
  session: encodeSession3({
8290
8356
  threadId: "th1",
8291
8357
  cwd: DIR2,
8292
- promptFingerprint: fingerprintPrompt("an older composed prompt")
8358
+ promptFingerprint: fingerprintPrompt("an older composed prompt"),
8359
+ usageBaseline: null
8293
8360
  })
8294
8361
  }),
8295
8362
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -8314,7 +8381,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
8314
8381
  session: encodeSession3({
8315
8382
  threadId: "old",
8316
8383
  cwd: "/env/gone",
8317
- promptFingerprint: fingerprintPrompt(PROMPT)
8384
+ promptFingerprint: fingerprintPrompt(PROMPT),
8385
+ usageBaseline: null
8318
8386
  })
8319
8387
  }),
8320
8388
  nativeStream: [
@@ -8421,12 +8489,12 @@ async function performAct(acts, act) {
8421
8489
  );
8422
8490
  } catch (err) {
8423
8491
  if (err instanceof ApiError) {
8424
- if (err.status === 409) {
8492
+ if (err.status === 409 && apiErrorCode(err) === "no_active_turn") {
8425
8493
  return toolError(
8426
8494
  "No active turn: this turn has already settled, so the act was not performed."
8427
8495
  );
8428
8496
  }
8429
- return toolError(`The act was refused (${err.status}): ${err.message}`);
8497
+ return toolError(`The act was refused (${err.status}): ${apiErrorMessage(err)}`);
8430
8498
  }
8431
8499
  const message = err instanceof Error ? err.message : String(err);
8432
8500
  return toolError(
@@ -9480,6 +9548,7 @@ var DEFAULT_LEASE_RENEWAL_MS = 3e4;
9480
9548
  function initialOutcome() {
9481
9549
  return {
9482
9550
  sessionWritten: false,
9551
+ lastSessionStateWritten: null,
9483
9552
  sessionDegraded: false,
9484
9553
  sessionWriteRejected: false,
9485
9554
  okResult: false,
@@ -10269,8 +10338,9 @@ var TurnExecution = class {
10269
10338
  if (event.type === "session") {
10270
10339
  o.latestSessionState = event.state;
10271
10340
  if (event.degraded) o.sessionDegraded = true;
10272
- if (!o.sessionWritten) {
10341
+ if (o.lastSessionStateWritten !== event.state) {
10273
10342
  o.sessionWritten = true;
10343
+ o.lastSessionStateWritten = event.state;
10274
10344
  try {
10275
10345
  await this.opts.api.setActiveRun(
10276
10346
  workspaceId,
package/dist/runtime.js CHANGED
@@ -2211,6 +2211,14 @@ function apiErrorCode(err) {
2211
2211
  const body = err.body;
2212
2212
  return typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : null;
2213
2213
  }
2214
+ function apiErrorMessage(err) {
2215
+ const body = err instanceof ApiError ? err.body : void 0;
2216
+ if (body !== null && typeof body === "object") {
2217
+ const message = body.message;
2218
+ if (typeof message === "string" && message.length > 0) return message;
2219
+ }
2220
+ return err instanceof Error ? err.message : String(err);
2221
+ }
2214
2222
  function leaseRefusal(err) {
2215
2223
  const code = apiErrorCode(err);
2216
2224
  if (code && LEASE_REFUSALS.has(code)) return code;
@@ -6252,6 +6260,16 @@ function readUsage(ev) {
6252
6260
  cacheCreationTokens: num(usage.cache_write_input_tokens)
6253
6261
  };
6254
6262
  }
6263
+ function codexUsageDelta(cumulative, baseline) {
6264
+ if (!baseline) return cumulative;
6265
+ const sub = (a, b) => Math.max(0, a - b);
6266
+ return {
6267
+ inputTokens: sub(cumulative.inputTokens, baseline.inputTokens),
6268
+ outputTokens: sub(cumulative.outputTokens, baseline.outputTokens),
6269
+ cacheReadTokens: sub(cumulative.cacheReadTokens, baseline.cacheReadTokens),
6270
+ cacheCreationTokens: sub(cumulative.cacheCreationTokens, baseline.cacheCreationTokens)
6271
+ };
6272
+ }
6255
6273
  function toStatus(v) {
6256
6274
  return v === "completed" || v === "failed" ? v : "in_progress";
6257
6275
  }
@@ -6326,9 +6344,26 @@ function decideResume3(stored, currentCwd) {
6326
6344
  }
6327
6345
  return {
6328
6346
  resume: state.threadId,
6329
- promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null
6347
+ promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
6348
+ usageBaseline: readUsageBaseline(state.usageBaseline)
6330
6349
  };
6331
6350
  }
6351
+ function readUsageBaseline(raw) {
6352
+ if (!raw || typeof raw !== "object") return null;
6353
+ const rec = raw;
6354
+ const read = (key) => {
6355
+ const v = rec[key];
6356
+ return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
6357
+ };
6358
+ const inputTokens = read("inputTokens");
6359
+ const outputTokens = read("outputTokens");
6360
+ const cacheReadTokens = read("cacheReadTokens");
6361
+ const cacheCreationTokens = read("cacheCreationTokens");
6362
+ if (inputTokens === null || outputTokens === null || cacheReadTokens === null || cacheCreationTokens === null) {
6363
+ return null;
6364
+ }
6365
+ return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens };
6366
+ }
6332
6367
 
6333
6368
  // packages/agent-runtime/src/codex/loop.ts
6334
6369
  async function* decodeCodexStream(events, ctx) {
@@ -6340,6 +6375,8 @@ async function* decodeCodexStream(events, ctx) {
6340
6375
  let ok = false;
6341
6376
  let reason;
6342
6377
  let usage;
6378
+ let cumulativeUsage;
6379
+ let threadIdForSession = ctx.resumedThreadId;
6343
6380
  let settled = false;
6344
6381
  function* flushInterim() {
6345
6382
  const sealed = sealHeld2(held, false);
@@ -6350,11 +6387,17 @@ async function* decodeCodexStream(events, ctx) {
6350
6387
  if (ctx.signal.aborted) return;
6351
6388
  if (ev.type === "thread.started") {
6352
6389
  const threadId = readThreadId(ev);
6390
+ if (threadId) threadIdForSession = threadId;
6353
6391
  if (threadId && !sessionEmitted) {
6354
6392
  const state = encodeSession3({
6355
6393
  threadId,
6356
6394
  cwd: ctx.cwd ?? "",
6357
- promptFingerprint: ctx.promptFingerprint ?? null
6395
+ promptFingerprint: ctx.promptFingerprint ?? null,
6396
+ // CT1645: the baseline this turn INHERITED, carried through unchanged.
6397
+ // This early write exists so a turn that dies mid-stream still leaves
6398
+ // the thread id recorded; it cannot yet know what this turn will spend.
6399
+ // The closing write below records the new baseline.
6400
+ usageBaseline: ctx.usageBaseline ?? null
6358
6401
  });
6359
6402
  if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
6360
6403
  sessionEmitted = true;
@@ -6429,7 +6472,8 @@ async function* decodeCodexStream(events, ctx) {
6429
6472
  const sealed = sealHeld2(held, true);
6430
6473
  held = null;
6431
6474
  if (sealed) yield sealed;
6432
- usage = readUsage(ev);
6475
+ cumulativeUsage = readUsage(ev);
6476
+ usage = cumulativeUsage ? codexUsageDelta(cumulativeUsage, ctx.usageBaseline ?? null) : void 0;
6433
6477
  ok = true;
6434
6478
  settled = true;
6435
6479
  break;
@@ -6455,6 +6499,15 @@ async function* decodeCodexStream(events, ctx) {
6455
6499
  if (sealed) yield sealed;
6456
6500
  reason = "no_terminal";
6457
6501
  }
6502
+ if (ok && cumulativeUsage && threadIdForSession) {
6503
+ const closing = encodeSession3({
6504
+ threadId: threadIdForSession,
6505
+ cwd: ctx.cwd ?? "",
6506
+ promptFingerprint: ctx.promptFingerprint ?? null,
6507
+ usageBaseline: cumulativeUsage
6508
+ });
6509
+ yield { type: "session", state: closing };
6510
+ }
6458
6511
  const resolvedConfig = ctx.resolvedReasoningEffort ? { reasoningEffort: ctx.resolvedReasoningEffort } : void 0;
6459
6512
  yield {
6460
6513
  type: "result",
@@ -6753,6 +6806,7 @@ function createCodexAdapter(deps = {}) {
6753
6806
  const decision = decideResume3(req.session, directory);
6754
6807
  const resumeThreadId = "resume" in decision ? decision.resume : null;
6755
6808
  const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
6809
+ const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
6756
6810
  if ("fresh" in decision && decision.reason === "cwd_mismatch") {
6757
6811
  deps.onWarn?.(
6758
6812
  "codex adapter: stored session directory no longer matches the current environment \u2014 starting a fresh thread (SJ527-analogue guard)",
@@ -6812,6 +6866,7 @@ function createCodexAdapter(deps = {}) {
6812
6866
  // was actually sent.
6813
6867
  promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
6814
6868
  storedState: req.session,
6869
+ usageBaseline: threadUsageBaseline,
6815
6870
  // CT601: Codex reports no model in-stream, so record what the thread was
6816
6871
  // started with (null on the default token). Same for the reasoning effort.
6817
6872
  resolvedModel: spec.model,
@@ -6902,9 +6957,9 @@ var OP_FAILURE = {
6902
6957
  message: "files.read returned more than the result ceiling"
6903
6958
  }
6904
6959
  };
6905
- var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT)) => ({
6960
+ var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT), usageBaseline = null) => ({
6906
6961
  type: "session",
6907
- state: encodeSession3({ threadId, cwd, promptFingerprint }),
6962
+ state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
6908
6963
  ...degraded ? { degraded: true } : {}
6909
6964
  });
6910
6965
  var CODEX_CONFORMANCE_FIXTURES = [
@@ -6932,6 +6987,16 @@ var CODEX_CONFORMANCE_FIXTURES = [
6932
6987
  expected: [
6933
6988
  sessionEvent3(NEW_THREAD_ID),
6934
6989
  { type: "text", body: "Hello there.", terminal: true },
6990
+ // CT1645: the closing session write — the thread's ending totals, recorded
6991
+ // as the baseline the NEXT turn subtracts. This is a fresh thread, so the
6992
+ // `result` usage below is the frame verbatim (nothing to subtract yet);
6993
+ // what makes it per-turn is this figure being carried into turn two.
6994
+ sessionEvent3(NEW_THREAD_ID, DIR2, false, fingerprintPrompt(PROMPT), {
6995
+ inputTokens: 100,
6996
+ outputTokens: 50,
6997
+ cacheReadTokens: 20,
6998
+ cacheCreationTokens: 5
6999
+ }),
6935
7000
  {
6936
7001
  type: "result",
6937
7002
  ok: true,
@@ -7657,7 +7722,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
7657
7722
  session: encodeSession3({
7658
7723
  threadId: "th1",
7659
7724
  cwd: DIR2,
7660
- promptFingerprint: fingerprintPrompt(PROMPT)
7725
+ promptFingerprint: fingerprintPrompt(PROMPT),
7726
+ usageBaseline: null
7661
7727
  })
7662
7728
  }),
7663
7729
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -7702,7 +7768,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
7702
7768
  session: encodeSession3({
7703
7769
  threadId: "th1",
7704
7770
  cwd: DIR2,
7705
- promptFingerprint: fingerprintPrompt("an older composed prompt")
7771
+ promptFingerprint: fingerprintPrompt("an older composed prompt"),
7772
+ usageBaseline: null
7706
7773
  })
7707
7774
  }),
7708
7775
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -7727,7 +7794,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
7727
7794
  session: encodeSession3({
7728
7795
  threadId: "old",
7729
7796
  cwd: "/env/gone",
7730
- promptFingerprint: fingerprintPrompt(PROMPT)
7797
+ promptFingerprint: fingerprintPrompt(PROMPT),
7798
+ usageBaseline: null
7731
7799
  })
7732
7800
  }),
7733
7801
  nativeStream: [
@@ -7844,12 +7912,12 @@ async function performAct(acts, act) {
7844
7912
  );
7845
7913
  } catch (err) {
7846
7914
  if (err instanceof ApiError) {
7847
- if (err.status === 409) {
7915
+ if (err.status === 409 && apiErrorCode(err) === "no_active_turn") {
7848
7916
  return toolError(
7849
7917
  "No active turn: this turn has already settled, so the act was not performed."
7850
7918
  );
7851
7919
  }
7852
- return toolError(`The act was refused (${err.status}): ${err.message}`);
7920
+ return toolError(`The act was refused (${err.status}): ${apiErrorMessage(err)}`);
7853
7921
  }
7854
7922
  const message = err instanceof Error ? err.message : String(err);
7855
7923
  return toolError(
@@ -8903,6 +8971,7 @@ var DEFAULT_LEASE_RENEWAL_MS = 3e4;
8903
8971
  function initialOutcome() {
8904
8972
  return {
8905
8973
  sessionWritten: false,
8974
+ lastSessionStateWritten: null,
8906
8975
  sessionDegraded: false,
8907
8976
  sessionWriteRejected: false,
8908
8977
  okResult: false,
@@ -9692,8 +9761,9 @@ var TurnExecution = class {
9692
9761
  if (event.type === "session") {
9693
9762
  o.latestSessionState = event.state;
9694
9763
  if (event.degraded) o.sessionDegraded = true;
9695
- if (!o.sessionWritten) {
9764
+ if (o.lastSessionStateWritten !== event.state) {
9696
9765
  o.sessionWritten = true;
9766
+ o.lastSessionStateWritten = event.state;
9697
9767
  try {
9698
9768
  await this.opts.api.setActiveRun(
9699
9769
  workspaceId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.114",
3
+ "version": "0.6.116",
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",
@@ -29,8 +29,8 @@
29
29
  "prepublishOnly": "pnpm build"
30
30
  },
31
31
  "dependencies": {
32
- "@anthropic-ai/claude-agent-sdk": "0.3.270",
33
- "@openai/codex-sdk": "0.154.0",
32
+ "@anthropic-ai/claude-agent-sdk": "0.3.280",
33
+ "@openai/codex-sdk": "0.155.1",
34
34
  "@hono/node-server": "1.19.14",
35
35
  "@inquirer/prompts": "7.10.1",
36
36
  "commander": "12.1.0",