codeam-cli 2.65.5 → 2.65.7

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.6] — 2026-08-15
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Accumulated stream keeps post-cut deltas + unclosed trailing fence tolerated
12
+
7
13
  ## [2.65.4] — 2026-08-14
8
14
 
9
15
  ### 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.5" : "0.0.0-dev",
8082
+ cliVersion: true ? "2.65.7" : "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.5",
8263
+ version: "2.65.7",
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",
@@ -8632,7 +8632,19 @@ async function postHeadroomEvent(input) {
8632
8632
  };
8633
8633
  }
8634
8634
  }
8635
- async function fetchProvisionCredential(input) {
8635
+ function parseBackendErrorBody(body) {
8636
+ if (!body) return null;
8637
+ try {
8638
+ const parsed = JSON.parse(body);
8639
+ const nested = parsed.error;
8640
+ const code = typeof nested?.code === "string" ? nested.code : typeof parsed.code === "string" ? parsed.code : void 0;
8641
+ const message = typeof nested?.message === "string" ? nested.message : typeof parsed.message === "string" ? parsed.message : void 0;
8642
+ return code || message ? { code, message } : null;
8643
+ } catch {
8644
+ return null;
8645
+ }
8646
+ }
8647
+ async function fetchProvisionCredentialDetailed(input) {
8636
8648
  try {
8637
8649
  const res = await _transport.postJsonAuthed(
8638
8650
  `${API_BASE}/api/plugin/agents/${input.agentId}/provision-credential`,
@@ -8646,16 +8658,29 @@ async function fetchProvisionCredential(input) {
8646
8658
  const data = res?.data;
8647
8659
  if (data && (data.method === "api_key" || data.method === "oauth") && typeof data.credential === "string" && data.credential.length > 0) {
8648
8660
  return {
8661
+ ok: true,
8649
8662
  method: data.method,
8650
8663
  credential: data.credential,
8651
8664
  ...typeof data.installScript === "string" && data.installScript.length > 0 ? { installScript: data.installScript } : {}
8652
8665
  };
8653
8666
  }
8654
- return null;
8655
- } catch {
8656
- return null;
8667
+ return { ok: false, status: 0 };
8668
+ } catch (err) {
8669
+ const e = err;
8670
+ const status2 = typeof e.statusCode === "number" ? e.statusCode : 0;
8671
+ const parsed = parseBackendErrorBody(e.body);
8672
+ return { ok: false, status: status2, code: parsed?.code, message: parsed?.message };
8657
8673
  }
8658
8674
  }
8675
+ async function fetchProvisionCredential(input) {
8676
+ const res = await fetchProvisionCredentialDetailed(input);
8677
+ if (!res.ok) return null;
8678
+ return {
8679
+ method: res.method,
8680
+ credential: res.credential,
8681
+ ...res.installScript ? { installScript: res.installScript } : {}
8682
+ };
8683
+ }
8659
8684
  async function fetchSquadRoster(input) {
8660
8685
  try {
8661
8686
  const res = await _transport.postJsonAuthed(
@@ -8860,6 +8885,7 @@ function makeHttpError(statusCode, retryAfterHeader, responseBody) {
8860
8885
  );
8861
8886
  err.statusCode = statusCode;
8862
8887
  if (typeof retryAfterSeconds === "number") err.retryAfterSeconds = retryAfterSeconds;
8888
+ if (responseBody) err.body = responseBody;
8863
8889
  return err;
8864
8890
  }
8865
8891
  async function _postJson(url2, body, extraHeaders) {
@@ -9712,7 +9738,7 @@ var CommandRelayService = class _CommandRelayService {
9712
9738
  // fresh + clear the "CLI update available" banner after a self-update
9713
9739
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9714
9740
  // pair/reconnect). Older backends ignore the extra field.
9715
- ..."2.65.5" ? { ideVersion: "2.65.5" } : {}
9741
+ ..."2.65.7" ? { ideVersion: "2.65.7" } : {}
9716
9742
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9717
9743
  }
9718
9744
  /**
@@ -21641,7 +21667,7 @@ async function autoUpgradeBeforeCriticalCommand() {
21641
21667
  if (process.env.NODE_ENV === "test") return;
21642
21668
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21643
21669
  if (process.env.CI) return;
21644
- const current2 = true ? "2.65.5" : null;
21670
+ const current2 = true ? "2.65.7" : null;
21645
21671
  if (!current2) return;
21646
21672
  const cache = readCache();
21647
21673
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21658,7 +21684,7 @@ function checkForUpdates() {
21658
21684
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21659
21685
  if (process.env.CI) return;
21660
21686
  if (!process.stdout.isTTY) return;
21661
- const current2 = true ? "2.65.5" : null;
21687
+ const current2 = true ? "2.65.7" : null;
21662
21688
  if (!current2) return;
21663
21689
  const cache = readCache();
21664
21690
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21678,7 +21704,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
21678
21704
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
21679
21705
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
21680
21706
  function currentCliVersion() {
21681
- return true ? "2.65.5" : null;
21707
+ return true ? "2.65.7" : null;
21682
21708
  }
21683
21709
  function runCmd(cmd, args2, timeoutMs) {
21684
21710
  return new Promise((resolve9) => {
@@ -33971,10 +33997,7 @@ var AcpClient = class {
33971
33997
  const input = import_node_stream.Readable.toWeb(child.stdout);
33972
33998
  const output = import_node_stream.Writable.toWeb(child.stdin);
33973
33999
  const stream = ndJsonStream(output, input);
33974
- this.connection = new ClientSideConnection(
33975
- (_agent) => this.buildClient(),
33976
- stream
33977
- );
34000
+ this.connection = new ClientSideConnection((_agent) => this.buildClient(), stream);
33978
34001
  const startAborted = new Promise((_2, reject) => {
33979
34002
  this.startAbortReject = reject;
33980
34003
  });
@@ -34105,14 +34128,8 @@ var AcpClient = class {
34105
34128
  if (!this.connection || !this.sessionId) {
34106
34129
  throw new Error("AcpClient.sendPromptOnce called before start()");
34107
34130
  }
34108
- const textLen = blocks.reduce(
34109
- (n, b) => b.type === "text" ? n + b.text.length : n,
34110
- 0
34111
- );
34112
- const imageCount = blocks.reduce(
34113
- (n, b) => b.type === "image" ? n + 1 : n,
34114
- 0
34115
- );
34131
+ const textLen = blocks.reduce((n, b) => b.type === "text" ? n + b.text.length : n, 0);
34132
+ const imageCount = blocks.reduce((n, b) => b.type === "image" ? n + 1 : n, 0);
34116
34133
  log.info(
34117
34134
  "acpClient",
34118
34135
  `prompt \u2192 session=${this.sessionId.slice(0, 8)} textChars=${textLen} imageBlocks=${imageCount}`
@@ -34133,6 +34150,7 @@ var AcpClient = class {
34133
34150
  this.pendingToolCalls.clear();
34134
34151
  try {
34135
34152
  const result = await Promise.race([send, idle.promise]);
34153
+ await new Promise((resolve9) => setImmediate(resolve9));
34136
34154
  log.info(
34137
34155
  "acpClient",
34138
34156
  `prompt \u2190 ok stopReason=${result.stopReason ?? "?"} elapsedMs=${Date.now() - t0}`
@@ -34413,9 +34431,7 @@ var AcpClient = class {
34413
34431
  * hardcoded list, no guessed model.
34414
34432
  */
34415
34433
  captureModelConfig(configOptions) {
34416
- const modelOption = configOptions.find(
34417
- (o) => o.category === "model" && o.type === "select"
34418
- );
34434
+ const modelOption = configOptions.find((o) => o.category === "model" && o.type === "select");
34419
34435
  if (!modelOption || modelOption.type !== "select") {
34420
34436
  this.modelConfigId = void 0;
34421
34437
  this.availableModels = [];
@@ -34447,9 +34463,7 @@ var AcpClient = class {
34447
34463
  throw new Error("AcpClient.setMode called before start()");
34448
34464
  }
34449
34465
  if (this.availableModes.length === 0) {
34450
- throw new Error(
34451
- "mode selection not available: this agent advertises no ACP session modes"
34452
- );
34466
+ throw new Error("mode selection not available: this agent advertises no ACP session modes");
34453
34467
  }
34454
34468
  log.info("acpClient", `setMode \u2192 ${modeId}`);
34455
34469
  await this.connection.setSessionMode({
@@ -34594,11 +34608,9 @@ var AcpClient = class {
34594
34608
  const code = err.code;
34595
34609
  if (code === "ENOENT") throw RequestError.resourceNotFound(params.path);
34596
34610
  if (code === "EACCES" || code === "EPERM") {
34597
- throw new RequestError(
34598
- -32002,
34599
- `Permission denied: ${params.path}`,
34600
- { uri: params.path }
34601
- );
34611
+ throw new RequestError(-32002, `Permission denied: ${params.path}`, {
34612
+ uri: params.path
34613
+ });
34602
34614
  }
34603
34615
  if (code === "EISDIR") {
34604
34616
  throw RequestError.invalidParams(`path is a directory: ${params.path}`);
@@ -34616,16 +34628,12 @@ var AcpClient = class {
34616
34628
  } catch (err) {
34617
34629
  const code = err.code;
34618
34630
  if (code === "EACCES" || code === "EPERM") {
34619
- throw new RequestError(
34620
- -32002,
34621
- `Permission denied: ${params.path}`,
34622
- { uri: params.path }
34623
- );
34631
+ throw new RequestError(-32002, `Permission denied: ${params.path}`, {
34632
+ uri: params.path
34633
+ });
34624
34634
  }
34625
34635
  if (code === "ENOENT") {
34626
- throw RequestError.invalidParams(
34627
- `Parent directory does not exist for: ${params.path}`
34628
- );
34636
+ throw RequestError.invalidParams(`Parent directory does not exist for: ${params.path}`);
34629
34637
  }
34630
34638
  throw RequestError.internalError({ uri: params.path }, code ?? String(err));
34631
34639
  }
@@ -34704,9 +34712,7 @@ function knownAgentBinaryDirs() {
34704
34712
  });
34705
34713
  }
34706
34714
  function expandPathForAgentBinaries(existingPath) {
34707
- const existing = new Set(
34708
- existingPath.split(path74.delimiter).filter((p2) => p2.length > 0)
34709
- );
34715
+ const existing = new Set(existingPath.split(path74.delimiter).filter((p2) => p2.length > 0));
34710
34716
  const additions = [];
34711
34717
  for (const dir of knownAgentBinaryDirs()) {
34712
34718
  if (!existing.has(dir)) {
@@ -34758,6 +34764,10 @@ var NON_SWITCHABLE = /* @__PURE__ */ new Set([
34758
34764
  function displayName(id) {
34759
34765
  return isKnownAgentId(id) ? AGENT_REGISTRY[id]?.displayName ?? id : id;
34760
34766
  }
34767
+ function credentialFailureMessage(agentId, failure) {
34768
+ if (failure.message) return failure.message;
34769
+ return `No linked credential for ${displayName(agentId)}. Link it in Profile \u203A Agents first.`;
34770
+ }
34761
34771
  function resolveSwitchTarget(raw, currentAgent) {
34762
34772
  if (typeof raw !== "string" || raw.length === 0) {
34763
34773
  return { ok: false, error: "switch_agent: missing agentId" };
@@ -34874,10 +34884,8 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34874
34884
  if (!fastPath.skipProvision) {
34875
34885
  void emitStep("credential");
34876
34886
  const cred = await deps.fetchCredential(agentId);
34877
- if (!cred) {
34878
- return fail2(
34879
- `No linked credential for ${displayName(agentId)}. Link it in Profile \u203A Agents first.`
34880
- );
34887
+ if (!cred.ok) {
34888
+ return fail2(credentialFailureMessage(agentId, cred));
34881
34889
  }
34882
34890
  try {
34883
34891
  deps.provisionCredential(agentId, toAgentAuth(cred.method, cred.credential));
@@ -35383,7 +35391,7 @@ var DEGENERATE_LINE_RE = new RegExp(
35383
35391
  "^[ \\t]*(`{0,2})[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]+(\\{.*\\})[ \\t]*\\1[ \\t]*\\r?$"
35384
35392
  );
35385
35393
  var TAG_ONLY_LINE_RE = new RegExp(
35386
- "^[ \\t]*`{0,2}[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]*`{0,2}[ \\t]*\\r?$"
35394
+ "^[ \\t]*`*[ \\t]*" + HANDOFF_FENCE_TAG + "[ \\t]*`*[ \\t]*\\r?$"
35387
35395
  );
35388
35396
  var CLOSING_BACKTICK_LINE_RE = /^[ \t]*`+[ \t]*\r?$/;
35389
35397
  var OUTER_FENCE_RE = /(`{4,})[\s\S]*?\1/g;
@@ -35594,6 +35602,15 @@ function handoffFenceStartMasked(text) {
35594
35602
  }
35595
35603
  return -1;
35596
35604
  }
35605
+ function withholdTrailingPartialFenceMarker(text) {
35606
+ const maxLen = Math.min(text.length, FENCE_OPEN.length - 1);
35607
+ for (let len = maxLen; len > 0; len--) {
35608
+ if (FENCE_OPEN.startsWith(text.slice(text.length - len))) {
35609
+ return text.slice(0, text.length - len);
35610
+ }
35611
+ }
35612
+ return text;
35613
+ }
35597
35614
 
35598
35615
  // src/agents/acp/onboarding.ts
35599
35616
  var import_child_process28 = require("child_process");
@@ -36628,6 +36645,11 @@ var AUTH_FAILURE_MESSAGE = "\u{1F512} **Authentication failed \u2014 your agent
36628
36645
  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)";
36629
36646
  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.";
36630
36647
  var TURN_FAILURE_MESSAGE = "\u26A0\uFE0F **The agent hit an error and couldn\u2019t finish this turn.** Please send your message again.";
36648
+ function emptyReplyMessage(agent) {
36649
+ const label = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].displayName : agent;
36650
+ const binary = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].binaryName : agent;
36651
+ return `\u26A0\uFE0F **${label} returned an empty reply.** Its provider may be having issues \u2014 check the agent's login/subscription on this machine (e.g. run \`${binary} -p "test"\` there).`;
36652
+ }
36631
36653
  var PROVIDER_OUTAGE_RE = /overloaded_error|\boverloaded\b|service[ _]unavailable|temporarily[ _]unavailable|(?:api error|http|status)[:\s]+(?:529|503|502|504)\b|\b(?:529|503|502|504)\b[^\n]{0,40}(?:overload|unavailable|gateway|upstream|server error)|bad gateway|gateway time-?out|upstream (?:error|connect|timeout)/i;
36632
36654
  function looksLikeProviderOutage(text) {
36633
36655
  return PROVIDER_OUTAGE_RE.test(text);
@@ -36663,12 +36685,10 @@ ${recentStderr}`;
36663
36685
  if (looksLikeAuthFailure(haystack2)) return AUTH_FAILURE_MESSAGE;
36664
36686
  if (looksLikeProviderOutage(haystack2)) return providerOutageMessage(agent);
36665
36687
  const tail = recentStderr.split("\n").filter(Boolean).slice(-3).join("\n");
36666
- return [
36667
- `\u26A0\uFE0F The ${agent} agent failed to start.`,
36668
- "",
36669
- tail ? `Details:
36670
- ${tail}` : detail
36671
- ].join("\n");
36688
+ return [`\u26A0\uFE0F The ${agent} agent failed to start.`, "", tail ? `Details:
36689
+ ${tail}` : detail].join(
36690
+ "\n"
36691
+ );
36672
36692
  }
36673
36693
  function budgetBubbleMessage(agent, period) {
36674
36694
  return `\u{1F4B8} **Headroom budget reached for the ${period} period.**
@@ -38084,6 +38104,18 @@ async function startTaskH(ctx) {
38084
38104
  error: "agent reply reported 1M-context usage-credits gate"
38085
38105
  });
38086
38106
  return;
38107
+ } else if (reply.stopReason === "end_turn" && finalText.trim().length === 0 && !streaming.hasVisibleProgress()) {
38108
+ const bubble = emptyReplyMessage(opts.agent);
38109
+ await streaming.closeWithBubble(bubble);
38110
+ turnClosed = true;
38111
+ history.appendAgentReply(bubble);
38112
+ void history.flush();
38113
+ turnFiles.flushTurn().catch((err) => {
38114
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
38115
+ });
38116
+ log.info("acpRunner", `start_task \u2190 empty-reply id=${cmd.id.slice(0, 8)}`);
38117
+ await relay.sendResult(cmd.id, "failed", { error: "agent turn ended with an empty reply" });
38118
+ return;
38087
38119
  } else {
38088
38120
  await streaming.closeTurnWithInteractiveDetection();
38089
38121
  turnClosed = true;
@@ -38976,13 +39008,14 @@ var StreamingState = class {
38976
39008
  const cumulativeContent = reconcileCumulative(existing?.content ?? "", delta.delta);
38977
39009
  this.streamingChunks.set(chunkId, { kind: delta.kind, content: cumulativeContent });
38978
39010
  let fenceCut = -1;
39011
+ let textVisible = null;
38979
39012
  if (delta.kind === "text") {
38980
39013
  this.recomputeText();
38981
39014
  fenceCut = handoffFenceStartMasked(this.text);
38982
- const visibleText = fenceCut === -1 ? this.text : this.text.slice(0, fenceCut).trimEnd();
38983
- void this.publisher.publishOutput({ type: "text", content: visibleText, done: false });
39015
+ textVisible = fenceCut === -1 ? withholdTrailingPartialFenceMarker(this.text) : this.text.slice(0, fenceCut).trimEnd();
39016
+ void this.publisher.publishOutput({ type: "text", content: textVisible, done: false });
38984
39017
  }
38985
- const visibleChunkContent = delta.kind === "text" && fenceCut !== -1 ? cumulativeContent.slice(0, fenceCut).trimEnd() : cumulativeContent;
39018
+ const visibleChunkContent = delta.kind === "text" ? textVisible : cumulativeContent;
38986
39019
  void this.publisher.publishStreamingChunk({
38987
39020
  chunkId,
38988
39021
  kind: delta.kind,
@@ -39717,7 +39750,7 @@ async function runAcpSession(opts) {
39717
39750
  const switchDeps = {
39718
39751
  currentAgent: () => opts.agent,
39719
39752
  postEvent: emitSwitchEvent,
39720
- fetchCredential: (agentId) => fetchProvisionCredential({
39753
+ fetchCredential: (agentId) => fetchProvisionCredentialDetailed({
39721
39754
  agentId,
39722
39755
  sessionId: opts.sessionId,
39723
39756
  pluginId: opts.pluginId,
@@ -44645,7 +44678,7 @@ function checkChokidar() {
44645
44678
  }
44646
44679
  async function doctor(args2 = []) {
44647
44680
  const json = args2.includes("--json");
44648
- const cliVersion = true ? "2.65.5" : "0.0.0-dev";
44681
+ const cliVersion = true ? "2.65.7" : "0.0.0-dev";
44649
44682
  const apiBase2 = resolveApiBaseUrl();
44650
44683
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
44651
44684
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -45036,7 +45069,7 @@ async function mcpRun(args2) {
45036
45069
  // src/commands/version.ts
45037
45070
  var import_picocolors15 = __toESM(require("picocolors"));
45038
45071
  function version2() {
45039
- const v = true ? "2.65.5" : "unknown";
45072
+ const v = true ? "2.65.7" : "unknown";
45040
45073
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
45041
45074
  }
45042
45075
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.65.5",
3
+ "version": "2.65.7",
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",