codeam-cli 2.75.13 → 2.75.15

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.js +109 -23
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ 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.75.14] — 2026-09-24
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** PTY helper forwards SIGTERM to the agent; kill is awaited before respawn/hand-off
12
+ - **cli:** A new ACP text message after a finished sentence starts a new paragraph
13
+
14
+ ## [2.75.13] — 2026-09-24
15
+
16
+ ### Added
17
+
18
+ - **cli:** Attribute provider 402s — routing in the debug log, BYO no-credits bubble, pack fail
19
+
20
+ ### Fixed
21
+
22
+ - **cli:** Host-agent resumes every session live at shutdown, bounded, not just the last one
23
+ - **cli:** Collapse `bd prime` tool result so its docs tail no longer shows under the reply
24
+
7
25
  ## [2.75.12] — 2026-09-24
8
26
 
9
27
  ### Fixed
package/dist/index.js CHANGED
@@ -10509,7 +10509,7 @@ function readAnonId() {
10509
10509
  }
10510
10510
  function superProperties() {
10511
10511
  return {
10512
- cliVersion: true ? "2.75.13" : "0.0.0-dev",
10512
+ cliVersion: true ? "2.75.15" : "0.0.0-dev",
10513
10513
  nodeVersion: process.version,
10514
10514
  platform: process.platform,
10515
10515
  arch: process.arch,
@@ -10751,7 +10751,7 @@ var http = __toESM(require("http"));
10751
10751
  // package.json
10752
10752
  var package_default = {
10753
10753
  name: "codeam-cli",
10754
- version: "2.75.13",
10754
+ version: "2.75.15",
10755
10755
  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.",
10756
10756
  type: "commonjs",
10757
10757
  main: "dist/index.js",
@@ -12081,7 +12081,7 @@ var CommandRelayService = class _CommandRelayService {
12081
12081
  // fresh + clear the "CLI update available" banner after a self-update
12082
12082
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12083
12083
  // pair/reconnect). Older backends ignore the extra field.
12084
- ..."2.75.13" ? { ideVersion: "2.75.13" } : {}
12084
+ ..."2.75.15" ? { ideVersion: "2.75.15" } : {}
12085
12085
  };
12086
12086
  try {
12087
12087
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -16510,9 +16510,13 @@ def onwinch(n,f):
16510
16510
  sz=os.get_terminal_size(2)
16511
16511
  fcntl.ioctl(m,termios.TIOCSWINSZ,struct.pack('HHHH',sz.lines,sz.columns,0,0))
16512
16512
  except Exception:pass
16513
+ def onterm(n,f):
16514
+ done[0]=True
16513
16515
  signal.signal(signal.SIGCHLD,onchld)
16514
16516
  signal.signal(signal.SIGWINCH,onwinch)
16515
16517
  signal.signal(signal.SIGHUP,signal.SIG_IGN)
16518
+ signal.signal(signal.SIGTERM,onterm)
16519
+ signal.signal(signal.SIGINT,onterm)
16516
16520
  i=sys.stdin.fileno()
16517
16521
  o=sys.stdout.fileno()
16518
16522
  while not done[0]:
@@ -16533,10 +16537,20 @@ while not done[0]:
16533
16537
  except OSError:done[0]=True
16534
16538
  try:os.kill(pid,signal.SIGTERM)
16535
16539
  except Exception:pass
16536
- try:
16537
- _,st=os.waitpid(pid,0)
16538
- sys.exit((st>>8)&0xFF)
16539
- except Exception:sys.exit(0)
16540
+ import time
16541
+ t0=time.time()
16542
+ while True:
16543
+ try:w,st=os.waitpid(pid,os.WNOHANG)
16544
+ except ChildProcessError:sys.exit(0)
16545
+ except Exception:sys.exit(0)
16546
+ if w==pid:sys.exit((st>>8)&0xFF)
16547
+ if time.time()-t0>5:
16548
+ try:os.kill(pid,signal.SIGKILL)
16549
+ except Exception:pass
16550
+ try:os.waitpid(pid,0)
16551
+ except Exception:pass
16552
+ sys.exit(137)
16553
+ time.sleep(0.05)
16540
16554
  `;
16541
16555
  var UnixPtyStrategy = class {
16542
16556
  constructor(opts) {
@@ -16653,11 +16667,42 @@ var UnixPtyStrategy = class {
16653
16667
  this.proc?.stdin?.write(data);
16654
16668
  }
16655
16669
  kill() {
16670
+ void this.killAndWait();
16671
+ }
16672
+ /**
16673
+ * SIGTERM the helper and resolve once it has EXITED (bounded; SIGKILL
16674
+ * fallback). The helper forwards the signal to the agent and waits for it,
16675
+ * so when this resolves the agent process is gone too.
16676
+ *
16677
+ * ⚠️ Why awaited (2026-09-24): the helper used to die under SIGTERM with no
16678
+ * handler, leaving the agent orphaned; on a baton hand-off and in the
16679
+ * baton integration suite the orphaned Claude exited a beat later and
16680
+ * rewrote `~/.claude.json` from memory, clobbering the just-written
16681
+ * workspace-trust entry for the NEXT session — which then wedged on the
16682
+ * trust dialog (CI run 35946818546). Callers that start a new agent or a
16683
+ * new session must `await` this.
16684
+ */
16685
+ async killAndWait(timeoutMs = 6e3) {
16656
16686
  const proc = this.proc;
16657
16687
  this.proc = null;
16658
16688
  if (proc) {
16659
16689
  proc.removeAllListeners("exit");
16660
- proc.kill();
16690
+ const exited = new Promise((resolve12) => {
16691
+ if (proc.exitCode !== null || proc.signalCode !== null) return resolve12();
16692
+ proc.once("exit", () => resolve12());
16693
+ });
16694
+ proc.kill("SIGTERM");
16695
+ const timer = new Promise((resolve12) => {
16696
+ const t2 = setTimeout(() => resolve12("timeout"), timeoutMs);
16697
+ void exited.then(() => clearTimeout(t2));
16698
+ });
16699
+ if (await Promise.race([exited, timer]) === "timeout") {
16700
+ try {
16701
+ proc.kill("SIGKILL");
16702
+ } catch {
16703
+ }
16704
+ await Promise.race([exited, new Promise((r) => setTimeout(r, 1e3))]);
16705
+ }
16661
16706
  }
16662
16707
  this.removeTempFile();
16663
16708
  this.dispose();
@@ -16847,6 +16892,9 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
16847
16892
  if (!this.pty) return;
16848
16893
  this.pty.write(typeof data === "string" ? data : data.toString("utf8"));
16849
16894
  }
16895
+ async killAndWait() {
16896
+ this.kill();
16897
+ }
16850
16898
  kill() {
16851
16899
  try {
16852
16900
  this.pty?.kill();
@@ -16927,6 +16975,9 @@ var WindowsPtyStrategy = class {
16927
16975
  this.proc?.kill();
16928
16976
  this.dispose();
16929
16977
  }
16978
+ async killAndWait() {
16979
+ this.kill();
16980
+ }
16930
16981
  dispose() {
16931
16982
  process.stdin.removeListener("data", this.stdinHandler);
16932
16983
  if (process.stdin.isTTY) {
@@ -26515,7 +26566,7 @@ var resumeSession = async (ctx, _cmd, parsed) => {
26515
26566
  ctx.historySvc.setCurrentConversationId(id);
26516
26567
  await ctx.historySvc.loadConversation(id);
26517
26568
  await ctx.outputSvc.newTurnResume(id);
26518
- ctx.agent.restart(id, auto ?? false);
26569
+ void ctx.agent.restart(id, auto ?? false);
26519
26570
  };
26520
26571
  var getContext = async (ctx, cmd) => {
26521
26572
  const usage = ctx.historySvc.getCurrentUsage();
@@ -28748,6 +28799,10 @@ var AgentService = class _AgentService {
28748
28799
  kill() {
28749
28800
  this.strategy?.kill();
28750
28801
  }
28802
+ /** Kill and resolve once the agent process is gone (see `IPtyStrategy.killAndWait`). */
28803
+ async killAndWait() {
28804
+ await this.strategy?.killAndWait();
28805
+ }
28751
28806
  /**
28752
28807
  * Kill the current Claude process and relaunch it resuming the given session.
28753
28808
  * Pass auto=true to add --dangerously-skip-permissions (no confirmation prompts).
@@ -28757,7 +28812,7 @@ var AgentService = class _AgentService {
28757
28812
  * For agents that use a post-spawn PTY instruction (e.g. Codex), `resumeLaunchArgs`
28758
28813
  * returns [] and `postSpawnInstruction` types the resume command into the PTY.
28759
28814
  */
28760
- restart(sessionId, auto = false) {
28815
+ async restart(sessionId, auto = false) {
28761
28816
  if (!this.strategy || !this.initialLaunch) return;
28762
28817
  const resumeArgs = this.runtime.resumeLaunchArgs(sessionId, { auto });
28763
28818
  const launch = this.runtime.prepareResumeLaunch?.(sessionId, { auto }) ?? {
@@ -28769,7 +28824,7 @@ var AgentService = class _AgentService {
28769
28824
  clearTimeout(this.quietTimer);
28770
28825
  this.quietTimer = null;
28771
28826
  }
28772
- this.strategy.kill();
28827
+ await this.strategy.killAndWait();
28773
28828
  this.strategy.spawn(launch.cmd, this.opts.cwd, launch.args);
28774
28829
  if (resumeArgs.length === 0 && this.runtime.postSpawnInstruction) {
28775
28830
  const { ptyInput } = this.runtime.postSpawnInstruction(sessionId);
@@ -37610,7 +37665,7 @@ function replyIsHouseAgentLimit(finalText) {
37610
37665
  const t2 = finalText.trim();
37611
37666
  return t2.length > 0 && t2.length <= 400 && looksLikeHouseAgentLimit(t2);
37612
37667
  }
37613
- var BYO_PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b[^\n]{0,80}insufficient (?:credits?|balance)|\b402\b[^\n]{0,40}insufficient (?:credits?|balance)|insufficient (?:credits?|balance)[^\n]{0,40}\b402\b/i;
37668
+ var BYO_PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b[^\n]{0,80}(?:insufficient (?:credits?|balance)|requires more credits)|\b402\b[^\n]{0,40}(?:insufficient (?:credits?|balance)|requires more credits)|insufficient (?:credits?|balance)[^\n]{0,40}\b402\b/i;
37614
37669
  function looksLikeByoProviderBilling(text, env = process.env) {
37615
37670
  return !isHouseProxyEnv(env) && BYO_PROVIDER_BILLING_RE.test(text);
37616
37671
  }
@@ -40507,6 +40562,16 @@ var StreamingState = class {
40507
40562
  * no matter how many message ids the adapter spreads it across.
40508
40563
  */
40509
40564
  turnTextChunkId = null;
40565
+ /**
40566
+ * Adapter message id of the LAST text delta appended this turn. A text delta
40567
+ * under a NEW message id that appends (not a snapshot re-emit) is a new
40568
+ * paragraph — Codex emits several `agent_message_chunk` messages per turn
40569
+ * around tool calls ('…run every gate.' → tools → 'I'm resuming at…') and,
40570
+ * collapsed onto `turnTextChunkId`, they glued into 'gate.I'm resuming'
40571
+ * (replay 2026-09-23, bead codeagent-wqpi). Both feeds carry the joined
40572
+ * string, so the boundary can only be restored here.
40573
+ */
40574
+ lastTextMessageId = null;
40510
40575
  /**
40511
40576
  * Stable chunkId that ALL `thinking` segments of the current turn collapse
40512
40577
  * onto (set to the first thought chunk's id, reset each {@link beginTurn}) —
@@ -40675,6 +40740,7 @@ var StreamingState = class {
40675
40740
  this.text = "";
40676
40741
  this.streamingChunks.clear();
40677
40742
  this.turnTextChunkId = null;
40743
+ this.lastTextMessageId = null;
40678
40744
  this.turnThoughtChunkId = null;
40679
40745
  this.turnSeq += 1;
40680
40746
  if (this.pending?.kind === "permission") {
@@ -40707,7 +40773,18 @@ var StreamingState = class {
40707
40773
  `streaming-chunk kind flip chunkId=${chunkId.slice(0, 8)} from=${existing.kind} to=${delta.kind}`
40708
40774
  );
40709
40775
  }
40710
- const cumulativeContent = reconcileCumulative(existing?.content ?? "", delta.delta);
40776
+ const prior = existing?.content ?? "";
40777
+ let cumulativeContent = reconcileCumulative(prior, delta.delta);
40778
+ if (delta.kind === "text") {
40779
+ const newMessage = this.lastTextMessageId !== null && this.lastTextMessageId !== delta.chunkId;
40780
+ const appended = cumulativeContent === prior + delta.delta;
40781
+ if (newMessage && appended && /\S$/.test(prior) && !/^\s/.test(delta.delta)) {
40782
+ cumulativeContent = `${prior}
40783
+
40784
+ ${delta.delta}`;
40785
+ }
40786
+ this.lastTextMessageId = delta.chunkId;
40787
+ }
40711
40788
  this.streamingChunks.set(chunkId, { kind: delta.kind, content: cumulativeContent });
40712
40789
  let fenceCut = -1;
40713
40790
  let textVisible = null;
@@ -43181,7 +43258,7 @@ var NativeTuiDriver = class _NativeTuiDriver {
43181
43258
  async stop() {
43182
43259
  this.unwatchSwitch?.();
43183
43260
  this.unwatchSwitch = null;
43184
- this.agent.kill();
43261
+ await this.agent.killAndWait();
43185
43262
  parkTerminalForReadonly();
43186
43263
  }
43187
43264
  async dispatch(cmd) {
@@ -43733,7 +43810,7 @@ async function runBatonSession(opts) {
43733
43810
  process.removeListener("SIGTERM", onSignal);
43734
43811
  process.removeListener("SIGHUP", onSignal);
43735
43812
  mirror?.stop();
43736
- void controller.shutdown();
43813
+ await controller.shutdown();
43737
43814
  await stopRelayWithGoodbye(relay);
43738
43815
  }
43739
43816
  const onSignal = () => {
@@ -44265,9 +44342,18 @@ var import_picocolors4 = __toESM(require("picocolors"));
44265
44342
 
44266
44343
  // src/lib/agent-prompt.ts
44267
44344
  function parseAgentFlag(args2) {
44268
- const flag = args2.find((a) => a.startsWith("--agent="));
44269
- if (!flag) return null;
44270
- const value = flag.slice("--agent=".length);
44345
+ let value;
44346
+ const eq = args2.find((a) => a.startsWith("--agent="));
44347
+ if (eq) {
44348
+ value = eq.slice("--agent=".length);
44349
+ } else {
44350
+ const i = args2.indexOf("--agent");
44351
+ if (i === -1) return null;
44352
+ value = args2[i + 1];
44353
+ if (!value || value.startsWith("-")) {
44354
+ throw new Error(`--agent needs a value; valid: ${Object.keys(AGENT_REGISTRY).join(", ")}`);
44355
+ }
44356
+ }
44271
44357
  if (!isKnownAgentId(value)) {
44272
44358
  throw new Error(
44273
44359
  `invalid agent "${value}"; valid: ${Object.keys(AGENT_REGISTRY).join(", ")}`
@@ -47512,7 +47598,7 @@ async function autoUpgradeBeforeCriticalCommand() {
47512
47598
  if (process.env.NODE_ENV === "test") return;
47513
47599
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47514
47600
  if (process.env.CI) return;
47515
- const current2 = true ? "2.75.13" : null;
47601
+ const current2 = true ? "2.75.15" : null;
47516
47602
  if (!current2) return;
47517
47603
  const cache = readCache();
47518
47604
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47529,7 +47615,7 @@ function checkForUpdates() {
47529
47615
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47530
47616
  if (process.env.CI) return;
47531
47617
  if (!process.stdout.isTTY) return;
47532
- const current2 = true ? "2.75.13" : null;
47618
+ const current2 = true ? "2.75.15" : null;
47533
47619
  if (!current2) return;
47534
47620
  const cache = readCache();
47535
47621
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47552,7 +47638,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
47552
47638
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
47553
47639
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
47554
47640
  function currentCliVersion() {
47555
- return true ? "2.75.13" : null;
47641
+ return true ? "2.75.15" : null;
47556
47642
  }
47557
47643
  async function installedVersion(deps) {
47558
47644
  const ls = await deps.run(
@@ -49413,7 +49499,7 @@ function checkChokidar() {
49413
49499
  }
49414
49500
  async function doctor(args2 = []) {
49415
49501
  const json = args2.includes("--json");
49416
- const cliVersion = true ? "2.75.13" : "0.0.0-dev";
49502
+ const cliVersion = true ? "2.75.15" : "0.0.0-dev";
49417
49503
  const apiBase2 = resolveApiBaseUrl();
49418
49504
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
49419
49505
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -49937,7 +50023,7 @@ async function mcpWarm(argv = [], opts = {}) {
49937
50023
  // src/commands/version.ts
49938
50024
  var import_picocolors15 = __toESM(require("picocolors"));
49939
50025
  function version2() {
49940
- const v = true ? "2.75.13" : "unknown";
50026
+ const v = true ? "2.75.15" : "unknown";
49941
50027
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
49942
50028
  }
49943
50029
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.13",
3
+ "version": "2.75.15",
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",