codeam-cli 2.60.5 → 2.60.7

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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.60.6] — 2026-07-08
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Serialize ACP prompts so a 2nd in-flight turn can't clobber the watchdog
12
+
13
+ ## [2.60.5] — 2026-07-08
14
+
15
+ ### Added
16
+
17
+ - **cli:** Baton support for cursor + gate to agents with a resumable transcript
18
+
19
+ ### Documentation
20
+
21
+ - **cli:** Document the Session Baton (native TUI <-> mobile ACP) architecture + hand-off invariants
22
+
23
+ ### Fixed
24
+
25
+ - **cli:** Serialize baton state POSTs so handback never sticks on "Switching…"
26
+
7
27
  ## [2.60.4] — 2026-07-08
8
28
 
9
29
  ### Fixed
package/dist/index.js CHANGED
@@ -5653,7 +5653,7 @@ function readAnonId() {
5653
5653
  }
5654
5654
  function superProperties() {
5655
5655
  return {
5656
- cliVersion: true ? "2.60.5" : "0.0.0-dev",
5656
+ cliVersion: true ? "2.60.7" : "0.0.0-dev",
5657
5657
  nodeVersion: process.version,
5658
5658
  platform: process.platform,
5659
5659
  arch: process.arch,
@@ -5834,7 +5834,7 @@ var os4 = __toESM(require("os"));
5834
5834
  // package.json
5835
5835
  var package_default = {
5836
5836
  name: "codeam-cli",
5837
- version: "2.60.5",
5837
+ version: "2.60.7",
5838
5838
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
5839
5839
  type: "commonjs",
5840
5840
  main: "dist/index.js",
@@ -6905,7 +6905,7 @@ var CommandRelayService = class {
6905
6905
  // fresh + clear the "CLI update available" banner after a self-update
6906
6906
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
6907
6907
  // pair/reconnect). Older backends ignore the extra field.
6908
- ..."2.60.5" ? { ideVersion: "2.60.5" } : {}
6908
+ ..."2.60.7" ? { ideVersion: "2.60.7" } : {}
6909
6909
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
6910
6910
  }
6911
6911
  /**
@@ -15986,7 +15986,7 @@ async function autoUpgradeBeforeCriticalCommand() {
15986
15986
  if (process.env.NODE_ENV === "test") return;
15987
15987
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
15988
15988
  if (process.env.CI) return;
15989
- const current = true ? "2.60.5" : null;
15989
+ const current = true ? "2.60.7" : null;
15990
15990
  if (!current) return;
15991
15991
  const cache = readCache();
15992
15992
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -16003,7 +16003,7 @@ function checkForUpdates() {
16003
16003
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
16004
16004
  if (process.env.CI) return;
16005
16005
  if (!process.stdout.isTTY) return;
16006
- const current = true ? "2.60.5" : null;
16006
+ const current = true ? "2.60.7" : null;
16007
16007
  if (!current) return;
16008
16008
  const cache = readCache();
16009
16009
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -16023,7 +16023,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
16023
16023
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
16024
16024
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
16025
16025
  function currentCliVersion() {
16026
- return true ? "2.60.5" : null;
16026
+ return true ? "2.60.7" : null;
16027
16027
  }
16028
16028
  function runCmd(cmd, args2, timeoutMs) {
16029
16029
  return new Promise((resolve7) => {
@@ -24850,6 +24850,11 @@ function createIdleTimeout(idleMs, makeError) {
24850
24850
  }
24851
24851
 
24852
24852
  // src/agents/acp/client.ts
24853
+ var TRANSIENT_ADAPTER_SPAWN_RE = /ETXTBSY|ENOENT/;
24854
+ var MAX_START_ATTEMPTS = 5;
24855
+ var _acpStartSeam = {
24856
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms))
24857
+ };
24853
24858
  var PROTOCOL_VERSION2 = 1;
24854
24859
  var FATAL_STARTUP_RE = /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist|Error authenticating|ProjectIdRequiredError/i;
24855
24860
  var NEWSESSION_TIMEOUT_MS = 12e4;
@@ -24880,6 +24885,12 @@ var AcpClient = class {
24880
24885
  * (the agent is demonstrably working, just blocked on the tool), and
24881
24886
  * re-arm only once the last one finishes. Reset per prompt. */
24882
24887
  pendingToolCalls = /* @__PURE__ */ new Set();
24888
+ /** Serializes {@link prompt}: each turn waits for the previous to fully
24889
+ * settle before arming its own watchdog. `promptIdle`/`pendingToolCalls`
24890
+ * are single-instance fields, so a 2nd concurrent prompt() would overwrite
24891
+ * them mid-turn and break turn A's idle watchdog (mobile "send-while-active"
24892
+ * can fire two prompts back-to-back). See {@link prompt}. */
24893
+ promptChain = Promise.resolve();
24883
24894
  /** Last few adapter stderr lines — so a startup failure surfaces the REAL
24884
24895
  * cause (e.g. gemini's `IneligibleTierError`) instead of a bare timeout. */
24885
24896
  recentStderr = [];
@@ -24889,15 +24900,57 @@ var AcpClient = class {
24889
24900
  * so without this the session hangs forever ("loading… / offline"). */
24890
24901
  startupFailureReject = null;
24891
24902
  /**
24892
- * Spawn the adapter + perform the initial handshake (initialize
24893
- * → newSession). Returns the ACP-assigned sessionId so the caller
24894
- * can route subsequent prompts, plus optional model + tier that
24895
- * some adapters (codex-acp today) surface on the newSession
24896
- * response used by the runner to enrich the welcome card
24897
- * subtitle without an extra round-trip.
24903
+ * Spawn the adapter + handshake, retrying a TRANSIENT in-adapter spawn race.
24904
+ *
24905
+ * The adapter itself execs the real agent binary (claude-agent-acp spawns the
24906
+ * ~250MB SDK-bundled `claude` during newSession). On a fresh/waking codespace
24907
+ * that binary is momentarily open-for-write the adapter rejects newSession
24908
+ * with `-32603 spawn ETXTBSY` (or ENOENT before it lands). Both clear within
24909
+ * ms, so we respawn the adapter + re-handshake up to {@link MAX_START_ATTEMPTS}
24910
+ * times. `startOnce` fully cleans up on failure (see cleanupAfterFailedStart),
24911
+ * so each retry starts from a clean slate. Non-transient failures throw on the
24912
+ * first attempt — no masking of real auth/outage errors.
24898
24913
  */
24899
24914
  async start() {
24915
+ let lastErr;
24916
+ for (let attempt = 1; attempt <= MAX_START_ATTEMPTS; attempt += 1) {
24917
+ try {
24918
+ return await this.startOnce();
24919
+ } catch (err) {
24920
+ lastErr = err;
24921
+ if (attempt < MAX_START_ATTEMPTS && this.isTransientAdapterSpawn(err)) {
24922
+ log.info(
24923
+ "acpClient",
24924
+ `adapter hit a transient spawn race (binary busy) \u2014 retrying start ${attempt + 1}/${MAX_START_ATTEMPTS}`
24925
+ );
24926
+ await _acpStartSeam.sleep(400 * attempt);
24927
+ continue;
24928
+ }
24929
+ throw err;
24930
+ }
24931
+ }
24932
+ throw lastErr;
24933
+ }
24934
+ /** True when a failed {@link startOnce} was caused by the adapter's OWN
24935
+ * transient spawn of the agent binary (ETXTBSY/ENOENT). Checks the thrown
24936
+ * error's message/data AND the adapter stderr ring — the adapter logs the
24937
+ * real cause to stderr while returning a generic `-32603 Internal error`. */
24938
+ isTransientAdapterSpawn(err) {
24939
+ const parts = [...this.recentStderr];
24940
+ if (err instanceof Error) parts.push(err.message);
24941
+ if (err && typeof err === "object") {
24942
+ const e = err;
24943
+ try {
24944
+ parts.push(JSON.stringify(e.data));
24945
+ } catch {
24946
+ }
24947
+ if (typeof e.details === "string") parts.push(e.details);
24948
+ }
24949
+ return TRANSIENT_ADAPTER_SPAWN_RE.test(parts.join(" "));
24950
+ }
24951
+ async startOnce() {
24900
24952
  if (this.child) throw new Error("AcpClient already started");
24953
+ this.recentStderr.length = 0;
24901
24954
  const { adapter, cwd } = this.opts;
24902
24955
  const augmentedPath = expandPathForAgentBinaries(process.env.PATH ?? "");
24903
24956
  log.info(
@@ -25034,6 +25087,14 @@ var AcpClient = class {
25034
25087
  * shows a permanent "Thinking…" spinner with no way to recover.
25035
25088
  */
25036
25089
  async prompt(input) {
25090
+ const run = this.promptChain.then(() => this.runPrompt(input));
25091
+ this.promptChain = run.then(
25092
+ () => void 0,
25093
+ () => void 0
25094
+ );
25095
+ return run;
25096
+ }
25097
+ async runPrompt(input) {
25037
25098
  if (!this.connection || !this.sessionId) {
25038
25099
  throw new Error("AcpClient.prompt called before start()");
25039
25100
  }
@@ -32741,7 +32802,7 @@ function checkChokidar() {
32741
32802
  }
32742
32803
  async function doctor(args2 = []) {
32743
32804
  const json = args2.includes("--json");
32744
- const cliVersion = true ? "2.60.5" : "0.0.0-dev";
32805
+ const cliVersion = true ? "2.60.7" : "0.0.0-dev";
32745
32806
  const apiBase2 = resolveApiBaseUrl();
32746
32807
  const diagnosticId = (0, import_node_crypto9.randomUUID)();
32747
32808
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -32940,7 +33001,7 @@ async function completion(args2) {
32940
33001
  // src/commands/version.ts
32941
33002
  var import_picocolors15 = __toESM(require("picocolors"));
32942
33003
  function version2() {
32943
- const v = true ? "2.60.5" : "unknown";
33004
+ const v = true ? "2.60.7" : "unknown";
32944
33005
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
32945
33006
  }
32946
33007
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.60.5",
3
+ "version": "2.60.7",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",