codeam-cli 2.76.15 → 2.76.16

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 +11 -0
  2. package/dist/index.js +119 -30
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ 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.76.15] — 2026-10-03
8
+
9
+ ### Documentation
10
+
11
+ - CLI release lag — batch releases; in prod only after Box image + prebuild (#902)
12
+
13
+ ### Fixed
14
+
15
+ - **cli:** Self-update logs every outcome, never restarts onto its own folder, canonical links (#905)
16
+ - **cli:** Fleet ops on per-container lanes — a slow migrate no longer swallows a scratch create (#906)
17
+
7
18
  ## [2.76.14] — 2026-10-02
8
19
 
9
20
  ### Fixed
package/dist/index.js CHANGED
@@ -10579,7 +10579,7 @@ function readAnonId() {
10579
10579
  }
10580
10580
  function superProperties() {
10581
10581
  return {
10582
- cliVersion: true ? "2.76.15" : "0.0.0-dev",
10582
+ cliVersion: true ? "2.76.16" : "0.0.0-dev",
10583
10583
  nodeVersion: process.version,
10584
10584
  platform: process.platform,
10585
10585
  arch: process.arch,
@@ -10753,6 +10753,10 @@ var os4 = __toESM(require("os"));
10753
10753
  var path4 = __toESM(require("path"));
10754
10754
  var import_child_process2 = require("child_process");
10755
10755
  var WRAPPER_REPO_NAME = "codeam-codespace";
10756
+ function isWrapperCheckout(dir) {
10757
+ const resolved = path4.posix.normalize(dir.replace(/\\/g, "/")).replace(/\/+$/, "");
10758
+ return resolved === `/workspaces/${WRAPPER_REPO_NAME}`;
10759
+ }
10756
10760
  function resolveSessionHostname(cwd = process.cwd()) {
10757
10761
  if (process.env.CODESPACES !== "true") {
10758
10762
  const label = process.env.CODEAM_HOST_LABEL?.trim();
@@ -10824,7 +10828,7 @@ var http = __toESM(require("http"));
10824
10828
  // package.json
10825
10829
  var package_default = {
10826
10830
  name: "codeam-cli",
10827
- version: "2.76.15",
10831
+ version: "2.76.16",
10828
10832
  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.",
10829
10833
  type: "commonjs",
10830
10834
  main: "dist/index.js",
@@ -12160,7 +12164,7 @@ var CommandRelayService = class _CommandRelayService {
12160
12164
  // fresh + clear the "CLI update available" banner after a self-update
12161
12165
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12162
12166
  // pair/reconnect). Older backends ignore the extra field.
12163
- ..."2.76.15" ? { ideVersion: "2.76.15" } : {}
12167
+ ..."2.76.16" ? { ideVersion: "2.76.16" } : {}
12164
12168
  };
12165
12169
  try {
12166
12170
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -19176,7 +19180,13 @@ function codexLoginLauncher() {
19176
19180
  };
19177
19181
  }
19178
19182
 
19183
+ // src/agents/codex/state-runtime-race.ts
19184
+ var CODEX_STATE_RUNTIME_RACE_RE = /failed to initialize (sqlite )?state runtime/i;
19185
+
19179
19186
  // src/agents/codex/runtime.ts
19187
+ var _codexOneShotSeam = {
19188
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms))
19189
+ };
19180
19190
  var CODEX_CONTEXT_WINDOW = 272e3;
19181
19191
  var CODEX_MODELS = [
19182
19192
  { id: "gpt-5.5", label: "GPT-5.5", contextWindow: CODEX_CONTEXT_WINDOW },
@@ -19295,12 +19305,22 @@ var CodexRuntimeStrategy = class {
19295
19305
  const binary = this.os.findInPath("codex");
19296
19306
  if (!binary) return null;
19297
19307
  const launch = this.os.buildLaunch(binary, ["exec", prompt]);
19298
- return spawnAndCapture(launch.cmd, launch.args, {
19299
- onStderr: opts?.onStderr,
19300
- onFailedOutput: opts?.onFailedOutput,
19301
- cwd: opts?.cwd,
19302
- timeoutMs: opts?.timeoutMs
19303
- });
19308
+ const runOnce = () => {
19309
+ let stderr = "";
19310
+ return spawnAndCapture(launch.cmd, launch.args, {
19311
+ onStderr: (chunk) => {
19312
+ stderr += chunk;
19313
+ opts?.onStderr?.(chunk);
19314
+ },
19315
+ onFailedOutput: opts?.onFailedOutput,
19316
+ cwd: opts?.cwd,
19317
+ timeoutMs: opts?.timeoutMs
19318
+ }).then((text) => ({ text, stderr }));
19319
+ };
19320
+ const first = await runOnce();
19321
+ if (first.text !== null || !CODEX_STATE_RUNTIME_RACE_RE.test(first.stderr)) return first.text;
19322
+ await _codexOneShotSeam.sleep(1e3);
19323
+ return (await runOnce()).text;
19304
19324
  }
19305
19325
  };
19306
19326
  function resolveNpm(os74) {
@@ -27200,7 +27220,7 @@ var BdAdapter = class {
27200
27220
  * exit codes — note bd exits 0 even for some error states (e.g. "no beads
27201
27221
  * database found"), so JSON-parsing callers must also inspect the payload.
27202
27222
  */
27203
- async run(args2) {
27223
+ async run(args2, runOpts = {}) {
27204
27224
  const binary = this.resolveBinary();
27205
27225
  if (!binary) {
27206
27226
  return { code: -1, stdout: "", stderr: "bd binary not resolved" };
@@ -27215,16 +27235,14 @@ var BdAdapter = class {
27215
27235
  }
27216
27236
  env.BEADS_DOLT_SHARED_SERVER = "1";
27217
27237
  delete env.BEADS_DIR;
27218
- log.trace(
27219
- "beads",
27220
- `bd ${args2.join(" ")} (cwd=${this.opts.cwd ?? process.cwd()}, shared-server)`
27221
- );
27222
- let res = await _spawnSeam.run(binary, args2, { cwd: this.opts.cwd, env });
27238
+ const cwd = runOpts.cwd ?? this.opts.cwd;
27239
+ log.trace("beads", `bd ${args2.join(" ")} (cwd=${cwd ?? process.cwd()}, shared-server)`);
27240
+ let res = await _spawnSeam.run(binary, args2, { cwd, env });
27223
27241
  const TRANSIENT_SPAWN = /ENOENT|ETXTBSY/;
27224
27242
  for (let attempt = 1; attempt <= 5 && res.code === -1 && TRANSIENT_SPAWN.test(res.stderr); attempt++) {
27225
27243
  log.info("beads", `bd ${args2[0]} transient spawn failure (binary busy) \u2014 retry ${attempt}/5`);
27226
27244
  await _adapterSeam.sleep(750 * attempt);
27227
- res = await _spawnSeam.run(binary, args2, { cwd: this.opts.cwd, env });
27245
+ res = await _spawnSeam.run(binary, args2, { cwd, env });
27228
27246
  }
27229
27247
  return res;
27230
27248
  }
@@ -27667,7 +27685,15 @@ var _provisionSeam = {
27667
27685
  /** Silence bd's `beads.role not configured` warning. */
27668
27686
  setGitBeadsRole,
27669
27687
  /** Does the project's git remote carry Dolt data (`refs/dolt/data`)? */
27670
- remoteHasDoltData
27688
+ remoteHasDoltData,
27689
+ /** Throwaway cwd for a user-level-only `bd setup` (see GLOBAL_ONLY_RECIPES). */
27690
+ makeSetupCwd: () => fs59.mkdtempSync(path67.join(os41.tmpdir(), "codeam-bd-setup-")),
27691
+ removeSetupCwd: (dir) => {
27692
+ try {
27693
+ fs59.rmSync(dir, { recursive: true, force: true });
27694
+ } catch {
27695
+ }
27696
+ }
27671
27697
  };
27672
27698
  function remoteHasDoltData(cwd) {
27673
27699
  try {
@@ -27944,15 +27970,17 @@ async function projectDbReachable(bd) {
27944
27970
  async function setupAgents(bd, agents) {
27945
27971
  const wired = [];
27946
27972
  for (const recipe of dedupeRecipes(agents)) {
27973
+ const scratchCwd = GLOBAL_ONLY_RECIPES.has(recipe) ? _provisionSeam.makeSetupCwd() : null;
27974
+ const runOpts = scratchCwd ? { cwd: scratchCwd } : {};
27947
27975
  try {
27948
- const check = await bd.run(["setup", recipe, "--global", "--check"]);
27976
+ const check = await bd.run(["setup", recipe, "--global", "--check"], runOpts);
27949
27977
  if (check.code === 0) {
27950
27978
  log.trace("beads", `bd setup ${recipe} --global already installed \u2014 skipping`);
27951
27979
  wired.push(recipe);
27952
27980
  continue;
27953
27981
  }
27954
27982
  log.info("beads", `wiring agent natively: bd setup ${recipe} --global`);
27955
- const setup = await bd.run(["setup", recipe, "--global"]);
27983
+ const setup = await bd.run(["setup", recipe, "--global"], runOpts);
27956
27984
  if (setup.code === 0) {
27957
27985
  wired.push(recipe);
27958
27986
  } else {
@@ -27963,10 +27991,13 @@ async function setupAgents(bd, agents) {
27963
27991
  }
27964
27992
  } catch (err) {
27965
27993
  log.warn("beads", `bd setup ${recipe} --global threw (non-fatal)`, err);
27994
+ } finally {
27995
+ if (scratchCwd) _provisionSeam.removeSetupCwd(scratchCwd);
27966
27996
  }
27967
27997
  }
27968
27998
  return wired;
27969
27999
  }
28000
+ var GLOBAL_ONLY_RECIPES = /* @__PURE__ */ new Set(["claude", "codex", "gemini"]);
27970
28001
  function dedupeRecipes(agents) {
27971
28002
  const seen2 = /* @__PURE__ */ new Set();
27972
28003
  for (const agent of agents) {
@@ -29372,7 +29403,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
29372
29403
  if (!nodeModulesDir) return 0;
29373
29404
  const readdirSync18 = deps?.readdirSync ?? fs65.readdirSync;
29374
29405
  const statSync17 = deps?.statSync ?? fs65.statSync;
29375
- const rmSync14 = deps?.rmSync ?? fs65.rmSync;
29406
+ const rmSync15 = deps?.rmSync ?? fs65.rmSync;
29376
29407
  let removed = 0;
29377
29408
  let entries2;
29378
29409
  try {
@@ -29386,7 +29417,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
29386
29417
  try {
29387
29418
  const st3 = statSync17(full);
29388
29419
  if (now - st3.mtimeMs < STALE_STAGING_AGE_MS) continue;
29389
- rmSync14(full, { recursive: true, force: true });
29420
+ rmSync15(full, { recursive: true, force: true });
29390
29421
  removed++;
29391
29422
  } catch {
29392
29423
  }
@@ -36637,7 +36668,10 @@ var AcpClient = class {
36637
36668
  const joined = parts.join(" ");
36638
36669
  return TRANSIENT_ADAPTER_SPAWN_RE.test(joined) || ADAPTER_MODULE_LOAD_ERROR_RE.test(joined) || // The transport dying mid-handshake = the adapter crashed starting up,
36639
36670
  // even when the child-exit handler lost the race (see the RE's doc).
36640
- TRANSPORT_DOWN_DURING_START_RE.test(joined);
36671
+ TRANSPORT_DOWN_DURING_START_RE.test(joined) || // codex-acp stays up but its `codex app-server` child lost the first-run
36672
+ // ~/.codex SQLite migration race to another codex process; a respawn
36673
+ // opens the DBs the winner migrated (see state-runtime-race.ts).
36674
+ CODEX_STATE_RUNTIME_RACE_RE.test(joined);
36641
36675
  }
36642
36676
  async startOnce() {
36643
36677
  if (this.child) throw new Error("AcpClient already started");
@@ -42270,6 +42304,28 @@ function buildAcpPromptBlocks(payload) {
42270
42304
  return blocks;
42271
42305
  }
42272
42306
 
42307
+ // src/agents/acp/reply-language.ts
42308
+ var hinted = /* @__PURE__ */ new Set();
42309
+ function replyLanguageName(locale) {
42310
+ if (typeof locale !== "string" || locale.trim().length === 0) return null;
42311
+ try {
42312
+ const language = new Intl.Locale(locale.trim().replace(/_/g, "-")).language;
42313
+ if (!language || language === "en" || language === "und") return null;
42314
+ const name = new Intl.DisplayNames(["en"], { type: "language" }).of(language);
42315
+ return name && name.toLowerCase() !== language ? name : null;
42316
+ } catch {
42317
+ return null;
42318
+ }
42319
+ }
42320
+ function replyLanguageContextOnce(locale, sessionId, agent) {
42321
+ const language = replyLanguageName(locale);
42322
+ if (!language) return null;
42323
+ const key = `${sessionId}:${agent}`;
42324
+ if (hinted.has(key)) return null;
42325
+ hinted.add(key);
42326
+ return `The user's device language is ${language}. Reply in ${language} unless the user writes in another language.`;
42327
+ }
42328
+
42273
42329
  // src/agents/acp/promptEcho.ts
42274
42330
  var MAX_PROMPT_CHARS = 200;
42275
42331
  var MAX_AGENT_REPLY_CHARS = 280;
@@ -42300,7 +42356,20 @@ function formatAgentReplyLine(text) {
42300
42356
  function assembleAcpCommandContext(session, cmd) {
42301
42357
  return { ...session, cmd };
42302
42358
  }
42303
- var ACP_QUICK_REPLIES = ["Continue", "Yes, go ahead", "Explain"];
42359
+ function quickRepliesForTurn(replyText, stopReason) {
42360
+ if (stopReason === "max_tokens" || stopReason === "max_turn_requests") return ["Continue"];
42361
+ return endsWithYesNoQuestion(replyText) ? ["Yes, go ahead", "No"] : [];
42362
+ }
42363
+ var YES_NO_LEAD_RE = /^(?:so|ok(?:ay)?|great|alright|then|now)?[,\s]*(?:shall|should|do|does|did|would|will|can|could|may|is|are|was|want|ready|sound|sounds|ok(?:ay)?|proceed|go ahead)\b/i;
42364
+ function endsWithYesNoQuestion(replyText) {
42365
+ const text = replyText.replace(/[*_`]/g, "").trimEnd();
42366
+ if (!text.endsWith("?")) return false;
42367
+ const body = text.slice(0, -1);
42368
+ const start2 = Math.max(body.lastIndexOf("\n"), body.lastIndexOf(". "), body.lastIndexOf("! "), body.lastIndexOf("? "));
42369
+ const question = body.slice(start2 + 1).trim();
42370
+ if (/\bor\b/i.test(question)) return false;
42371
+ return YES_NO_LEAD_RE.test(question);
42372
+ }
42304
42373
  async function recoverFromFailedTurn(client3, streaming) {
42305
42374
  await cancelStuckTurn(client3);
42306
42375
  await streaming.closeAll();
@@ -42600,6 +42669,8 @@ async function startTaskH(ctx) {
42600
42669
  ctx.history.appendUserPrompt(promptText);
42601
42670
  maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
42602
42671
  const squadContext = collectSquadContext(ctx);
42672
+ const languageContext = replyLanguageContextOnce(payload?.locale, opts.sessionId, opts.agent);
42673
+ if (languageContext) squadContext.push(languageContext);
42603
42674
  applySquadContext(blocks, squadContext, squadContextMode(ctx));
42604
42675
  ctx.squad?.resetHops();
42605
42676
  let turnBlocks = blocks;
@@ -42728,7 +42799,7 @@ async function startTaskH(ctx) {
42728
42799
  }
42729
42800
  void publisher.publishOutput({
42730
42801
  type: "input_suggestion",
42731
- content: ACP_QUICK_REPLIES,
42802
+ content: quickRepliesForTurn(cleanText, reply.stopReason),
42732
42803
  done: true
42733
42804
  });
42734
42805
  log.info(
@@ -50389,7 +50460,7 @@ async function autoUpgradeBeforeCriticalCommand() {
50389
50460
  if (process.env.NODE_ENV === "test") return;
50390
50461
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
50391
50462
  if (process.env.CI) return;
50392
- const current2 = true ? "2.76.15" : null;
50463
+ const current2 = true ? "2.76.16" : null;
50393
50464
  if (!current2) return;
50394
50465
  const cache = readCache();
50395
50466
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -50406,7 +50477,7 @@ function checkForUpdates() {
50406
50477
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
50407
50478
  if (process.env.CI) return;
50408
50479
  if (!process.stdout.isTTY) return;
50409
- const current2 = true ? "2.76.15" : null;
50480
+ const current2 = true ? "2.76.16" : null;
50410
50481
  if (!current2) return;
50411
50482
  const cache = readCache();
50412
50483
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -50688,7 +50759,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
50688
50759
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
50689
50760
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
50690
50761
  function currentCliVersion() {
50691
- return true ? "2.76.15" : null;
50762
+ return true ? "2.76.16" : null;
50692
50763
  }
50693
50764
  async function installedVersion(deps) {
50694
50765
  const ls = await deps.run(
@@ -50899,7 +50970,7 @@ function signalHostReady(send = process.send?.bind(process)) {
50899
50970
  }
50900
50971
  function defaultDeps() {
50901
50972
  return {
50902
- ownVersion: true ? "2.76.15" : null,
50973
+ ownVersion: true ? "2.76.16" : null,
50903
50974
  root: cliVersionsRoot(),
50904
50975
  args: process.argv.slice(3),
50905
50976
  execPath: process.execPath,
@@ -52592,6 +52663,13 @@ var HostAgentSupervisor = class {
52592
52663
  );
52593
52664
  return null;
52594
52665
  }
52666
+ if (isWrapperCheckout(cwd)) {
52667
+ log.warn(
52668
+ "host-agent",
52669
+ `resume: skipping session ${session.id.slice(0, 8)} \u2014 it has no saved workspace and the fallback is the codespace wrapper checkout (${cwd}), not the user's repo`
52670
+ );
52671
+ return null;
52672
+ }
52595
52673
  const deployId = deployIdFromWorkspace(session.cwd) ?? session.id;
52596
52674
  return { deployId, cwd, session };
52597
52675
  }
@@ -52719,6 +52797,17 @@ var HostAgentSupervisor = class {
52719
52797
  const st3 = this.resumeStateFor(target);
52720
52798
  try {
52721
52799
  const cwd = fs97.existsSync(target.cwd) ? target.cwd : process.cwd();
52800
+ if (isWrapperCheckout(cwd)) {
52801
+ log.warn(
52802
+ "host-agent",
52803
+ `resume: skipping session ${session.id.slice(0, 8)} deploy=${deployId.slice(0, 8)} \u2014 resolved cwd ${cwd} is the codespace wrapper checkout, not the user's repo`
52804
+ );
52805
+ return;
52806
+ }
52807
+ log.info(
52808
+ "host-agent",
52809
+ `resume: session ${session.id.slice(0, 8)} deploy=${deployId.slice(0, 8)} cwd=${cwd}`
52810
+ );
52722
52811
  const proc = this.resumeSpawner(
52723
52812
  { ...readHouseProxyChildEnv(deployId), CODEAM_RESUME_SESSION_ID: session.id },
52724
52813
  cwd
@@ -53278,7 +53367,7 @@ function checkChokidar() {
53278
53367
  }
53279
53368
  async function doctor(args2 = []) {
53280
53369
  const json = args2.includes("--json");
53281
- const cliVersion = true ? "2.76.15" : "0.0.0-dev";
53370
+ const cliVersion = true ? "2.76.16" : "0.0.0-dev";
53282
53371
  const apiBase2 = resolveApiBaseUrl();
53283
53372
  const diagnosticId = (0, import_node_crypto17.randomUUID)();
53284
53373
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -53802,7 +53891,7 @@ async function mcpWarm(argv = [], opts = {}) {
53802
53891
  // src/commands/version.ts
53803
53892
  var import_picocolors15 = __toESM(require("picocolors"));
53804
53893
  function version2() {
53805
- const v = true ? "2.76.15" : "unknown";
53894
+ const v = true ? "2.76.16" : "unknown";
53806
53895
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
53807
53896
  }
53808
53897
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.76.15",
3
+ "version": "2.76.16",
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",