codeam-cli 2.74.13 → 2.74.14

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,36 @@ 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.74.13] — 2026-09-10
8
+
9
+ ### Added
10
+
11
+ - **cli:** Open the command stream on the stream host with a one-time api-host fallback (#745)
12
+
13
+ ### Build
14
+
15
+ - **deps:** Bump gradle-wrapper in /apps/jetbrains-plugin (#661)
16
+ - **deps:** Bump com.squareup.okhttp3:okhttp in /apps/jetbrains-plugin (#641)
17
+ - **deps:** Bump actions/setup-java from 5 to 6 (#662)
18
+
19
+ ### CI
20
+
21
+ - **workflow:** Root the npm Dependabot config at the workspace, so its PRs can actually install (#737)
22
+
23
+ ### Chore
24
+
25
+ - **deps:** Bump org.jetbrains.kotlin.jvm in /apps/jetbrains-plugin (#743)
26
+ - **deps:** Bump docker/setup-buildx-action from 3 to 4 (#414)
27
+ - **deps:** Bump actions/checkout from 4 to 7 (#366)
28
+ - **deps:** Bump ws from 8.21.0 to 8.21.3 (#740)
29
+ - **deps:** Bump posthog-node from 5.35.1 to 5.51.6 (#741)
30
+ - **deps-dev:** Bump knip from 6.25.0 to 6.34.0 (#742)
31
+
32
+ ### Fixed
33
+
34
+ - **cli:** Local baton sessions go silent on mobile after the mirror's 10-min startup wait (#744)
35
+ - **vsc-plugin:** Stop advertising a Copilot agent the plugin cannot dispatch to (#746)
36
+
7
37
  ## [2.74.12] — 2026-09-09
8
38
 
9
39
  ### Fixed
package/dist/index.js CHANGED
@@ -10199,7 +10199,7 @@ function readAnonId() {
10199
10199
  }
10200
10200
  function superProperties() {
10201
10201
  return {
10202
- cliVersion: true ? "2.74.13" : "0.0.0-dev",
10202
+ cliVersion: true ? "2.74.14" : "0.0.0-dev",
10203
10203
  nodeVersion: process.version,
10204
10204
  platform: process.platform,
10205
10205
  arch: process.arch,
@@ -10441,7 +10441,7 @@ var http = __toESM(require("http"));
10441
10441
  // package.json
10442
10442
  var package_default = {
10443
10443
  name: "codeam-cli",
10444
- version: "2.74.13",
10444
+ version: "2.74.14",
10445
10445
  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.",
10446
10446
  type: "commonjs",
10447
10447
  main: "dist/index.js",
@@ -12131,7 +12131,7 @@ var CommandRelayService = class _CommandRelayService {
12131
12131
  // fresh + clear the "CLI update available" banner after a self-update
12132
12132
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12133
12133
  // pair/reconnect). Older backends ignore the extra field.
12134
- ..."2.74.13" ? { ideVersion: "2.74.13" } : {}
12134
+ ..."2.74.14" ? { ideVersion: "2.74.14" } : {}
12135
12135
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
12136
12136
  }
12137
12137
  /**
@@ -24451,7 +24451,7 @@ async function autoUpgradeBeforeCriticalCommand() {
24451
24451
  if (process.env.NODE_ENV === "test") return;
24452
24452
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
24453
24453
  if (process.env.CI) return;
24454
- const current2 = true ? "2.74.13" : null;
24454
+ const current2 = true ? "2.74.14" : null;
24455
24455
  if (!current2) return;
24456
24456
  const cache = readCache();
24457
24457
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -24468,7 +24468,7 @@ function checkForUpdates() {
24468
24468
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
24469
24469
  if (process.env.CI) return;
24470
24470
  if (!process.stdout.isTTY) return;
24471
- const current2 = true ? "2.74.13" : null;
24471
+ const current2 = true ? "2.74.14" : null;
24472
24472
  if (!current2) return;
24473
24473
  const cache = readCache();
24474
24474
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -24491,7 +24491,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
24491
24491
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
24492
24492
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
24493
24493
  function currentCliVersion() {
24494
- return true ? "2.74.13" : null;
24494
+ return true ? "2.74.14" : null;
24495
24495
  }
24496
24496
  async function installedVersion(deps) {
24497
24497
  const ls = await deps.run(
@@ -39845,13 +39845,17 @@ function createWakeCredentialProbe(deps) {
39845
39845
  function reconcileCumulative(existing, incoming) {
39846
39846
  if (incoming.length === 0) return existing;
39847
39847
  if (incoming.startsWith(existing)) return incoming;
39848
- if (existing.startsWith(incoming)) return existing;
39849
39848
  const shared = commonPrefixLength(existing, incoming);
39850
- if (shared > 0 && shared >= existing.length / 2) {
39849
+ if (looksLikeSnapshot(existing, incoming, shared)) {
39850
+ if (shared === incoming.length) return existing;
39851
39851
  return existing.slice(0, shared) + incoming.slice(shared);
39852
39852
  }
39853
39853
  return existing + incoming;
39854
39854
  }
39855
+ var MIN_SNAPSHOT_PREFIX = 8;
39856
+ function looksLikeSnapshot(existing, incoming, shared) {
39857
+ return shared >= MIN_SNAPSHOT_PREFIX && shared * 2 >= existing.length && incoming.length * 2 >= existing.length;
39858
+ }
39855
39859
  function commonPrefixLength(a, b) {
39856
39860
  const max = Math.min(a.length, b.length);
39857
39861
  let i = 0;
@@ -40262,25 +40266,19 @@ function mapSessionUpdate(notification) {
40262
40266
  }
40263
40267
  }
40264
40268
  function mapPermissionRequest(request2) {
40265
- const prompt = describeToolCall(request2.toolCall) ?? "The agent requested permission to continue.";
40266
- const optionIdByLabel = {};
40267
- const kindByLabel = {};
40268
- const labels = [];
40269
- for (const opt of request2.options) {
40270
- const label = opt.name?.trim() || humanizeKind(opt.kind);
40271
- if (label in optionIdByLabel) continue;
40272
- optionIdByLabel[label] = opt.optionId;
40273
- kindByLabel[label] = opt.kind;
40274
- labels.push(label);
40275
- }
40269
+ const prompt = describePermissionToolCall(request2.toolCall) ?? "The agent requested permission to continue.";
40270
+ const options = request2.options.map((opt) => ({
40271
+ label: opt.name?.trim() || humanizeKind(opt.kind),
40272
+ optionId: opt.optionId,
40273
+ kind: opt.kind
40274
+ }));
40276
40275
  return {
40277
40276
  event: {
40278
40277
  questionId: (0, import_node_crypto10.randomUUID)(),
40279
40278
  prompt,
40280
- options: labels.length > 0 ? labels : void 0
40279
+ options: options.length > 0 ? options.map((o) => ({ label: o.label, value: o.optionId })) : void 0
40281
40280
  },
40282
- optionIdByLabel,
40283
- kindByLabel
40281
+ options
40284
40282
  };
40285
40283
  }
40286
40284
  function messageChunkId(messageId) {
@@ -40311,6 +40309,28 @@ function describeToolCall(call) {
40311
40309
  }
40312
40310
  return null;
40313
40311
  }
40312
+ var PERMISSION_DETAIL_MAX = 400;
40313
+ function permissionInputDetail(rawInput) {
40314
+ if (!rawInput || typeof rawInput !== "object") return null;
40315
+ const input = rawInput;
40316
+ for (const key of ["command", "file_path", "path"]) {
40317
+ const v = input[key];
40318
+ if (typeof v === "string" && v.trim().length > 0) {
40319
+ const detail = v.trim();
40320
+ return detail.length > PERMISSION_DETAIL_MAX ? `${detail.slice(0, PERMISSION_DETAIL_MAX)}\u2026` : detail;
40321
+ }
40322
+ }
40323
+ return null;
40324
+ }
40325
+ function describePermissionToolCall(call) {
40326
+ const title = describeToolCall(call);
40327
+ const detail = permissionInputDetail(call.rawInput);
40328
+ if (!detail) return title;
40329
+ if (!title) return detail;
40330
+ if (title.includes(detail)) return title;
40331
+ return `${title}
40332
+ ${detail}`;
40333
+ }
40314
40334
  function describeToolCallUpdate(update) {
40315
40335
  const parts = [];
40316
40336
  if (Array.isArray(update.content)) {
@@ -40404,13 +40424,9 @@ function createOnRequestPermission(deps) {
40404
40424
  }
40405
40425
  log.warn("acpRunner", "AUTO mode \u2014 no allow option offered; falling back to interactive");
40406
40426
  }
40407
- const { event, optionIdByLabel } = mapPermissionRequest(request2);
40427
+ const { event, options } = mapPermissionRequest(request2);
40408
40428
  await deps.publisher.publishAwaitingAnswer(event);
40409
- return deps.registerPermission({
40410
- questionId: event.questionId,
40411
- labels: event.options ?? [],
40412
- optionIdByLabel
40413
- });
40429
+ return deps.registerPermission({ questionId: event.questionId, options });
40414
40430
  };
40415
40431
  }
40416
40432
 
@@ -43172,20 +43188,19 @@ async function selectOptionH(ctx) {
43172
43188
  const { cmd, client: client3, relay, streaming, history, budgetRecovery } = ctx;
43173
43189
  const payload = cmd.payload;
43174
43190
  const index = typeof payload?.index === "number" ? payload.index : 0;
43175
- const offset = typeof payload?.from === "number" ? payload.from : 0;
43176
- const absoluteIndex = index + offset;
43177
- if (await budgetRecovery.tryRecover(cmd.id, absoluteIndex)) return;
43178
- const result = streaming.resolveSelection(absoluteIndex);
43191
+ const optionId = typeof payload?.optionId === "string" && payload.optionId.length > 0 ? payload.optionId : typeof payload?.answer === "string" && payload.answer.length > 0 ? payload.answer : void 0;
43192
+ if (await budgetRecovery.tryRecover(cmd.id, index)) return;
43193
+ const result = streaming.resolveSelection(index, optionId);
43179
43194
  switch (result.kind) {
43180
43195
  case "resolved":
43181
- log.info("acpRunner", `select_option index=${absoluteIndex} \u2192 permission resolved`);
43182
- await relay.sendResult(cmd.id, "completed", {});
43183
- return;
43184
- case "reprompt": {
43185
43196
  log.info(
43186
43197
  "acpRunner",
43187
- `select_option index=${absoluteIndex} \u2192 reprompt chars=${result.text.length}`
43198
+ `select_option index=${index} \u2192 permission resolved optionId=${result.optionId ?? "cancelled"}`
43188
43199
  );
43200
+ await relay.sendResult(cmd.id, "completed", {});
43201
+ return;
43202
+ case "reprompt": {
43203
+ log.info("acpRunner", `select_option index=${index} \u2192 reprompt chars=${result.text.length}`);
43189
43204
  await streaming.beginTurn();
43190
43205
  history.appendUserPrompt(result.text);
43191
43206
  try {
@@ -43205,7 +43220,7 @@ async function selectOptionH(ctx) {
43205
43220
  case "none":
43206
43221
  log.warn(
43207
43222
  "acpRunner",
43208
- `select_option index=${absoluteIndex} arrived with no pending question \u2014 likely stale`
43223
+ `select_option index=${index} arrived with no pending question \u2014 likely stale`
43209
43224
  );
43210
43225
  await relay.sendResult(cmd.id, "failed", {
43211
43226
  error: "No pending interactive question \u2014 the prompt may have expired."
@@ -43643,9 +43658,17 @@ var StreamingState = class {
43643
43658
  turnSeq = 0;
43644
43659
  /**
43645
43660
  * Register a permission Promise. The Promise stays pending until
43646
- * `resolveSelection(index)` is called from the relay handler, OR
43647
- * the safety timer fires after PERMISSION_TIMEOUT_MS and we
43648
- * default-reject. Caller is the SDK's `onRequestPermission`.
43661
+ * `resolveSelection()` is called from the relay handler, OR the
43662
+ * safety timer fires after PERMISSION_TIMEOUT_MS and we auto-cancel.
43663
+ * Caller is the SDK's `onRequestPermission`.
43664
+ *
43665
+ * The auto-cancel is NEVER silent (same HARD RULE as the auto-denies
43666
+ * in ./permission-gate.ts): a visible chat line says the request
43667
+ * expired. The adapter reports a cancel to the model as a failed tool
43668
+ * ("Tool use aborted"), which the model then narrates as a refusal —
43669
+ * replay 01a08b05 (2026-09-10): two prompts expired while the user was
43670
+ * in the preview, Claude told him HE had refused them twice, and he
43671
+ * protested "J'ai rien refusé". The line is the user's evidence.
43649
43672
  */
43650
43673
  registerPermission(args2) {
43651
43674
  return new Promise((resolve11) => {
@@ -43656,14 +43679,18 @@ var StreamingState = class {
43656
43679
  `permission ${args2.questionId.slice(0, 8)} TTL expired \u2014 auto-cancel`
43657
43680
  );
43658
43681
  this.pending = null;
43682
+ void this.publisher.publishOutput({
43683
+ type: "text",
43684
+ content: permissionExpiredNotice(),
43685
+ done: true
43686
+ });
43659
43687
  resolve11({ outcome: { outcome: "cancelled" } });
43660
43688
  }
43661
43689
  }, PERMISSION_TIMEOUT_MS);
43662
43690
  this.pending = {
43663
43691
  kind: "permission",
43664
43692
  questionId: args2.questionId,
43665
- labels: args2.labels,
43666
- optionIdByLabel: args2.optionIdByLabel,
43693
+ options: args2.options,
43667
43694
  resolve: resolve11,
43668
43695
  timeoutTimer
43669
43696
  };
@@ -43683,28 +43710,33 @@ var StreamingState = class {
43683
43710
  * routing instruction the runner uses to drive the next ACP RPC:
43684
43711
  *
43685
43712
  * - 'resolved' — permission Promise resolved; SDK is unblocked
43686
- * and the runner just acks the relay command.
43713
+ * and the runner just acks the relay command. `optionId` is
43714
+ * what was answered (null = cancelled).
43687
43715
  * - 'reprompt' — free-form selection; the runner must call
43688
43716
  * `client.prompt(text)` with the returned text to send the
43689
43717
  * user's pick to the adapter as a new turn.
43690
43718
  * - 'none' — nothing pending; the runner acks the command as
43691
43719
  * failed so mobile shows a stale-question affordance.
43720
+ *
43721
+ * For a permission, `optionId` (the wire `value` the client echoes
43722
+ * back) wins when it names one of the registered options; otherwise
43723
+ * `index` is the position in the options we put on the wire.
43692
43724
  */
43693
- resolveSelection(index) {
43725
+ resolveSelection(index, optionId) {
43694
43726
  if (!this.pending) return { kind: "none" };
43695
43727
  if (this.pending.kind === "permission") {
43696
- const label = this.pending.labels[index];
43697
- const optionId = label ? this.pending.optionIdByLabel[label] : void 0;
43728
+ const byId = optionId ? this.pending.options.find((o) => o.optionId === optionId) : void 0;
43729
+ const picked = byId ?? this.pending.options[index];
43698
43730
  clearTimeout(this.pending.timeoutTimer);
43699
43731
  const resolve11 = this.pending.resolve;
43700
43732
  this.pending = null;
43701
- if (!optionId) {
43733
+ if (!picked) {
43702
43734
  log.warn("acpRunner", `select_option index=${index} out of bounds \u2014 cancel`);
43703
43735
  resolve11({ outcome: { outcome: "cancelled" } });
43704
- return { kind: "resolved" };
43736
+ return { kind: "resolved", optionId: null };
43705
43737
  }
43706
- resolve11({ outcome: { outcome: "selected", optionId } });
43707
- return { kind: "resolved" };
43738
+ resolve11({ outcome: { outcome: "selected", optionId: picked.optionId } });
43739
+ return { kind: "resolved", optionId: picked.optionId };
43708
43740
  }
43709
43741
  const text = this.pending.options[index];
43710
43742
  this.pending = null;
@@ -43968,6 +44000,10 @@ var StreamingState = class {
43968
44000
  }
43969
44001
  };
43970
44002
  var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
44003
+ function permissionExpiredNotice() {
44004
+ const minutes = Math.round(PERMISSION_TIMEOUT_MS / 6e4);
44005
+ return `\u23F1 A permission request expired after ${minutes} minutes with no answer, so CodeAgent cancelled it \u2014 this was a timeout, not a refusal by you. Ask the agent to try again and approve it from here.`;
44006
+ }
43971
44007
  var AcpHistory = class {
43972
44008
  constructor(publisher, opts) {
43973
44009
  this.publisher = publisher;
@@ -46604,13 +46640,9 @@ async function runBatonSession(opts) {
46604
46640
  beginLoadReplay: () => streaming.beginLoadReplay(),
46605
46641
  endLoadReplay: () => streaming.endLoadReplay(),
46606
46642
  onRequestPermission: async (request2) => {
46607
- const { event, optionIdByLabel } = mapPermissionRequest(request2);
46643
+ const { event, options } = mapPermissionRequest(request2);
46608
46644
  await publisher.publishAwaitingAnswer(event);
46609
- return streaming.registerPermission({
46610
- questionId: event.questionId,
46611
- labels: event.options ?? [],
46612
- optionIdByLabel
46613
- });
46645
+ return streaming.registerPermission({ questionId: event.questionId, options });
46614
46646
  },
46615
46647
  onStderr: (line) => {
46616
46648
  recentStderr.push(line);
@@ -49825,7 +49857,7 @@ function checkChokidar() {
49825
49857
  }
49826
49858
  async function doctor(args2 = []) {
49827
49859
  const json = args2.includes("--json");
49828
- const cliVersion = true ? "2.74.13" : "0.0.0-dev";
49860
+ const cliVersion = true ? "2.74.14" : "0.0.0-dev";
49829
49861
  const apiBase2 = resolveApiBaseUrl();
49830
49862
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
49831
49863
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -50349,7 +50381,7 @@ async function mcpWarm(argv = [], opts = {}) {
50349
50381
  // src/commands/version.ts
50350
50382
  var import_picocolors15 = __toESM(require("picocolors"));
50351
50383
  function version2() {
50352
- const v = true ? "2.74.13" : "unknown";
50384
+ const v = true ? "2.74.14" : "unknown";
50353
50385
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
50354
50386
  }
50355
50387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.74.13",
3
+ "version": "2.74.14",
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",