codeam-cli 2.65.3 → 2.65.4

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,12 @@ 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.3] — 2026-08-14
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Strip squad-context + agent meta blocks from codex hydration (title/summary leak)
12
+
7
13
  ## [2.65.2] — 2026-08-14
8
14
 
9
15
  ### Added
package/dist/index.js CHANGED
@@ -976,6 +976,12 @@ var PUBLIC_TO_INTERNAL = {
976
976
  // MiniMax proxy). Its internal runtime is therefore `claude`.
977
977
  [HOUSE_AGENT_ID]: "claude"
978
978
  };
979
+ function isPublicToInternalKey(v) {
980
+ return Object.prototype.hasOwnProperty.call(PUBLIC_TO_INTERNAL, v);
981
+ }
982
+ function publicToInternal(publicId) {
983
+ return isPublicToInternalKey(publicId) ? PUBLIC_TO_INTERNAL[publicId] : null;
984
+ }
979
985
  var TERMINAL_AGENT_PREFIX = "__terminal__:";
980
986
  var AGENT_ID_ALIASES = {
981
987
  claude_code: "claude",
@@ -8073,7 +8079,7 @@ function readAnonId() {
8073
8079
  }
8074
8080
  function superProperties() {
8075
8081
  return {
8076
- cliVersion: true ? "2.65.3" : "0.0.0-dev",
8082
+ cliVersion: true ? "2.65.4" : "0.0.0-dev",
8077
8083
  nodeVersion: process.version,
8078
8084
  platform: process.platform,
8079
8085
  arch: process.arch,
@@ -8254,7 +8260,7 @@ var os4 = __toESM(require("os"));
8254
8260
  // package.json
8255
8261
  var package_default = {
8256
8262
  name: "codeam-cli",
8257
- version: "2.65.3",
8263
+ version: "2.65.4",
8258
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.",
8259
8265
  type: "commonjs",
8260
8266
  main: "dist/index.js",
@@ -9706,7 +9712,7 @@ var CommandRelayService = class _CommandRelayService {
9706
9712
  // fresh + clear the "CLI update available" banner after a self-update
9707
9713
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9708
9714
  // pair/reconnect). Older backends ignore the extra field.
9709
- ..."2.65.3" ? { ideVersion: "2.65.3" } : {}
9715
+ ..."2.65.4" ? { ideVersion: "2.65.4" } : {}
9710
9716
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9711
9717
  }
9712
9718
  /**
@@ -15717,11 +15723,17 @@ var TEAM_PREAMBLE_MARKER = "[Team context]";
15717
15723
  var TEAM_PREAMBLE_LINES = [
15718
15724
  "[Team context] You are the active agent in a CodeAgent Mobile session where the user",
15719
15725
  "has a squad of agents and can pass work between them. Your available teammates:",
15720
- "If a task clearly fits a teammate better than you, you MAY propose a handoff by ending",
15721
- `your reply with a fenced code block tagged ${HANDOFF_FENCE_TAG} containing ONE JSON object:`,
15726
+ "If a task clearly fits a teammate better than you, you MAY propose a handoff. The fence",
15727
+ `MUST be a three-backtick code block with the info string exactly ${HANDOFF_FENCE_TAG}`,
15728
+ "(never a single backtick, never bare text) with the JSON alone on its own line inside",
15729
+ "it, exactly like this:",
15730
+ "```" + HANDOFF_FENCE_TAG,
15722
15731
  '{"to":"<teammate id>","reason":"<one sentence>","prompt":"<the prompt they should run>"}',
15723
- "Propose at most one handoff per reply, only when genuinely better, and never announce",
15724
- "the block in prose \u2014 the app renders it as a card the user can accept."
15732
+ "```",
15733
+ '"to" MUST be one of the ids shown above, written EXACTLY as shown (e.g. "claude", never',
15734
+ 'a display name like "Claude Code"). Propose at most one handoff per reply, only when',
15735
+ "genuinely better, and never announce the block in prose \u2014 the app renders it as a card",
15736
+ "the user can accept."
15725
15737
  ];
15726
15738
  var TEAM_PREAMBLE_BULLET_RE = /^- .+ — best at: /;
15727
15739
  var BRIEFING_MARKER = "[Team update]";
@@ -15733,7 +15745,9 @@ function buildTeamPreamble(roster, currentAgent, opts) {
15733
15745
  const lines = [
15734
15746
  TEAM_PREAMBLE_LINES[0],
15735
15747
  TEAM_PREAMBLE_LINES[1],
15736
- ...others.map((a) => `- ${a.displayName} \u2014 best at: ${specialtyFor(a.agentId)}`)
15748
+ ...others.map(
15749
+ (a) => `- ${a.displayName} (id: ${a.agentId}) \u2014 best at: ${specialtyFor(a.agentId)}`
15750
+ )
15737
15751
  ];
15738
15752
  if (opts.handoffInstructions) {
15739
15753
  lines.push(...TEAM_PREAMBLE_LINES.slice(2));
@@ -21627,7 +21641,7 @@ async function autoUpgradeBeforeCriticalCommand() {
21627
21641
  if (process.env.NODE_ENV === "test") return;
21628
21642
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21629
21643
  if (process.env.CI) return;
21630
- const current2 = true ? "2.65.3" : null;
21644
+ const current2 = true ? "2.65.4" : null;
21631
21645
  if (!current2) return;
21632
21646
  const cache = readCache();
21633
21647
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21644,7 +21658,7 @@ function checkForUpdates() {
21644
21658
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21645
21659
  if (process.env.CI) return;
21646
21660
  if (!process.stdout.isTTY) return;
21647
- const current2 = true ? "2.65.3" : null;
21661
+ const current2 = true ? "2.65.4" : null;
21648
21662
  if (!current2) return;
21649
21663
  const cache = readCache();
21650
21664
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21664,7 +21678,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
21664
21678
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
21665
21679
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
21666
21680
  function currentCliVersion() {
21667
- return true ? "2.65.3" : null;
21681
+ return true ? "2.65.4" : null;
21668
21682
  }
21669
21683
  function runCmd(cmd, args2, timeoutMs) {
21670
21684
  return new Promise((resolve9) => {
@@ -35365,6 +35379,9 @@ var REASON_MAX = 1e3;
35365
35379
  var PROMPT_MAX2 = 8e3;
35366
35380
  var FENCE_OPEN = "```" + HANDOFF_FENCE_TAG;
35367
35381
  var FENCE_RE = new RegExp("```" + HANDOFF_FENCE_TAG + "\\s*\\n([\\s\\S]*?)\\n?```", "g");
35382
+ var DEGENERATE_LINE_RE = new RegExp(
35383
+ "^[ \\t]*(`{0,2})[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]+(\\{.*\\})[ \\t]*\\1[ \\t]*\\r?$"
35384
+ );
35368
35385
  var OUTER_FENCE_RE = /(`{4,})[\s\S]*?\1/g;
35369
35386
  var outerFencePlaceholder = (i) => `@@HANDOFF_MASK_${i}@@`;
35370
35387
  function maskOuterFences(text) {
@@ -35377,62 +35394,117 @@ function maskOuterFences(text) {
35377
35394
  const restore = (s) => spans.reduce((acc, span, i) => acc.split(outerFencePlaceholder(i)).join(span), s);
35378
35395
  return { masked, restore };
35379
35396
  }
35380
- function parseProposal(raw, currentAgent, validTargets) {
35397
+ function parseProposalShape(raw) {
35381
35398
  let parsed;
35382
35399
  try {
35383
35400
  parsed = JSON.parse(raw);
35384
35401
  } catch {
35385
- log.debug("handoffProtocol", "dropped proposal: malformed JSON");
35386
- return null;
35387
- }
35388
- if (typeof parsed !== "object" || parsed === null) {
35389
- log.debug("handoffProtocol", "dropped proposal: not a JSON object");
35390
35402
  return null;
35391
35403
  }
35404
+ if (typeof parsed !== "object" || parsed === null) return null;
35392
35405
  const { to, reason, prompt } = parsed;
35393
- if (typeof to !== "string" || to.length === 0) {
35394
- log.debug("handoffProtocol", 'dropped proposal: missing/invalid "to"');
35406
+ if (typeof to !== "string" || to.length === 0) return null;
35407
+ if (typeof reason !== "string" || reason.length === 0 || reason.length > REASON_MAX) {
35395
35408
  return null;
35396
35409
  }
35397
- if (!validTargets.has(to)) {
35398
- log.debug("handoffProtocol", `dropped proposal: unknown target "${to}"`);
35410
+ if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > PROMPT_MAX2) {
35399
35411
  return null;
35400
35412
  }
35401
- if (to === currentAgent) {
35402
- log.debug("handoffProtocol", "dropped proposal: target is the current agent");
35413
+ return { to, reason, prompt };
35414
+ }
35415
+ function resolveHandoffTarget(raw, validTargets) {
35416
+ const trimmed = (raw ?? "").trim();
35417
+ if (!trimmed) return null;
35418
+ const lower = trimmed.toLowerCase();
35419
+ for (const target of validTargets) {
35420
+ if (target.toLowerCase() === lower) return target;
35421
+ }
35422
+ const viaAlias = normalizeAgentId(trimmed);
35423
+ if (viaAlias && validTargets.has(viaAlias)) return viaAlias;
35424
+ const viaPublic = publicToInternal(lower);
35425
+ if (viaPublic && validTargets.has(viaPublic)) return viaPublic;
35426
+ for (const meta of Object.values(AGENT_REGISTRY)) {
35427
+ if (meta.displayName.toLowerCase() === lower && validTargets.has(meta.id)) {
35428
+ return meta.id;
35429
+ }
35430
+ }
35431
+ return null;
35432
+ }
35433
+ function parseProposal(raw, currentAgent, validTargets) {
35434
+ const shape = parseProposalShape(raw);
35435
+ if (!shape) {
35436
+ log.debug("handoffProtocol", "dropped proposal: malformed JSON or invalid shape");
35403
35437
  return null;
35404
35438
  }
35405
- if (typeof reason !== "string" || reason.length === 0 || reason.length > REASON_MAX) {
35406
- log.debug("handoffProtocol", 'dropped proposal: invalid "reason"');
35439
+ const to = resolveHandoffTarget(shape.to, validTargets);
35440
+ if (!to) {
35441
+ log.debug("handoffProtocol", `dropped proposal: unknown target "${shape.to}"`);
35407
35442
  return null;
35408
35443
  }
35409
- if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > PROMPT_MAX2) {
35410
- log.debug("handoffProtocol", 'dropped proposal: invalid "prompt"');
35444
+ if (to === currentAgent) {
35445
+ log.debug("handoffProtocol", "dropped proposal: target is the current agent");
35411
35446
  return null;
35412
35447
  }
35413
- return { to, reason, prompt };
35448
+ return { to, reason: shape.reason, prompt: shape.prompt };
35449
+ }
35450
+ function collapseSeams(s) {
35451
+ return s.replace(/(?:\r?\n){3,}/g, "\n\n").trim();
35414
35452
  }
35415
35453
  function stripFences(masked) {
35416
- const stripped = masked.replace(FENCE_RE, "");
35417
- return stripped.replace(/(?:\r?\n){3,}/g, "\n\n").trim();
35454
+ return collapseSeams(masked.replace(FENCE_RE, ""));
35455
+ }
35456
+ function lastNonBlankLine(masked) {
35457
+ const parts = masked.split("\n");
35458
+ const starts = [];
35459
+ let offset = 0;
35460
+ for (const p2 of parts) {
35461
+ starts.push(offset);
35462
+ offset += p2.length + 1;
35463
+ }
35464
+ for (let i = parts.length - 1; i >= 0; i--) {
35465
+ if (parts[i].trim().length > 0) return { line: parts[i], start: starts[i] };
35466
+ }
35467
+ return null;
35468
+ }
35469
+ function trailingDegenerateMatch(masked) {
35470
+ const last = lastNonBlankLine(masked);
35471
+ if (!last) return null;
35472
+ const match = last.line.match(DEGENERATE_LINE_RE);
35473
+ return match ? { match, start: last.start } : null;
35418
35474
  }
35419
35475
  function extractHandoffProposal(text, currentAgent, validTargets) {
35420
35476
  const { masked, restore } = maskOuterFences(text);
35421
35477
  const matches = [...masked.matchAll(FENCE_RE)];
35422
- if (matches.length === 0) {
35478
+ if (matches.length > 0) {
35479
+ const last = matches[matches.length - 1];
35480
+ const proposal = parseProposal(last[1].trim(), currentAgent, validTargets);
35481
+ const cleanText = restore(stripFences(masked));
35482
+ return { cleanText, proposal };
35483
+ }
35484
+ const degenerate = trailingDegenerateMatch(masked);
35485
+ if (degenerate) {
35486
+ const proposal = parseProposal(degenerate.match[2].trim(), currentAgent, validTargets);
35487
+ if (proposal) {
35488
+ const { start: start2 } = degenerate;
35489
+ const stripped = masked.slice(0, start2) + masked.slice(start2 + degenerate.match[0].length);
35490
+ return { cleanText: restore(collapseSeams(stripped)), proposal };
35491
+ }
35423
35492
  return { cleanText: text, proposal: null };
35424
35493
  }
35425
- const last = matches[matches.length - 1];
35426
- const proposal = parseProposal(last[1].trim(), currentAgent, validTargets);
35427
- const cleanText = restore(stripFences(masked));
35428
- return { cleanText, proposal };
35494
+ return { cleanText: text, proposal: null };
35429
35495
  }
35430
35496
  function stripHandoffFences(text) {
35431
35497
  const { masked, restore } = maskOuterFences(text);
35432
- if (masked.search(FENCE_RE) === -1) {
35433
- return text;
35498
+ if (masked.search(FENCE_RE) !== -1) {
35499
+ return restore(stripFences(masked));
35500
+ }
35501
+ 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);
35505
+ return restore(collapseSeams(stripped));
35434
35506
  }
35435
- return restore(stripFences(masked));
35507
+ return text;
35436
35508
  }
35437
35509
  function handoffFenceStartMasked(text) {
35438
35510
  const { masked } = maskOuterFences(text);
@@ -44499,7 +44571,7 @@ function checkChokidar() {
44499
44571
  }
44500
44572
  async function doctor(args2 = []) {
44501
44573
  const json = args2.includes("--json");
44502
- const cliVersion = true ? "2.65.3" : "0.0.0-dev";
44574
+ const cliVersion = true ? "2.65.4" : "0.0.0-dev";
44503
44575
  const apiBase2 = resolveApiBaseUrl();
44504
44576
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
44505
44577
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -44890,7 +44962,7 @@ async function mcpRun(args2) {
44890
44962
  // src/commands/version.ts
44891
44963
  var import_picocolors15 = __toESM(require("picocolors"));
44892
44964
  function version2() {
44893
- const v = true ? "2.65.3" : "unknown";
44965
+ const v = true ? "2.65.4" : "unknown";
44894
44966
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
44895
44967
  }
44896
44968
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.65.3",
3
+ "version": "2.65.4",
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",