codeam-cli 2.65.4 → 2.65.5

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,13 @@ 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.65.4] — 2026-08-14
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Handoff protocol tolerates display-name targets + inline degenerate fences
12
+ - **cli:** Degenerate handoff form must be reply-trailing (kills fabricated-proposal false positive)
13
+
7
14
  ## [2.65.3] — 2026-08-14
8
15
 
9
16
  ### Fixed
package/dist/index.js CHANGED
@@ -8079,7 +8079,7 @@ function readAnonId() {
8079
8079
  }
8080
8080
  function superProperties() {
8081
8081
  return {
8082
- cliVersion: true ? "2.65.4" : "0.0.0-dev",
8082
+ cliVersion: true ? "2.65.5" : "0.0.0-dev",
8083
8083
  nodeVersion: process.version,
8084
8084
  platform: process.platform,
8085
8085
  arch: process.arch,
@@ -8260,7 +8260,7 @@ var os4 = __toESM(require("os"));
8260
8260
  // package.json
8261
8261
  var package_default = {
8262
8262
  name: "codeam-cli",
8263
- version: "2.65.4",
8263
+ version: "2.65.5",
8264
8264
  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.",
8265
8265
  type: "commonjs",
8266
8266
  main: "dist/index.js",
@@ -9712,7 +9712,7 @@ var CommandRelayService = class _CommandRelayService {
9712
9712
  // fresh + clear the "CLI update available" banner after a self-update
9713
9713
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9714
9714
  // pair/reconnect). Older backends ignore the extra field.
9715
- ..."2.65.4" ? { ideVersion: "2.65.4" } : {}
9715
+ ..."2.65.5" ? { ideVersion: "2.65.5" } : {}
9716
9716
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9717
9717
  }
9718
9718
  /**
@@ -21641,7 +21641,7 @@ async function autoUpgradeBeforeCriticalCommand() {
21641
21641
  if (process.env.NODE_ENV === "test") return;
21642
21642
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21643
21643
  if (process.env.CI) return;
21644
- const current2 = true ? "2.65.4" : null;
21644
+ const current2 = true ? "2.65.5" : null;
21645
21645
  if (!current2) return;
21646
21646
  const cache = readCache();
21647
21647
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21658,7 +21658,7 @@ function checkForUpdates() {
21658
21658
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21659
21659
  if (process.env.CI) return;
21660
21660
  if (!process.stdout.isTTY) return;
21661
- const current2 = true ? "2.65.4" : null;
21661
+ const current2 = true ? "2.65.5" : null;
21662
21662
  if (!current2) return;
21663
21663
  const cache = readCache();
21664
21664
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21678,7 +21678,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
21678
21678
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
21679
21679
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
21680
21680
  function currentCliVersion() {
21681
- return true ? "2.65.4" : null;
21681
+ return true ? "2.65.5" : null;
21682
21682
  }
21683
21683
  function runCmd(cmd, args2, timeoutMs) {
21684
21684
  return new Promise((resolve9) => {
@@ -35382,6 +35382,10 @@ var FENCE_RE = new RegExp("```" + HANDOFF_FENCE_TAG + "\\s*\\n([\\s\\S]*?)\\n?``
35382
35382
  var DEGENERATE_LINE_RE = new RegExp(
35383
35383
  "^[ \\t]*(`{0,2})[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]+(\\{.*\\})[ \\t]*\\1[ \\t]*\\r?$"
35384
35384
  );
35385
+ var TAG_ONLY_LINE_RE = new RegExp(
35386
+ "^[ \\t]*`{0,2}[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]*`{0,2}[ \\t]*\\r?$"
35387
+ );
35388
+ var CLOSING_BACKTICK_LINE_RE = /^[ \t]*`+[ \t]*\r?$/;
35385
35389
  var OUTER_FENCE_RE = /(`{4,})[\s\S]*?\1/g;
35386
35390
  var outerFencePlaceholder = (i) => `@@HANDOFF_MASK_${i}@@`;
35387
35391
  function maskOuterFences(text) {
@@ -35453,24 +35457,92 @@ function collapseSeams(s) {
35453
35457
  function stripFences(masked) {
35454
35458
  return collapseSeams(masked.replace(FENCE_RE, ""));
35455
35459
  }
35456
- function lastNonBlankLine(masked) {
35457
- const parts = masked.split("\n");
35460
+ function lineStarts(parts) {
35458
35461
  const starts = [];
35459
35462
  let offset = 0;
35460
35463
  for (const p2 of parts) {
35461
35464
  starts.push(offset);
35462
35465
  offset += p2.length + 1;
35463
35466
  }
35467
+ return starts;
35468
+ }
35469
+ function lastNonBlankLine(masked) {
35470
+ const parts = masked.split("\n");
35471
+ const starts = lineStarts(parts);
35464
35472
  for (let i = parts.length - 1; i >= 0; i--) {
35465
35473
  if (parts[i].trim().length > 0) return { line: parts[i], start: starts[i] };
35466
35474
  }
35467
35475
  return null;
35468
35476
  }
35469
- function trailingDegenerateMatch(masked) {
35477
+ function trailingSameLineMatch(masked) {
35470
35478
  const last = lastNonBlankLine(masked);
35471
35479
  if (!last) return null;
35472
- const match = last.line.match(DEGENERATE_LINE_RE);
35473
- return match ? { match, start: last.start } : null;
35480
+ const m = last.line.match(DEGENERATE_LINE_RE);
35481
+ if (!m) return null;
35482
+ return { jsonRaw: m[2], start: last.start, end: last.start + m[0].length };
35483
+ }
35484
+ function trailingBlockMatch(masked) {
35485
+ const lines = masked.split("\n");
35486
+ const starts = lineStarts(lines);
35487
+ let tagLineIdx = -1;
35488
+ for (let i = lines.length - 1; i >= 0; i--) {
35489
+ if (TAG_ONLY_LINE_RE.test(lines[i])) {
35490
+ tagLineIdx = i;
35491
+ break;
35492
+ }
35493
+ }
35494
+ if (tagLineIdx === -1) return null;
35495
+ let end = lines.length - 1;
35496
+ while (end > tagLineIdx && lines[end].trim().length === 0) end--;
35497
+ if (end > tagLineIdx && CLOSING_BACKTICK_LINE_RE.test(lines[end])) {
35498
+ end--;
35499
+ while (end > tagLineIdx && lines[end].trim().length === 0) end--;
35500
+ }
35501
+ if (end <= tagLineIdx) return null;
35502
+ const jsonRaw = lines.slice(tagLineIdx + 1, end + 1).join("\n");
35503
+ return { jsonRaw, start: starts[tagLineIdx], end: masked.length };
35504
+ }
35505
+ function trailingDegenerateMatch(masked) {
35506
+ return trailingSameLineMatch(masked) ?? trailingBlockMatch(masked);
35507
+ }
35508
+ function earlierShapeValidDegenerateSpans(masked, beforeOffset) {
35509
+ const lines = masked.split("\n");
35510
+ const starts = lineStarts(lines);
35511
+ const spans = [];
35512
+ for (let i = 0; i < lines.length && starts[i] < beforeOffset; i++) {
35513
+ const same = lines[i].match(DEGENERATE_LINE_RE);
35514
+ if (same) {
35515
+ const end = starts[i] + same[0].length;
35516
+ if (end <= beforeOffset && parseProposalShape(same[2].trim())) {
35517
+ spans.push({ start: starts[i], end });
35518
+ }
35519
+ continue;
35520
+ }
35521
+ if (TAG_ONLY_LINE_RE.test(lines[i])) {
35522
+ let j2 = i + 1;
35523
+ while (j2 < lines.length && lines[j2].trim().length === 0) j2++;
35524
+ if (j2 >= lines.length || starts[j2] >= beforeOffset) continue;
35525
+ if (!parseProposalShape(lines[j2].trim())) continue;
35526
+ let end = starts[j2] + lines[j2].length;
35527
+ let k2 = j2 + 1;
35528
+ if (k2 < lines.length && starts[k2] < beforeOffset && CLOSING_BACKTICK_LINE_RE.test(lines[k2])) {
35529
+ end = starts[k2] + lines[k2].length;
35530
+ }
35531
+ if (end <= beforeOffset) spans.push({ start: starts[i], end });
35532
+ }
35533
+ }
35534
+ return spans;
35535
+ }
35536
+ function removeSpans(text, spans) {
35537
+ const sorted = [...spans].sort((a, b) => a.start - b.start);
35538
+ let result = "";
35539
+ let cursor = 0;
35540
+ for (const span of sorted) {
35541
+ result += text.slice(cursor, span.start);
35542
+ cursor = span.end;
35543
+ }
35544
+ result += text.slice(cursor);
35545
+ return result;
35474
35546
  }
35475
35547
  function extractHandoffProposal(text, currentAgent, validTargets) {
35476
35548
  const { masked, restore } = maskOuterFences(text);
@@ -35483,10 +35555,13 @@ function extractHandoffProposal(text, currentAgent, validTargets) {
35483
35555
  }
35484
35556
  const degenerate = trailingDegenerateMatch(masked);
35485
35557
  if (degenerate) {
35486
- const proposal = parseProposal(degenerate.match[2].trim(), currentAgent, validTargets);
35558
+ const proposal = parseProposal(degenerate.jsonRaw.trim(), currentAgent, validTargets);
35487
35559
  if (proposal) {
35488
- const { start: start2 } = degenerate;
35489
- const stripped = masked.slice(0, start2) + masked.slice(start2 + degenerate.match[0].length);
35560
+ const earlier = earlierShapeValidDegenerateSpans(masked, degenerate.start);
35561
+ const stripped = removeSpans(masked, [
35562
+ ...earlier,
35563
+ { start: degenerate.start, end: degenerate.end }
35564
+ ]);
35490
35565
  return { cleanText: restore(collapseSeams(stripped)), proposal };
35491
35566
  }
35492
35567
  return { cleanText: text, proposal: null };
@@ -35499,9 +35574,8 @@ function stripHandoffFences(text) {
35499
35574
  return restore(stripFences(masked));
35500
35575
  }
35501
35576
  const degenerate = trailingDegenerateMatch(masked);
35502
- if (degenerate && parseProposalShape(degenerate.match[2].trim())) {
35503
- const { start: start2 } = degenerate;
35504
- const stripped = masked.slice(0, start2) + masked.slice(start2 + degenerate.match[0].length);
35577
+ if (degenerate && parseProposalShape(degenerate.jsonRaw.trim())) {
35578
+ const stripped = masked.slice(0, degenerate.start) + masked.slice(degenerate.end);
35505
35579
  return restore(collapseSeams(stripped));
35506
35580
  }
35507
35581
  return text;
@@ -44571,7 +44645,7 @@ function checkChokidar() {
44571
44645
  }
44572
44646
  async function doctor(args2 = []) {
44573
44647
  const json = args2.includes("--json");
44574
- const cliVersion = true ? "2.65.4" : "0.0.0-dev";
44648
+ const cliVersion = true ? "2.65.5" : "0.0.0-dev";
44575
44649
  const apiBase2 = resolveApiBaseUrl();
44576
44650
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
44577
44651
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -44962,7 +45036,7 @@ async function mcpRun(args2) {
44962
45036
  // src/commands/version.ts
44963
45037
  var import_picocolors15 = __toESM(require("picocolors"));
44964
45038
  function version2() {
44965
- const v = true ? "2.65.4" : "unknown";
45039
+ const v = true ? "2.65.5" : "unknown";
44966
45040
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
44967
45041
  }
44968
45042
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.65.4",
3
+ "version": "2.65.5",
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",