codeam-cli 2.76.28 → 2.76.30

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,34 @@ 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.29] — 2026-10-13
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Run a queued message only after the running turn closes
12
+
13
+ ## [2.76.28] — 2026-10-13
14
+
15
+ ### Added
16
+
17
+ - **cli:** Let a fleet host run another registry tag of the Box image
18
+
19
+ ### CI
20
+
21
+ - **release:** Publish to npm next and promote to latest only after the journey canary
22
+
23
+ ## [2.76.27] — 2026-10-13
24
+
25
+ ### Fixed
26
+
27
+ - **cli:** No baton state before the local agent starts (04jp)
28
+
29
+ ## [2.76.26] — 2026-10-13
30
+
31
+ ### Fixed
32
+
33
+ - **both-plugins:** Report which agents can take a prompt from the phone
34
+
7
35
  ## [2.76.25] — 2026-10-12
8
36
 
9
37
  ### CI
package/dist/index.js CHANGED
@@ -10627,7 +10627,7 @@ function readAnonId() {
10627
10627
  }
10628
10628
  function superProperties() {
10629
10629
  return {
10630
- cliVersion: true ? "2.76.28" : "0.0.0-dev",
10630
+ cliVersion: true ? "2.76.30" : "0.0.0-dev",
10631
10631
  nodeVersion: process.version,
10632
10632
  platform: process.platform,
10633
10633
  arch: process.arch,
@@ -10876,7 +10876,7 @@ var http = __toESM(require("http"));
10876
10876
  // package.json
10877
10877
  var package_default = {
10878
10878
  name: "codeam-cli",
10879
- version: "2.76.28",
10879
+ version: "2.76.30",
10880
10880
  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.",
10881
10881
  type: "commonjs",
10882
10882
  main: "dist/index.js",
@@ -11064,7 +11064,9 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret, onUs
11064
11064
  const data = result?.data;
11065
11065
  if (!data?.paired) return null;
11066
11066
  if (onUserId && typeof data.userId === "string" && data.userId.length > 0) {
11067
- onUserId(data.userId);
11067
+ const user = data.user;
11068
+ const plan = typeof user === "object" && user !== null && "plan" in user && typeof user.plan === "string" ? user.plan : void 0;
11069
+ onUserId(data.userId, plan);
11068
11070
  }
11069
11071
  const token = data.pluginAuthToken;
11070
11072
  return typeof token === "string" && token.length > 0 ? token : null;
@@ -12239,7 +12241,7 @@ var CommandRelayService = class _CommandRelayService {
12239
12241
  // fresh + clear the "CLI update available" banner after a self-update
12240
12242
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12241
12243
  // pair/reconnect). Older backends ignore the extra field.
12242
- ..."2.76.28" ? { ideVersion: "2.76.28" } : {}
12244
+ ..."2.76.30" ? { ideVersion: "2.76.30" } : {}
12243
12245
  };
12244
12246
  try {
12245
12247
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body, this.pollSecretHeader());
@@ -25991,6 +25993,8 @@ var AUTH_FAILURE_MESSAGE = (
25991
25993
  var CURSOR_UPGRADE_MESSAGE = "\u26A1 **Cursor needs a paid plan to run the agent.**\n\nThe headless Cursor Agent requires Cursor **Pro** \u2014 your Free plan\u2019s included usage does NOT cover Agent runs, even with quota left. This is your Cursor account (not CodeAgent). Upgrade, then send your message again:\n\n[Upgrade to Cursor Pro \u2192](https://cursor.com/dashboard)";
25992
25994
  var ONE_M_CREDITS_MESSAGE = "\u{1F504} **Reconnect your Claude subscription to continue.**\n\nClaude requested 1M-context but your account doesn\u2019t have the usage credits for it on this credential. Reconnecting refreshes your subscription so the agent can keep going \u2014 disabling 1M context won\u2019t fix a credits gate.\n\nTap [Reconnect this agent](codeam://reauth) to reconnect your Claude subscription in Profile \u203A Agents, then send your message again.";
25993
25995
  var TURN_FAILURE_MESSAGE = "\u26A0\uFE0F **The agent hit an error and couldn\u2019t finish this turn.** Please send your message again.";
25996
+ var IDLE_TURN_FAILURE_MESSAGE = "\u26A0\uFE0F **The agent stopped responding, so this turn was ended.** Your message wasn\u2019t answered \u2014 send it again.";
25997
+ var IDLE_TURN_RE = /ACP prompt idle/;
25994
25998
  function emptyReplyMessage(agent) {
25995
25999
  const label = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].displayName : agent;
25996
26000
  const binary = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].binaryName : agent;
@@ -26066,7 +26070,9 @@ ${opts.recentStderr}`);
26066
26070
  if (looksLikeProviderOutage(opts.detail) || looksLikeProviderOutage(opts.recentStderr)) {
26067
26071
  return providerOutageMessage(opts.agent, opts.railWireId);
26068
26072
  }
26069
- if (!opts.hadText) return TURN_FAILURE_MESSAGE;
26073
+ if (!opts.hadText) {
26074
+ return IDLE_TURN_RE.test(opts.detail) ? IDLE_TURN_FAILURE_MESSAGE : TURN_FAILURE_MESSAGE;
26075
+ }
26070
26076
  return null;
26071
26077
  }
26072
26078
  var WINDOWS_CONTROL_C_EXIT = 3221225786;
@@ -30337,6 +30343,7 @@ function prewarmPreviewDetection(runtime) {
30337
30343
  try {
30338
30344
  const cwd = process.cwd();
30339
30345
  if (await readPreviewConfig(cwd)) return;
30346
+ if (isEmptyScratchProject(cwd)) return;
30340
30347
  const { raw } = await runDetectOneShot(generateOneShot);
30341
30348
  const detection = safeParseDetection(raw);
30342
30349
  if (!detection) {
@@ -36993,6 +37000,7 @@ var AGENT_ENV_WITHHELD = ["PREVIEW_TUNNEL_TOKEN", "PREVIEW_TUNNEL_HOSTNAME"];
36993
37000
  var LOADSESSION_TIMEOUT_MS = 6e4;
36994
37001
  var PROMPT_IDLE_TIMEOUT_MS = 9e4;
36995
37002
  var PROMPT_ACTIVE_IDLE_TIMEOUT_MS = 6e5;
37003
+ var ABANDONED_TURN_SETTLE_MS = 1e4;
36996
37004
  var CLIENT_CAPABILITIES = {
36997
37005
  fs: { readTextFile: true, writeTextFile: true },
36998
37006
  terminal: false
@@ -37397,15 +37405,20 @@ var AcpClient = class {
37397
37405
  `prompt \u2192 session=${this.sessionId.slice(0, 8)} textChars=${textLen} imageBlocks=${imageCount}`
37398
37406
  );
37399
37407
  const t0 = Date.now();
37408
+ const sessionId = this.sessionId;
37400
37409
  const send = this.connection.prompt({
37401
- sessionId: this.sessionId,
37410
+ sessionId,
37402
37411
  prompt: blocks
37403
37412
  });
37413
+ let idleTripped = false;
37404
37414
  const idle = createIdleTimeout(
37405
37415
  PROMPT_IDLE_TIMEOUT_MS,
37406
- () => new Error(
37407
- `ACP prompt idle \u2014 adapter sent no updates for the idle window (${PROMPT_IDLE_TIMEOUT_MS / 1e3}s silent-from-start / ${PROMPT_ACTIVE_IDLE_TIMEOUT_MS / 1e3}s once active). Likely the underlying agent's auth or network is misconfigured; check the adapter stderr lines above (acpAdapter tag) for the actual error.`
37408
- ),
37416
+ () => {
37417
+ idleTripped = true;
37418
+ return new Error(
37419
+ `ACP prompt idle \u2014 adapter sent no updates for the idle window (${PROMPT_IDLE_TIMEOUT_MS / 1e3}s silent-from-start / ${PROMPT_ACTIVE_IDLE_TIMEOUT_MS / 1e3}s once active). Likely the underlying agent's auth or network is misconfigured; check the adapter stderr lines above (acpAdapter tag) for the actual error.`
37420
+ );
37421
+ },
37409
37422
  PROMPT_ACTIVE_IDLE_TIMEOUT_MS
37410
37423
  );
37411
37424
  this.promptIdle = idle;
@@ -37425,12 +37438,41 @@ var AcpClient = class {
37425
37438
  "acpClient",
37426
37439
  `prompt \u2190 failed elapsedMs=${Date.now() - t0} err=${err instanceof Error ? err.message : String(err)}`
37427
37440
  );
37441
+ if (idleTripped) await this.cancelAbandonedTurn(sessionId, send);
37428
37442
  throw err;
37429
37443
  } finally {
37430
37444
  idle.clear();
37431
37445
  this.promptIdle = null;
37432
37446
  }
37433
37447
  }
37448
+ /**
37449
+ * Cancel a turn the idle watchdog abandoned and wait, bounded, for the
37450
+ * adapter to answer its `session/prompt` — the proof the turn is over before
37451
+ * the next prompt is sent. Best-effort: a dead adapter can't hold the queue
37452
+ * longer than {@link ABANDONED_TURN_SETTLE_MS}.
37453
+ */
37454
+ async cancelAbandonedTurn(sessionId, send) {
37455
+ try {
37456
+ await this.connection?.cancel({ sessionId });
37457
+ } catch (err) {
37458
+ const msg = err instanceof Error ? err.message : String(err);
37459
+ log.warn("acpClient", `cancel of abandoned turn failed: ${msg}`);
37460
+ return;
37461
+ }
37462
+ let timer;
37463
+ const settled = await Promise.race([
37464
+ send.then(
37465
+ () => true,
37466
+ () => true
37467
+ ),
37468
+ new Promise((resolve13) => {
37469
+ timer = setTimeout(() => resolve13(false), ABANDONED_TURN_SETTLE_MS);
37470
+ timer.unref?.();
37471
+ })
37472
+ ]);
37473
+ clearTimeout(timer);
37474
+ log.info("acpClient", `abandoned turn cancelled settled=${settled}`);
37475
+ }
37434
37476
  /**
37435
37477
  * True when a `session/prompt` rejection is the "the agent closed its ACP
37436
37478
  * session between turns" fact — the kimi post-turn/idle close. Keyed on the
@@ -40626,6 +40668,27 @@ async function postCredentialSync(opts, fetchImpl = fetch) {
40626
40668
  }
40627
40669
  }
40628
40670
 
40671
+ // src/agents/acp/turn-queue.ts
40672
+ var TurnQueue = class {
40673
+ tail = Promise.resolve();
40674
+ active = 0;
40675
+ /** True while a turn runs or waits to run. */
40676
+ get busy() {
40677
+ return this.active > 0;
40678
+ }
40679
+ run(turn) {
40680
+ this.active += 1;
40681
+ const result = this.tail.then(turn).finally(() => {
40682
+ this.active -= 1;
40683
+ });
40684
+ this.tail = result.then(
40685
+ () => void 0,
40686
+ () => void 0
40687
+ );
40688
+ return result;
40689
+ }
40690
+ };
40691
+
40629
40692
  // src/services/active-conversation-marker.ts
40630
40693
  var import_node_crypto13 = require("crypto");
40631
40694
  var import_promises6 = __toESM(require("fs/promises"));
@@ -43188,7 +43251,7 @@ function reportTurn2(ctx, phase, errorCode) {
43188
43251
  });
43189
43252
  }
43190
43253
  async function startTaskH(ctx) {
43191
- const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr } = ctx;
43254
+ const { cmd, relay, streaming, opts } = ctx;
43192
43255
  const payload = cmd.payload;
43193
43256
  reportTurn2(ctx, "received");
43194
43257
  const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
@@ -43230,6 +43293,13 @@ async function startTaskH(ctx) {
43230
43293
  if (echoLine.length > 0) {
43231
43294
  showInfo(echoLine);
43232
43295
  }
43296
+ if (streaming.turns.busy) {
43297
+ log.info("acpRunner", `start_task queued behind the running turn id=${cmd.id.slice(0, 8)}`);
43298
+ }
43299
+ await streaming.turns.run(() => runStartTaskTurn(ctx, payload, blocks, promptText));
43300
+ }
43301
+ async function runStartTaskTurn(ctx, payload, blocks, promptText) {
43302
+ const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr } = ctx;
43233
43303
  await streaming.beginTurn();
43234
43304
  ctx.history.appendUserPrompt(promptText);
43235
43305
  maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
@@ -44123,6 +44193,8 @@ var StreamingState = class {
44123
44193
  * kinds that key purely off the adapter id.
44124
44194
  */
44125
44195
  turnSeq = 0;
44196
+ /** Runs this session's user turns one at a time (see {@link TurnQueue}). */
44197
+ turns = new TurnQueue();
44126
44198
  chatTextPublishIntervalMs;
44127
44199
  /**
44128
44200
  * Register a permission Promise. The Promise stays pending until
@@ -47723,12 +47795,14 @@ async function start(requestedAgent, presetSession) {
47723
47795
  `);
47724
47796
  const cwd = process.cwd();
47725
47797
  let reconnectUserId;
47798
+ let reconnectPlan;
47726
47799
  const refreshed = await fetchCurrentPluginAuthToken(
47727
47800
  session.id,
47728
47801
  pluginId,
47729
47802
  session.pollSecret,
47730
- (userId) => {
47803
+ (userId, plan) => {
47731
47804
  reconnectUserId = userId;
47805
+ reconnectPlan = plan;
47732
47806
  }
47733
47807
  );
47734
47808
  if (refreshed && refreshed !== session.pluginAuthToken) {
@@ -47738,8 +47812,11 @@ async function start(requestedAgent, presetSession) {
47738
47812
  } else if (refreshed) {
47739
47813
  showInfo("Reconnected previous session.");
47740
47814
  }
47741
- if (reconnectUserId && reconnectUserId !== session.userId) {
47742
- session.userId = reconnectUserId;
47815
+ const staleUserId = !!reconnectUserId && reconnectUserId !== session.userId;
47816
+ const stalePlan = !!reconnectPlan && reconnectPlan !== session.plan;
47817
+ if (staleUserId || stalePlan) {
47818
+ if (reconnectUserId) session.userId = reconnectUserId;
47819
+ if (reconnectPlan) session.plan = reconnectPlan;
47743
47820
  addSession({ ...session });
47744
47821
  }
47745
47822
  if (session.userId) {
@@ -51344,7 +51421,7 @@ async function autoUpgradeBeforeCriticalCommand() {
51344
51421
  if (process.env.NODE_ENV === "test") return;
51345
51422
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51346
51423
  if (process.env.CI) return;
51347
- const current2 = true ? "2.76.28" : null;
51424
+ const current2 = true ? "2.76.30" : null;
51348
51425
  if (!current2) return;
51349
51426
  const cache = readCache();
51350
51427
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51361,7 +51438,7 @@ function checkForUpdates() {
51361
51438
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
51362
51439
  if (process.env.CI) return;
51363
51440
  if (!process.stdout.isTTY) return;
51364
- const current2 = true ? "2.76.28" : null;
51441
+ const current2 = true ? "2.76.30" : null;
51365
51442
  if (!current2) return;
51366
51443
  const cache = readCache();
51367
51444
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -51644,7 +51721,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
51644
51721
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
51645
51722
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
51646
51723
  function currentCliVersion() {
51647
- return true ? "2.76.28" : null;
51724
+ return true ? "2.76.30" : null;
51648
51725
  }
51649
51726
  async function installedVersion(deps) {
51650
51727
  const ls = await deps.run(
@@ -51855,7 +51932,7 @@ function signalHostReady(send = process.send?.bind(process)) {
51855
51932
  }
51856
51933
  function defaultDeps() {
51857
51934
  return {
51858
- ownVersion: true ? "2.76.28" : null,
51935
+ ownVersion: true ? "2.76.30" : null,
51859
51936
  root: cliVersionsRoot(),
51860
51937
  args: process.argv.slice(3),
51861
51938
  execPath: process.execPath,
@@ -54393,7 +54470,7 @@ function checkChokidar() {
54393
54470
  }
54394
54471
  async function doctor(args2 = []) {
54395
54472
  const json = args2.includes("--json");
54396
- const cliVersion = true ? "2.76.28" : "0.0.0-dev";
54473
+ const cliVersion = true ? "2.76.30" : "0.0.0-dev";
54397
54474
  const apiBase2 = resolveApiBaseUrl();
54398
54475
  const diagnosticId = (0, import_node_crypto17.randomUUID)();
54399
54476
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -54929,7 +55006,7 @@ async function mcpWarm(argv = [], opts = {}) {
54929
55006
  // src/commands/version.ts
54930
55007
  var import_picocolors15 = __toESM(require("picocolors"));
54931
55008
  function version2() {
54932
- const v = true ? "2.76.28" : "unknown";
55009
+ const v = true ? "2.76.30" : "unknown";
54933
55010
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
54934
55011
  }
54935
55012
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.76.28",
3
+ "version": "2.76.30",
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",