codeam-cli 2.60.19 → 2.60.21

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/index.js +216 -41
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ 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.20] — 2026-07-09
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Baton — pre-mint Cursor session id via `create-chat`
12
+ - **cli:** Baton Take Control for Cursor — bridge native↔ACP session stores
13
+
14
+ ## [2.60.19] — 2026-07-09
15
+
16
+ ### Fixed
17
+
18
+ - **cli:** Baton — pre-mint Cursor session id via `create-chat`
19
+
20
+ ### Tests
21
+
22
+ - **cli:** De-flake adapter module-graph gate — crash synchronously in settling fixture
23
+
24
+ ## [2.60.18] — 2026-07-09
25
+
26
+ ### Added
27
+
28
+ - **cli:** Auto-install Kimi on local pair when the binary is missing
29
+
30
+ ### Fixed
31
+
32
+ - **cli:** Baton — discover Kimi's self-minted session id after spawn
33
+
7
34
  ## [2.60.17] — 2026-07-09
8
35
 
9
36
  ### Added
package/dist/index.js CHANGED
@@ -5680,7 +5680,7 @@ function readAnonId() {
5680
5680
  }
5681
5681
  function superProperties() {
5682
5682
  return {
5683
- cliVersion: true ? "2.60.19" : "0.0.0-dev",
5683
+ cliVersion: true ? "2.60.21" : "0.0.0-dev",
5684
5684
  nodeVersion: process.version,
5685
5685
  platform: process.platform,
5686
5686
  arch: process.arch,
@@ -5861,7 +5861,7 @@ var os4 = __toESM(require("os"));
5861
5861
  // package.json
5862
5862
  var package_default = {
5863
5863
  name: "codeam-cli",
5864
- version: "2.60.19",
5864
+ version: "2.60.21",
5865
5865
  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.",
5866
5866
  type: "commonjs",
5867
5867
  main: "dist/index.js",
@@ -6932,7 +6932,7 @@ var CommandRelayService = class {
6932
6932
  // fresh + clear the "CLI update available" banner after a self-update
6933
6933
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
6934
6934
  // pair/reconnect). Older backends ignore the extra field.
6935
- ..."2.60.19" ? { ideVersion: "2.60.19" } : {}
6935
+ ..."2.60.21" ? { ideVersion: "2.60.21" } : {}
6936
6936
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
6937
6937
  }
6938
6938
  /**
@@ -9089,7 +9089,7 @@ function findGitRoot2(startDir) {
9089
9089
  }
9090
9090
 
9091
9091
  // src/commands/link.ts
9092
- var import_node_crypto7 = require("crypto");
9092
+ var import_node_crypto8 = require("crypto");
9093
9093
  var fs30 = __toESM(require("fs"));
9094
9094
  var path34 = __toESM(require("path"));
9095
9095
  var import_chokidar = __toESM(require("chokidar"));
@@ -12931,6 +12931,87 @@ function resolveHistoryFile2(cwd, sessionId, homeOverride) {
12931
12931
  }
12932
12932
  return null;
12933
12933
  }
12934
+ async function discoverSessionId(cwd, opts, homeOverride) {
12935
+ const home = homeOverride ?? import_node_os.default.homedir();
12936
+ const sessionsRoot = import_node_path2.default.join(home, ".codex", "sessions");
12937
+ const floor = opts.sinceMs - 2e3;
12938
+ const deadline = Date.now() + (opts.timeoutMs ?? 24e4);
12939
+ let resolvedCurrent;
12940
+ try {
12941
+ resolvedCurrent = import_node_fs3.default.realpathSync(cwd);
12942
+ } catch {
12943
+ resolvedCurrent = import_node_path2.default.resolve(cwd);
12944
+ }
12945
+ for (; ; ) {
12946
+ const id = newestRolloutIdSince(sessionsRoot, resolvedCurrent, floor);
12947
+ if (id) return id;
12948
+ if (Date.now() >= deadline) return null;
12949
+ await sleep2(500);
12950
+ }
12951
+ }
12952
+ function newestRolloutIdSince(sessionsRoot, resolvedCwd, floorMs) {
12953
+ if (!import_node_fs3.default.existsSync(sessionsRoot)) return null;
12954
+ const now = /* @__PURE__ */ new Date();
12955
+ let bestId = null;
12956
+ let bestMtime = -1;
12957
+ for (let dayOffset = 0; dayOffset < 2; dayOffset += 1) {
12958
+ const d3 = new Date(now.getTime() - dayOffset * 24 * 60 * 60 * 1e3);
12959
+ const yyyy = String(d3.getUTCFullYear());
12960
+ const mm = String(d3.getUTCMonth() + 1).padStart(2, "0");
12961
+ const dd = String(d3.getUTCDate()).padStart(2, "0");
12962
+ const dayDir = import_node_path2.default.join(sessionsRoot, yyyy, mm, dd);
12963
+ let dayFiles;
12964
+ try {
12965
+ dayFiles = import_node_fs3.default.readdirSync(dayDir, { withFileTypes: true });
12966
+ } catch {
12967
+ continue;
12968
+ }
12969
+ for (const entry of dayFiles) {
12970
+ if (!entry.isFile()) continue;
12971
+ if (!entry.name.startsWith("rollout-") || !entry.name.endsWith(".jsonl")) continue;
12972
+ const filePath = import_node_path2.default.join(dayDir, entry.name);
12973
+ let mtime;
12974
+ try {
12975
+ mtime = import_node_fs3.default.statSync(filePath).mtimeMs;
12976
+ } catch {
12977
+ continue;
12978
+ }
12979
+ if (mtime < floorMs || mtime <= bestMtime) continue;
12980
+ let metaCwd;
12981
+ let metaId;
12982
+ try {
12983
+ const raw = import_node_fs3.default.readFileSync(filePath, "utf8");
12984
+ for (const line of raw.split("\n")) {
12985
+ if (!line.trim()) continue;
12986
+ const rec = parseLine(line);
12987
+ if (!rec) continue;
12988
+ if (rec.type === "session_meta") {
12989
+ const meta = rec.payload;
12990
+ metaCwd = typeof meta?.cwd === "string" ? meta.cwd : void 0;
12991
+ metaId = typeof meta?.id === "string" ? meta.id : void 0;
12992
+ }
12993
+ break;
12994
+ }
12995
+ } catch {
12996
+ continue;
12997
+ }
12998
+ if (!metaId || !metaCwd) continue;
12999
+ let resolvedMeta;
13000
+ try {
13001
+ resolvedMeta = import_node_fs3.default.realpathSync(metaCwd);
13002
+ } catch {
13003
+ resolvedMeta = import_node_path2.default.resolve(metaCwd);
13004
+ }
13005
+ if (resolvedMeta !== resolvedCwd) continue;
13006
+ bestMtime = mtime;
13007
+ bestId = metaId;
13008
+ }
13009
+ }
13010
+ return bestId;
13011
+ }
13012
+ function sleep2(ms) {
13013
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
13014
+ }
12934
13015
  function getCurrentUsage2(historyDir) {
12935
13016
  if (!import_node_fs3.default.existsSync(historyDir)) return null;
12936
13017
  const files = import_node_fs3.default.readdirSync(historyDir).filter((f) => f.startsWith("rollout-") && f.endsWith(".jsonl")).map((f) => ({ name: f, full: import_node_path2.default.join(historyDir, f) })).map((e) => ({ ...e, mtime: import_node_fs3.default.statSync(e.full).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
@@ -13098,6 +13179,16 @@ var CodexRuntimeStrategy = class {
13098
13179
  resolveHistoryFile(cwd, sessionId) {
13099
13180
  return resolveHistoryFile2(cwd, sessionId);
13100
13181
  }
13182
+ /**
13183
+ * Codex mints its own session id (into a `rollout-*.jsonl` at TUI boot) and
13184
+ * neither accepts a pre-set one nor prints it — so, like Kimi, the baton's
13185
+ * NativeTuiDriver discovers it post-spawn. Codex shares that rollout store with
13186
+ * its ACP adapter, so discovery alone is enough (no cross-store bridge). The
13187
+ * TUI boots slowly, hence the generous poll budget inside.
13188
+ */
13189
+ discoverSessionId(cwd, opts) {
13190
+ return discoverSessionId(cwd, opts);
13191
+ }
13101
13192
  getCurrentUsage(historyDir) {
13102
13193
  return getCurrentUsage2(historyDir);
13103
13194
  }
@@ -13444,7 +13535,70 @@ var CoderabbitRuntimeStrategy = class {
13444
13535
  var fs23 = __toESM(require("fs"));
13445
13536
  var os21 = __toESM(require("os"));
13446
13537
  var path27 = __toESM(require("path"));
13538
+ var import_node_crypto5 = require("crypto");
13447
13539
  var HISTORY_ROOT = path27.join(os21.homedir(), ".cursor", "projects");
13540
+ var CURSOR_HOME = path27.join(os21.homedir(), ".cursor");
13541
+ var STORE_FILES = ["store.db", "store.db-wal", "store.db-shm"];
13542
+ function acpSessionDir(sessionId) {
13543
+ return path27.join(CURSOR_HOME, "acp-sessions", sessionId);
13544
+ }
13545
+ function nativeStoreDir(cwd, sessionId) {
13546
+ const chatsRoot = path27.join(CURSOR_HOME, "chats");
13547
+ let buckets = [];
13548
+ try {
13549
+ buckets = fs23.readdirSync(chatsRoot);
13550
+ } catch {
13551
+ }
13552
+ for (const b of buckets) {
13553
+ const candidate = path27.join(chatsRoot, b, sessionId);
13554
+ if (fs23.existsSync(path27.join(candidate, "store.db"))) return candidate;
13555
+ }
13556
+ const computed = path27.join(chatsRoot, (0, import_node_crypto5.createHash)("md5").update(cwd).digest("hex"), sessionId);
13557
+ return fs23.existsSync(computed) ? computed : null;
13558
+ }
13559
+ function copyStoreFiles(srcDir, dstDir) {
13560
+ fs23.mkdirSync(dstDir, { recursive: true });
13561
+ for (const f of STORE_FILES) {
13562
+ const dst = path27.join(dstDir, f);
13563
+ if (fs23.existsSync(dst)) fs23.rmSync(dst, { force: true });
13564
+ }
13565
+ for (const f of STORE_FILES) {
13566
+ const src = path27.join(srcDir, f);
13567
+ if (fs23.existsSync(src)) fs23.copyFileSync(src, path27.join(dstDir, f));
13568
+ }
13569
+ }
13570
+ function bridgeNativeToAcp(cwd, sessionId) {
13571
+ try {
13572
+ const src = nativeStoreDir(cwd, sessionId);
13573
+ if (!src) {
13574
+ log.warn("cursor", `baton bridge: no native store for ${sessionId.slice(0, 8)} \u2014 skip`);
13575
+ return;
13576
+ }
13577
+ const dst = acpSessionDir(sessionId);
13578
+ copyStoreFiles(src, dst);
13579
+ fs23.writeFileSync(
13580
+ path27.join(dst, "meta.json"),
13581
+ JSON.stringify({ schemaVersion: 1, cwd })
13582
+ );
13583
+ log.info("cursor", `baton bridge native\u2192acp ok (${sessionId.slice(0, 8)})`);
13584
+ } catch (err) {
13585
+ log.warn("cursor", `baton bridge native\u2192acp failed: ${err instanceof Error ? err.message : String(err)}`);
13586
+ }
13587
+ }
13588
+ function bridgeAcpToNative(cwd, sessionId) {
13589
+ try {
13590
+ const src = acpSessionDir(sessionId);
13591
+ if (!fs23.existsSync(path27.join(src, "store.db"))) {
13592
+ log.warn("cursor", `baton bridge: no acp store for ${sessionId.slice(0, 8)} \u2014 skip`);
13593
+ return;
13594
+ }
13595
+ const dst = nativeStoreDir(cwd, sessionId) ?? path27.join(CURSOR_HOME, "chats", (0, import_node_crypto5.createHash)("md5").update(cwd).digest("hex"), sessionId);
13596
+ copyStoreFiles(src, dst);
13597
+ log.info("cursor", `baton bridge acp\u2192native ok (${sessionId.slice(0, 8)})`);
13598
+ } catch (err) {
13599
+ log.warn("cursor", `baton bridge acp\u2192native failed: ${err instanceof Error ? err.message : String(err)}`);
13600
+ }
13601
+ }
13448
13602
  function encodeCursorCwd(cwd) {
13449
13603
  return cwd.replace(/^[/\\]+/, "").replace(/[/\\:]/g, "-");
13450
13604
  }
@@ -13685,6 +13839,25 @@ var CursorRuntimeStrategy = class {
13685
13839
  parseHistoryFile(filePath) {
13686
13840
  return parseHistoryFile3(filePath);
13687
13841
  }
13842
+ /**
13843
+ * Baton Take Control (native TUI → mobile ACP). Cursor keeps native-TUI
13844
+ * conversations in `~/.cursor/chats/<md5(cwd)>/<id>` but ACP `session/load`
13845
+ * only reads `~/.cursor/acp-sessions/<id>` — so without this, loading the
13846
+ * native session id fails "not found". Bridge the native store into the ACP
13847
+ * store just before the load. Best-effort; verified live (identical
13848
+ * `blobs`+`meta` SQLite schema, raw file copy replays the conversation).
13849
+ */
13850
+ async syncTranscriptForAcpResume(cwd, sessionId) {
13851
+ bridgeNativeToAcp(cwd, sessionId);
13852
+ }
13853
+ /**
13854
+ * Baton hand-back (mobile ACP → native TUI). Copy the ACP conversation store
13855
+ * back into the native `~/.cursor/chats` store so `cursor-agent --resume <id>`
13856
+ * in the terminal picks up whatever mobile did. Best-effort.
13857
+ */
13858
+ async syncTranscriptForNativeResume(cwd, sessionId) {
13859
+ bridgeAcpToNative(cwd, sessionId);
13860
+ }
13688
13861
  getCurrentUsage(historyDir) {
13689
13862
  return getCurrentUsage3(historyDir);
13690
13863
  }
@@ -13957,7 +14130,7 @@ var AiderRuntimeStrategy = class {
13957
14130
  };
13958
14131
 
13959
14132
  // src/agents/gemini/runtime.ts
13960
- var import_node_crypto5 = require("crypto");
14133
+ var import_node_crypto6 = require("crypto");
13961
14134
 
13962
14135
  // src/agents/gemini/link.ts
13963
14136
  var import_node_child_process12 = require("child_process");
@@ -14222,7 +14395,7 @@ var GeminiRuntimeStrategy = class {
14222
14395
  "Gemini CLI is not on PATH. Install it with:\n npm install -g @google/gemini-cli\n Then run `codeam pair` again."
14223
14396
  );
14224
14397
  }
14225
- const sessionId = (0, import_node_crypto5.randomUUID)();
14398
+ const sessionId = (0, import_node_crypto6.randomUUID)();
14226
14399
  const launch = this.os.buildLaunch(binary, ["--session-id", sessionId]);
14227
14400
  log.info(
14228
14401
  "gemini",
@@ -14329,12 +14502,12 @@ var import_node_path4 = require("path");
14329
14502
  var fs29 = __toESM(require("fs"));
14330
14503
  var os26 = __toESM(require("os"));
14331
14504
  var path33 = __toESM(require("path"));
14332
- var import_node_crypto6 = require("crypto");
14505
+ var import_node_crypto7 = require("crypto");
14333
14506
  function kimiHome() {
14334
14507
  return process.env.KIMI_CODE_HOME || path33.join(os26.homedir(), ".kimi-code");
14335
14508
  }
14336
14509
  function workDirKey(cwd) {
14337
- const hash = (0, import_node_crypto6.createHash)("sha256").update(cwd).digest("hex").slice(0, 12);
14510
+ const hash = (0, import_node_crypto7.createHash)("sha256").update(cwd).digest("hex").slice(0, 12);
14338
14511
  return `wd_${path33.basename(cwd)}_${hash}`;
14339
14512
  }
14340
14513
  function resolveHistoryDir6(cwd) {
@@ -14391,7 +14564,7 @@ function scanBucketsForSession(sessionId) {
14391
14564
  }
14392
14565
  return null;
14393
14566
  }
14394
- async function discoverSessionId(cwd, opts) {
14567
+ async function discoverSessionId2(cwd, opts) {
14395
14568
  const bucket = path33.join(kimiHome(), "sessions", workDirKey(cwd));
14396
14569
  const floor = opts.sinceMs - 2e3;
14397
14570
  const deadline = Date.now() + (opts.timeoutMs ?? 15e3);
@@ -14399,7 +14572,7 @@ async function discoverSessionId(cwd, opts) {
14399
14572
  const id = newestSessionSince(bucket, floor);
14400
14573
  if (id) return id;
14401
14574
  if (Date.now() >= deadline) return null;
14402
- await sleep2(250);
14575
+ await sleep3(250);
14403
14576
  }
14404
14577
  }
14405
14578
  function newestSessionSince(bucket, floorMs) {
@@ -14426,7 +14599,7 @@ function newestSessionSince(bucket, floorMs) {
14426
14599
  }
14427
14600
  return bestId;
14428
14601
  }
14429
- function sleep2(ms) {
14602
+ function sleep3(ms) {
14430
14603
  return new Promise((resolve7) => setTimeout(resolve7, ms));
14431
14604
  }
14432
14605
  function joinTextParts(parts) {
@@ -14549,7 +14722,7 @@ var KimiRuntimeStrategy = class {
14549
14722
  * have no baton), so the driver's optional call is inert for them.
14550
14723
  */
14551
14724
  discoverSessionId(cwd, opts) {
14552
- return discoverSessionId(cwd, opts);
14725
+ return discoverSessionId2(cwd, opts);
14553
14726
  }
14554
14727
  getCurrentUsage(historyDir) {
14555
14728
  return getCurrentUsage5(historyDir);
@@ -14727,9 +14900,9 @@ async function link(args2 = []) {
14727
14900
  await linkDryRunPreflight(ctx);
14728
14901
  return;
14729
14902
  }
14730
- const pluginId = (0, import_node_crypto7.randomUUID)();
14731
- const pollSecret = (0, import_node_crypto7.randomBytes)(32).toString("base64url");
14732
- const pluginSecretHash = (0, import_node_crypto7.createHash)("sha256").update(pollSecret).digest("hex");
14903
+ const pluginId = (0, import_node_crypto8.randomUUID)();
14904
+ const pollSecret = (0, import_node_crypto8.randomBytes)(32).toString("base64url");
14905
+ const pluginSecretHash = (0, import_node_crypto8.createHash)("sha256").update(pollSecret).digest("hex");
14733
14906
  const spin = dist_exports.spinner();
14734
14907
  spin.start("Requesting pairing code...");
14735
14908
  const pairing = await requestCode(pluginId, pluginSecretHash);
@@ -16560,7 +16733,7 @@ async function autoUpgradeBeforeCriticalCommand() {
16560
16733
  if (process.env.NODE_ENV === "test") return;
16561
16734
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
16562
16735
  if (process.env.CI) return;
16563
- const current = true ? "2.60.19" : null;
16736
+ const current = true ? "2.60.21" : null;
16564
16737
  if (!current) return;
16565
16738
  const cache = readCache();
16566
16739
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -16577,7 +16750,7 @@ function checkForUpdates() {
16577
16750
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
16578
16751
  if (process.env.CI) return;
16579
16752
  if (!process.stdout.isTTY) return;
16580
- const current = true ? "2.60.19" : null;
16753
+ const current = true ? "2.60.21" : null;
16581
16754
  if (!current) return;
16582
16755
  const cache = readCache();
16583
16756
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -16597,7 +16770,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
16597
16770
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
16598
16771
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
16599
16772
  function currentCliVersion() {
16600
- return true ? "2.60.19" : null;
16773
+ return true ? "2.60.21" : null;
16601
16774
  }
16602
16775
  function runCmd(cmd, args2, timeoutMs) {
16603
16776
  return new Promise((resolve7) => {
@@ -22129,7 +22302,7 @@ async function waitForClaudeNativeBinary(opts = {}) {
22129
22302
  const timeoutMs = opts.timeoutMs ?? 18e4;
22130
22303
  const pollMs = opts.pollMs ?? 500;
22131
22304
  const now = opts.now ?? Date.now;
22132
- const sleep4 = opts.sleep ?? realSleep;
22305
+ const sleep5 = opts.sleep ?? realSleep;
22133
22306
  const deps = {
22134
22307
  sdkDir: opts.sdkDir,
22135
22308
  platformKey: opts.platformKey,
@@ -22139,7 +22312,7 @@ async function waitForClaudeNativeBinary(opts = {}) {
22139
22312
  let found = resolveClaudeNativeBinary(deps);
22140
22313
  if (found) return found;
22141
22314
  while (now() < deadline) {
22142
- await sleep4(pollMs);
22315
+ await sleep5(pollMs);
22143
22316
  found = resolveClaudeNativeBinary(deps);
22144
22317
  if (found) return found;
22145
22318
  }
@@ -22161,13 +22334,13 @@ async function waitForCommandOnPath(cmd, opts = {}) {
22161
22334
  const timeoutMs = opts.timeoutMs ?? 18e4;
22162
22335
  const pollMs = opts.pollMs ?? 500;
22163
22336
  const now = opts.now ?? Date.now;
22164
- const sleep4 = opts.sleep ?? realSleep;
22337
+ const sleep5 = opts.sleep ?? realSleep;
22165
22338
  const probe = opts.probe;
22166
22339
  const check = () => isCommandOnPath(cmd, probe);
22167
22340
  const deadline = now() + timeoutMs;
22168
22341
  if (check()) return true;
22169
22342
  while (now() < deadline) {
22170
- await sleep4(pollMs);
22343
+ await sleep5(pollMs);
22171
22344
  if (check()) return true;
22172
22345
  }
22173
22346
  return check();
@@ -22190,12 +22363,12 @@ async function waitForCursorAgent(opts = {}) {
22190
22363
  const timeoutMs = opts.timeoutMs ?? 18e4;
22191
22364
  const pollMs = opts.pollMs ?? 500;
22192
22365
  const now = opts.now ?? Date.now;
22193
- const sleep4 = opts.sleep ?? realSleep;
22366
+ const sleep5 = opts.sleep ?? realSleep;
22194
22367
  const check = () => resolveCursorAgentBinary(opts) !== null || isCommandOnPath("cursor-agent", opts.probe);
22195
22368
  const deadline = now() + timeoutMs;
22196
22369
  if (check()) return true;
22197
22370
  while (now() < deadline) {
22198
- await sleep4(pollMs);
22371
+ await sleep5(pollMs);
22199
22372
  if (check()) return true;
22200
22373
  }
22201
22374
  return check();
@@ -22244,12 +22417,12 @@ async function waitForAdapterModuleGraph(command2, args2, opts = {}) {
22244
22417
  const timeoutMs = opts.timeoutMs ?? 18e4;
22245
22418
  const pollMs = opts.pollMs ?? 500;
22246
22419
  const now = opts.now ?? Date.now;
22247
- const sleep4 = opts.sleep ?? realSleep;
22420
+ const sleep5 = opts.sleep ?? realSleep;
22248
22421
  const probeOpts = { livenessMs: opts.livenessMs, spawnFn: opts.spawnFn };
22249
22422
  const deadline = now() + timeoutMs;
22250
22423
  if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
22251
22424
  while (now() < deadline) {
22252
- await sleep4(pollMs);
22425
+ await sleep5(pollMs);
22253
22426
  if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
22254
22427
  }
22255
22428
  return false;
@@ -22431,7 +22604,7 @@ function requiresAcp(agent) {
22431
22604
  }
22432
22605
 
22433
22606
  // src/agents/acp/runner.ts
22434
- var import_node_crypto9 = require("crypto");
22607
+ var import_node_crypto10 = require("crypto");
22435
22608
 
22436
22609
  // src/services/history.service.ts
22437
22610
  var fs55 = __toESM(require("fs"));
@@ -26632,7 +26805,7 @@ async function runOnboardingTurn(opts) {
26632
26805
  }
26633
26806
 
26634
26807
  // src/agents/acp/mappers.ts
26635
- var import_node_crypto8 = require("crypto");
26808
+ var import_node_crypto9 = require("crypto");
26636
26809
  function mapSessionUpdate(notification) {
26637
26810
  const update = notification.update;
26638
26811
  switch (update.sessionUpdate) {
@@ -26695,7 +26868,7 @@ function mapPermissionRequest(request) {
26695
26868
  }
26696
26869
  return {
26697
26870
  event: {
26698
- questionId: (0, import_node_crypto8.randomUUID)(),
26871
+ questionId: (0, import_node_crypto9.randomUUID)(),
26699
26872
  prompt,
26700
26873
  options: labels.length > 0 ? labels : void 0
26701
26874
  },
@@ -26705,7 +26878,7 @@ function mapPermissionRequest(request) {
26705
26878
  }
26706
26879
  function messageChunkId(messageId) {
26707
26880
  if (typeof messageId === "string" && messageId.length > 0) return messageId;
26708
- return (0, import_node_crypto8.randomUUID)();
26881
+ return (0, import_node_crypto9.randomUUID)();
26709
26882
  }
26710
26883
  function extractText4(content) {
26711
26884
  if (!content || typeof content !== "object") return null;
@@ -28518,7 +28691,7 @@ var AcpHistory = class {
28518
28691
  this.summary = trimmed.length > 120 ? trimmed.slice(0, 117) + "\u2026" : trimmed;
28519
28692
  }
28520
28693
  this.messages.push({
28521
- id: (0, import_node_crypto9.randomUUID)(),
28694
+ id: (0, import_node_crypto10.randomUUID)(),
28522
28695
  role: "user",
28523
28696
  text,
28524
28697
  timestamp: Date.now()
@@ -28527,7 +28700,7 @@ var AcpHistory = class {
28527
28700
  appendAgentReply(text) {
28528
28701
  if (text.length === 0) return;
28529
28702
  this.messages.push({
28530
- id: (0, import_node_crypto9.randomUUID)(),
28703
+ id: (0, import_node_crypto10.randomUUID)(),
28531
28704
  role: "agent",
28532
28705
  text,
28533
28706
  timestamp: Date.now()
@@ -28733,7 +28906,7 @@ async function runAcpSession(opts) {
28733
28906
  currentIndex: 0,
28734
28907
  done: true
28735
28908
  }),
28736
- publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto9.randomUUID)(), prompt, options }),
28909
+ publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto10.randomUUID)(), prompt, options }),
28737
28910
  publishRawChunk: (chunk) => publisher.publishOutput(chunk),
28738
28911
  sendResult: (commandId, status2, result) => relay.sendResult(commandId, status2, result),
28739
28912
  appendAgentReply: (text) => history.appendAgentReply(text),
@@ -29857,12 +30030,12 @@ var StreamingEmitterService = class {
29857
30030
  log.warn("streamingEmitter", `post error url=${url} attempt=${attempt + 1}`, err);
29858
30031
  }
29859
30032
  if (attempt < MAX_RETRIES2) {
29860
- await sleep3(RETRY_BACKOFF_MS3 * (attempt + 1));
30033
+ await sleep4(RETRY_BACKOFF_MS3 * (attempt + 1));
29861
30034
  }
29862
30035
  }
29863
30036
  }
29864
30037
  };
29865
- function sleep3(ms) {
30038
+ function sleep4(ms) {
29866
30039
  return new Promise((r) => setTimeout(r, ms));
29867
30040
  }
29868
30041
  var TREE_CONTINUATION_RE = /^\s*└/;
@@ -30041,6 +30214,7 @@ var NativeTuiDriver = class {
30041
30214
  keepAliveCtx;
30042
30215
  async start(resumeId) {
30043
30216
  if (resumeId !== void 0) {
30217
+ await this.deps.runtime.syncTranscriptForNativeResume?.(this.deps.opts.cwd, resumeId);
30044
30218
  await this.agent.restart(resumeId, false);
30045
30219
  return resumeId;
30046
30220
  }
@@ -30099,7 +30273,7 @@ var NativeTuiDriver = class {
30099
30273
  };
30100
30274
 
30101
30275
  // src/baton/acp-driver.ts
30102
- var import_node_crypto10 = require("crypto");
30276
+ var import_node_crypto11 = require("crypto");
30103
30277
  var AcpDriver = class {
30104
30278
  constructor(deps) {
30105
30279
  this.deps = deps;
@@ -30121,6 +30295,7 @@ var AcpDriver = class {
30121
30295
  try {
30122
30296
  started = await this.deps.client.start();
30123
30297
  if (resumeId !== void 0) {
30298
+ await this.deps.runtime.syncTranscriptForAcpResume?.(this.deps.opts.cwd, resumeId);
30124
30299
  this.deps.streaming.beginLoadReplay();
30125
30300
  try {
30126
30301
  await this.deps.client.loadSession(resumeId);
@@ -30192,7 +30367,7 @@ var AcpDriver = class {
30192
30367
  currentIndex: 0,
30193
30368
  done: true
30194
30369
  }),
30195
- publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto10.randomUUID)(), prompt, options }),
30370
+ publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto11.randomUUID)(), prompt, options }),
30196
30371
  publishRawChunk: (chunk) => publisher.publishOutput(chunk),
30197
30372
  sendResult: (commandId, status2, result) => relay.sendResult(commandId, status2, result),
30198
30373
  appendAgentReply: (text) => history.appendAgentReply(text),
@@ -33287,7 +33462,7 @@ async function invite() {
33287
33462
  // src/commands/doctor.ts
33288
33463
  var import_node_dns = require("dns");
33289
33464
  var import_node_util5 = require("util");
33290
- var import_node_crypto11 = require("crypto");
33465
+ var import_node_crypto12 = require("crypto");
33291
33466
  var fs62 = __toESM(require("fs"));
33292
33467
  var path69 = __toESM(require("path"));
33293
33468
  var import_picocolors14 = __toESM(require("picocolors"));
@@ -33457,9 +33632,9 @@ function checkChokidar() {
33457
33632
  }
33458
33633
  async function doctor(args2 = []) {
33459
33634
  const json = args2.includes("--json");
33460
- const cliVersion = true ? "2.60.19" : "0.0.0-dev";
33635
+ const cliVersion = true ? "2.60.21" : "0.0.0-dev";
33461
33636
  const apiBase2 = resolveApiBaseUrl();
33462
- const diagnosticId = (0, import_node_crypto11.randomUUID)();
33637
+ const diagnosticId = (0, import_node_crypto12.randomUUID)();
33463
33638
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
33464
33639
  const [dns, health] = await Promise.all([
33465
33640
  checkDns(apiBase2),
@@ -33656,7 +33831,7 @@ async function completion(args2) {
33656
33831
  // src/commands/version.ts
33657
33832
  var import_picocolors15 = __toESM(require("picocolors"));
33658
33833
  function version2() {
33659
- const v = true ? "2.60.19" : "unknown";
33834
+ const v = true ? "2.60.21" : "unknown";
33660
33835
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
33661
33836
  }
33662
33837
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.60.19",
3
+ "version": "2.60.21",
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",