codeam-cli 2.27.7 → 2.27.8

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.27.7] — 2026-06-06
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** ACP — wire-level instrumentation + prompt timeout
12
+
7
13
  ## [2.27.6] — 2026-06-06
8
14
 
9
15
  ### Fixed
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.27.7",
501
+ version: "2.27.8",
502
502
  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.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -889,8 +889,8 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
889
889
  (res) => {
890
890
  res.on("error", reject);
891
891
  let responseBody = "";
892
- res.on("data", (chunk2) => {
893
- responseBody += chunk2.toString();
892
+ res.on("data", (chunk) => {
893
+ responseBody += chunk.toString();
894
894
  });
895
895
  res.on("end", () => {
896
896
  if (res.statusCode && res.statusCode >= 400) {
@@ -937,8 +937,8 @@ async function _postJson(url, body) {
937
937
  (res) => {
938
938
  res.on("error", reject);
939
939
  let body2 = "";
940
- res.on("data", (chunk2) => {
941
- body2 += chunk2.toString();
940
+ res.on("data", (chunk) => {
941
+ body2 += chunk.toString();
942
942
  });
943
943
  res.on("end", () => {
944
944
  if (res.statusCode && res.statusCode >= 400) {
@@ -978,8 +978,8 @@ async function _getJson(url) {
978
978
  (res) => {
979
979
  res.on("error", reject);
980
980
  let body = "";
981
- res.on("data", (chunk2) => {
982
- body += chunk2.toString();
981
+ res.on("data", (chunk) => {
982
+ body += chunk.toString();
983
983
  });
984
984
  res.on("end", () => {
985
985
  if (res.statusCode && res.statusCode >= 400) {
@@ -5873,7 +5873,7 @@ function readAnonId() {
5873
5873
  }
5874
5874
  function superProperties() {
5875
5875
  return {
5876
- cliVersion: true ? "2.27.7" : "0.0.0-dev",
5876
+ cliVersion: true ? "2.27.8" : "0.0.0-dev",
5877
5877
  nodeVersion: process.version,
5878
5878
  platform: process.platform,
5879
5879
  arch: process.arch,
@@ -6080,9 +6080,9 @@ var CommandRelayService = class {
6080
6080
  this.armSseWatchdog();
6081
6081
  let buffer = "";
6082
6082
  res.setEncoding("utf8");
6083
- res.on("data", (chunk2) => {
6083
+ res.on("data", (chunk) => {
6084
6084
  this.sseLastByteAt = Date.now();
6085
- buffer += chunk2;
6085
+ buffer += chunk;
6086
6086
  let frameEnd;
6087
6087
  while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
6088
6088
  const frame = buffer.slice(0, frameEnd);
@@ -6751,9 +6751,9 @@ var UnixPtyStrategy = class {
6751
6751
  );
6752
6752
  process.exit(1);
6753
6753
  });
6754
- this.proc.stdout?.on("data", (chunk2) => {
6755
- process.stdout.write(chunk2);
6756
- this.opts.onData(chunk2.toString("utf8"));
6754
+ this.proc.stdout?.on("data", (chunk) => {
6755
+ process.stdout.write(chunk);
6756
+ this.opts.onData(chunk.toString("utf8"));
6757
6757
  });
6758
6758
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
6759
6759
  process.stdin.resume();
@@ -6845,8 +6845,8 @@ var UnixPtyStrategy = class {
6845
6845
  }
6846
6846
  }
6847
6847
  }
6848
- stdinHandler = (chunk2) => {
6849
- this.proc?.stdin?.write(chunk2);
6848
+ stdinHandler = (chunk) => {
6849
+ this.proc?.stdin?.write(chunk);
6850
6850
  };
6851
6851
  handleResize = () => {
6852
6852
  if (this.proc?.pid) {
@@ -7032,8 +7032,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
7032
7032
  }
7033
7033
  this.pty = null;
7034
7034
  }
7035
- stdinHandler = (chunk2) => {
7036
- this.pty?.write(chunk2.toString("utf8"));
7035
+ stdinHandler = (chunk) => {
7036
+ this.pty?.write(chunk.toString("utf8"));
7037
7037
  };
7038
7038
  };
7039
7039
 
@@ -7066,9 +7066,9 @@ var WindowsPtyStrategy = class {
7066
7066
  );
7067
7067
  process.exit(1);
7068
7068
  });
7069
- this.proc.stdout?.on("data", (chunk2) => {
7070
- process.stdout.write(chunk2);
7071
- this.opts.onData(chunk2.toString("utf8"));
7069
+ this.proc.stdout?.on("data", (chunk) => {
7070
+ process.stdout.write(chunk);
7071
+ this.opts.onData(chunk.toString("utf8"));
7072
7072
  });
7073
7073
  this.proc.stdin?.write("");
7074
7074
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
@@ -7095,8 +7095,8 @@ var WindowsPtyStrategy = class {
7095
7095
  }
7096
7096
  }
7097
7097
  }
7098
- stdinHandler = (chunk2) => {
7099
- this.proc?.stdin?.write(chunk2);
7098
+ stdinHandler = (chunk) => {
7099
+ this.proc?.stdin?.write(chunk);
7100
7100
  };
7101
7101
  };
7102
7102
 
@@ -9379,8 +9379,8 @@ async function fetchClaudeQuota() {
9379
9379
  });
9380
9380
  let output = "";
9381
9381
  let resolved = false;
9382
- proc.stdout?.on("data", (chunk2) => {
9383
- output += chunk2.toString("utf8");
9382
+ proc.stdout?.on("data", (chunk) => {
9383
+ output += chunk.toString("utf8");
9384
9384
  });
9385
9385
  setTimeout(() => {
9386
9386
  proc.stdin?.write("/usage\r");
@@ -9453,11 +9453,11 @@ async function spawnAndCapture(cmd, args2, opts = {}) {
9453
9453
  }
9454
9454
  activeChildren.add(child);
9455
9455
  let stdout = "";
9456
- child.stdout?.on("data", (chunk2) => {
9457
- stdout += chunk2.toString("utf8");
9456
+ child.stdout?.on("data", (chunk) => {
9457
+ stdout += chunk.toString("utf8");
9458
9458
  });
9459
- child.stderr?.on("data", (chunk2) => {
9460
- opts.onStderr?.(chunk2.toString("utf8"));
9459
+ child.stderr?.on("data", (chunk) => {
9460
+ opts.onStderr?.(chunk.toString("utf8"));
9461
9461
  });
9462
9462
  const timer = setTimeout(() => {
9463
9463
  try {
@@ -12091,8 +12091,8 @@ var AcpClient = class {
12091
12091
  });
12092
12092
  this.child = child;
12093
12093
  child.stderr?.setEncoding("utf8");
12094
- child.stderr?.on("data", (chunk2) => {
12095
- for (const line of chunk2.split(/\r?\n/)) {
12094
+ child.stderr?.on("data", (chunk) => {
12095
+ for (const line of chunk.split(/\r?\n/)) {
12096
12096
  const trimmed = line.trim();
12097
12097
  if (trimmed) {
12098
12098
  log.info("acpAdapter", trimmed);
@@ -12483,17 +12483,17 @@ function mapSessionUpdate(notification) {
12483
12483
  case "agent_message_chunk": {
12484
12484
  const text = extractText2(update.content);
12485
12485
  if (!text) return [];
12486
- return [chunk(messageChunkId(update.messageId), "text", text)];
12486
+ return [{ chunkId: messageChunkId(update.messageId), kind: "text", delta: text }];
12487
12487
  }
12488
12488
  case "agent_thought_chunk": {
12489
12489
  const text = extractText2(update.content);
12490
12490
  if (!text) return [];
12491
- return [chunk(messageChunkId(update.messageId), "thinking", text)];
12491
+ return [{ chunkId: messageChunkId(update.messageId), kind: "thinking", delta: text }];
12492
12492
  }
12493
12493
  case "tool_call": {
12494
12494
  const summary = describeToolCall(update);
12495
12495
  if (!summary) return [];
12496
- return [chunk(update.toolCallId, "tool_use", summary)];
12496
+ return [{ chunkId: update.toolCallId, kind: "tool_use", delta: summary }];
12497
12497
  }
12498
12498
  case "tool_call_update": {
12499
12499
  if (update.status !== "completed" && update.status !== "failed") {
@@ -12502,7 +12502,7 @@ function mapSessionUpdate(notification) {
12502
12502
  const body = describeToolCallUpdate(update);
12503
12503
  if (!body) return [];
12504
12504
  const prefix = update.status === "failed" ? "[failed] " : "";
12505
- return [chunk(update.toolCallId, "tool_result", prefix + body)];
12505
+ return [{ chunkId: update.toolCallId, kind: "tool_result", delta: prefix + body }];
12506
12506
  }
12507
12507
  case "user_message_chunk":
12508
12508
  return [];
@@ -12541,9 +12541,6 @@ function mapPermissionRequest(request) {
12541
12541
  kindByLabel
12542
12542
  };
12543
12543
  }
12544
- function chunk(chunkId, kind, content) {
12545
- return { chunkId, kind, content, isFinal: true };
12546
- }
12547
12544
  function messageChunkId(messageId) {
12548
12545
  if (typeof messageId === "string" && messageId.length > 0) return messageId;
12549
12546
  return (0, import_node_crypto5.randomUUID)();
@@ -12609,6 +12606,50 @@ function humanizeKind(kind) {
12609
12606
  }
12610
12607
 
12611
12608
  // src/agents/acp/runner.ts
12609
+ var StreamingState = class {
12610
+ constructor(publisher) {
12611
+ this.publisher = publisher;
12612
+ }
12613
+ publisher;
12614
+ open = /* @__PURE__ */ new Map();
12615
+ append(delta) {
12616
+ const existing = this.open.get(delta.chunkId);
12617
+ const content = (existing?.content ?? "") + delta.delta;
12618
+ if (existing && existing.kind !== delta.kind) {
12619
+ log.warn(
12620
+ "acpRunner",
12621
+ `chunk kind flip detected chunkId=${delta.chunkId.slice(0, 8)} from=${existing.kind} to=${delta.kind} \u2014 using new kind`
12622
+ );
12623
+ }
12624
+ this.open.set(delta.chunkId, { kind: delta.kind, content });
12625
+ void this.publisher.publishChunk({ chunkId: delta.chunkId, kind: delta.kind, content, isFinal: false }).catch((err) => {
12626
+ log.warn(
12627
+ "acpRunner",
12628
+ `publishChunk (streaming) failed: ${err instanceof Error ? err.message : String(err)}`
12629
+ );
12630
+ });
12631
+ }
12632
+ /**
12633
+ * Close every open chunk with `isFinal: true`. Idempotent — safe
12634
+ * to call multiple times per turn (e.g. once on prompt-completed,
12635
+ * once on cancel, once on adapter-exit).
12636
+ */
12637
+ async closeAll() {
12638
+ if (this.open.size === 0) return;
12639
+ const closing = Array.from(this.open.entries());
12640
+ this.open.clear();
12641
+ await Promise.all(
12642
+ closing.map(
12643
+ ([chunkId, { kind, content }]) => this.publisher.publishChunk({ chunkId, kind, content, isFinal: true }).catch((err) => {
12644
+ log.warn(
12645
+ "acpRunner",
12646
+ `publishChunk (closing) failed: ${err instanceof Error ? err.message : String(err)}`
12647
+ );
12648
+ })
12649
+ )
12650
+ );
12651
+ }
12652
+ };
12612
12653
  var ANSWER_POLL_MS = 1500;
12613
12654
  var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
12614
12655
  async function runAcpSession(opts) {
@@ -12617,6 +12658,7 @@ async function runAcpSession(opts) {
12617
12658
  pluginId: opts.pluginId,
12618
12659
  pluginAuthToken: opts.pluginAuthToken
12619
12660
  });
12661
+ const streaming = new StreamingState(publisher);
12620
12662
  let updateCount = 0;
12621
12663
  const client2 = new AcpClient({
12622
12664
  adapter: opts.adapter,
@@ -12624,15 +12666,13 @@ async function runAcpSession(opts) {
12624
12666
  onSessionUpdate: (notification) => {
12625
12667
  updateCount += 1;
12626
12668
  const variant = notification.update?.sessionUpdate ?? "unknown";
12627
- const chunks = mapSessionUpdate(notification);
12669
+ const deltas = mapSessionUpdate(notification);
12628
12670
  log.info(
12629
12671
  "acpRunner",
12630
- `update #${updateCount} variant=${variant} mappedChunks=${chunks.length}`
12672
+ `update #${updateCount} variant=${variant} mappedDeltas=${deltas.length}`
12631
12673
  );
12632
- for (const chunk2 of chunks) {
12633
- void publisher.publishChunk(chunk2).catch((err) => {
12634
- log.warn("acpRunner", `publishChunk failed: ${err instanceof Error ? err.message : String(err)}`);
12635
- });
12674
+ for (const delta of deltas) {
12675
+ streaming.append(delta);
12636
12676
  }
12637
12677
  },
12638
12678
  onRequestPermission: async (request) => {
@@ -12656,6 +12696,7 @@ async function runAcpSession(opts) {
12656
12696
  },
12657
12697
  onUnexpectedExit: (code, signal) => {
12658
12698
  log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
12699
+ void streaming.closeAll();
12659
12700
  void publisher.publishChunk({
12660
12701
  chunkId: (0, import_node_crypto6.randomUUID)(),
12661
12702
  kind: "text",
@@ -12677,7 +12718,7 @@ async function runAcpSession(opts) {
12677
12718
  const relay = new CommandRelayService(
12678
12719
  opts.pluginId,
12679
12720
  async (cmd) => {
12680
- await handleCommand(cmd, client2, relay, acpSessionId, models);
12721
+ await handleCommand(cmd, client2, relay, acpSessionId, models, streaming);
12681
12722
  },
12682
12723
  { id: opts.agent, name: opts.agent, displayName: opts.agent }
12683
12724
  );
@@ -12694,7 +12735,7 @@ async function runAcpSession(opts) {
12694
12735
  await new Promise(() => {
12695
12736
  });
12696
12737
  }
12697
- async function handleCommand(cmd, client2, relay, acpSessionId, models) {
12738
+ async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming) {
12698
12739
  switch (cmd.type) {
12699
12740
  case "start_task": {
12700
12741
  const payload = cmd.payload;
@@ -12707,9 +12748,11 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
12707
12748
  log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
12708
12749
  try {
12709
12750
  const reply = await client2.prompt(prompt);
12751
+ await streaming.closeAll();
12710
12752
  log.info("acpRunner", `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`);
12711
12753
  await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
12712
12754
  } catch (err) {
12755
+ await streaming.closeAll();
12713
12756
  log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
12714
12757
  await relay.sendResult(cmd.id, "failed", { error: describeError(err) });
12715
12758
  }
@@ -12719,6 +12762,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models) {
12719
12762
  case "escape_key": {
12720
12763
  try {
12721
12764
  await client2.cancel();
12765
+ await streaming.closeAll();
12722
12766
  await relay.sendResult(cmd.id, "completed", {});
12723
12767
  } catch (err) {
12724
12768
  log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
@@ -15188,13 +15232,13 @@ var TurnFileAggregator = class {
15188
15232
  return;
15189
15233
  }
15190
15234
  const chunks = chunkArray(novel, MAX_BATCH_SIZE);
15191
- for (const chunk2 of chunks) {
15235
+ for (const chunk of chunks) {
15192
15236
  const entry = {
15193
15237
  turnId: (0, import_crypto2.randomUUID)(),
15194
15238
  sessionId: this.opts.sessionId,
15195
15239
  pluginId: this.opts.pluginId,
15196
15240
  enqueuedAt: Date.now(),
15197
- files: chunk2
15241
+ files: chunk
15198
15242
  };
15199
15243
  await this.outbox.enqueue(entry);
15200
15244
  }
@@ -15355,11 +15399,11 @@ var StreamingEmitterService = class {
15355
15399
  }
15356
15400
  if (this.activeChunk) {
15357
15401
  const finalContent = this.activeChunk.currentContent;
15358
- const chunk2 = this.activeChunk;
15402
+ const chunk = this.activeChunk;
15359
15403
  this.activeChunk = null;
15360
15404
  await this.postChunk({
15361
- chunkId: chunk2.chunkId,
15362
- kind: chunk2.kind,
15405
+ chunkId: chunk.chunkId,
15406
+ kind: chunk.kind,
15363
15407
  content: finalContent,
15364
15408
  isFinal: true
15365
15409
  });
@@ -15431,17 +15475,17 @@ var StreamingEmitterService = class {
15431
15475
  this.maybeFlushActive(false);
15432
15476
  }
15433
15477
  maybeFlushActive(force) {
15434
- const chunk2 = this.activeChunk;
15435
- if (!chunk2) return;
15478
+ const chunk = this.activeChunk;
15479
+ if (!chunk) return;
15436
15480
  const now = Date.now();
15437
- if (!force && now - chunk2.lastEmitAt < TICK_MS) return;
15438
- if (chunk2.currentContent === chunk2.emittedContent) return;
15439
- chunk2.emittedContent = chunk2.currentContent;
15440
- chunk2.lastEmitAt = now;
15481
+ if (!force && now - chunk.lastEmitAt < TICK_MS) return;
15482
+ if (chunk.currentContent === chunk.emittedContent) return;
15483
+ chunk.emittedContent = chunk.currentContent;
15484
+ chunk.lastEmitAt = now;
15441
15485
  void this.postChunk({
15442
- chunkId: chunk2.chunkId,
15443
- kind: chunk2.kind,
15444
- content: chunk2.currentContent,
15486
+ chunkId: chunk.chunkId,
15487
+ kind: chunk.kind,
15488
+ content: chunk.currentContent,
15445
15489
  isFinal: false
15446
15490
  });
15447
15491
  }
@@ -17978,8 +18022,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
17978
18022
  let readyMatched = false;
17979
18023
  let expoUrl = null;
17980
18024
  const readyRe = new RegExp(detection.ready_pattern);
17981
- const onChunk = (chunk2) => {
17982
- const s = chunk2.toString();
18025
+ const onChunk = (chunk) => {
18026
+ const s = chunk.toString();
17983
18027
  if (!readyMatched && readyRe.test(s)) readyMatched = true;
17984
18028
  if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
17985
18029
  };
@@ -18102,8 +18146,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
18102
18146
  stdio: ["ignore", "pipe", "pipe"]
18103
18147
  });
18104
18148
  let parsedUrl = null;
18105
- const onTunnelChunk = (chunk2) => {
18106
- const s = chunk2.toString();
18149
+ const onTunnelChunk = (chunk) => {
18150
+ const s = chunk.toString();
18107
18151
  if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
18108
18152
  const trimmed = s.replace(/\n+$/g, "");
18109
18153
  if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
@@ -18202,8 +18246,8 @@ function runOnce(cmd, args2, cwd, env) {
18202
18246
  stdio: ["ignore", "pipe", "pipe"]
18203
18247
  });
18204
18248
  const tag = `setup:${cmd}`;
18205
- const onChunk = (chunk2) => {
18206
- const text = chunk2.toString().replace(/\n+$/g, "");
18249
+ const onChunk = (chunk) => {
18250
+ const text = chunk.toString().replace(/\n+$/g, "");
18207
18251
  if (text.length === 0) return;
18208
18252
  log.info("preview", `${tag}: ${text}`);
18209
18253
  };
@@ -21230,7 +21274,7 @@ function checkChokidar() {
21230
21274
  }
21231
21275
  async function doctor(args2 = []) {
21232
21276
  const json = args2.includes("--json");
21233
- const cliVersion = true ? "2.27.7" : "0.0.0-dev";
21277
+ const cliVersion = true ? "2.27.8" : "0.0.0-dev";
21234
21278
  const apiBase = resolveApiBaseUrl();
21235
21279
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
21236
21280
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -21429,7 +21473,7 @@ async function completion(args2) {
21429
21473
  // src/commands/version.ts
21430
21474
  var import_picocolors13 = __toESM(require("picocolors"));
21431
21475
  function version2() {
21432
- const v = true ? "2.27.7" : "unknown";
21476
+ const v = true ? "2.27.8" : "unknown";
21433
21477
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
21434
21478
  }
21435
21479
 
@@ -21616,8 +21660,8 @@ function fetchLatest() {
21616
21660
  }
21617
21661
  let buf = "";
21618
21662
  res.setEncoding("utf8");
21619
- res.on("data", (chunk2) => {
21620
- buf += chunk2;
21663
+ res.on("data", (chunk) => {
21664
+ buf += chunk;
21621
21665
  });
21622
21666
  res.on("end", () => {
21623
21667
  try {
@@ -21657,7 +21701,7 @@ function checkForUpdates() {
21657
21701
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21658
21702
  if (process.env.CI) return;
21659
21703
  if (!process.stdout.isTTY) return;
21660
- const current = true ? "2.27.7" : null;
21704
+ const current = true ? "2.27.8" : null;
21661
21705
  if (!current) return;
21662
21706
  const cache = readCache();
21663
21707
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.27.7",
3
+ "version": "2.27.8",
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",