codeam-cli 2.75.10 → 2.75.12

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 +12 -0
  2. package/dist/index.js +111 -19
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ 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.11] — 2026-09-24
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Bound streaming-chunk snapshots under the API cap and coalesce big re-sends
12
+
13
+ ## [2.75.10] — 2026-09-23
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Guardrails judge a write by its target; pack stage shows a mid-turn permission as awaiting
18
+
7
19
  ## [2.75.9] — 2026-09-23
8
20
 
9
21
  ### Added
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.10" : "0.0.0-dev",
10512
+ cliVersion: true ? "2.75.12" : "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.10",
10754
+ version: "2.75.12",
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.10" ? { ideVersion: "2.75.10" } : {}
12084
+ ..."2.75.12" ? { ideVersion: "2.75.12" } : {}
12085
12085
  };
12086
12086
  try {
12087
12087
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -18511,6 +18511,9 @@ function resolveHistoryDir2(_cwd, homeOverride) {
18511
18511
  if (!import_node_fs3.default.existsSync(todayDir)) return null;
18512
18512
  return todayDir;
18513
18513
  }
18514
+ function isHeadlessOneShotMeta(meta) {
18515
+ return meta.source === "exec" || meta.originator === "codex_exec";
18516
+ }
18514
18517
  function parseLine(line) {
18515
18518
  try {
18516
18519
  const parsed = JSON.parse(line);
@@ -18790,6 +18793,7 @@ function newestRolloutIdSince(sessionsRoot, resolvedCwd, floorMs) {
18790
18793
  if (mtime < floorMs || mtime <= bestMtime) continue;
18791
18794
  let metaCwd;
18792
18795
  let metaId;
18796
+ let headlessOneShot = false;
18793
18797
  try {
18794
18798
  const raw = import_node_fs3.default.readFileSync(filePath, "utf8");
18795
18799
  for (const line of raw.split("\n")) {
@@ -18800,13 +18804,14 @@ function newestRolloutIdSince(sessionsRoot, resolvedCwd, floorMs) {
18800
18804
  const meta = rec.payload;
18801
18805
  metaCwd = typeof meta?.cwd === "string" ? meta.cwd : void 0;
18802
18806
  metaId = typeof meta?.id === "string" ? meta.id : void 0;
18807
+ headlessOneShot = meta !== void 0 && isHeadlessOneShotMeta(meta);
18803
18808
  }
18804
18809
  break;
18805
18810
  }
18806
18811
  } catch {
18807
18812
  continue;
18808
18813
  }
18809
- if (!metaId || !metaCwd) continue;
18814
+ if (!metaId || !metaCwd || headlessOneShot) continue;
18810
18815
  let resolvedMeta;
18811
18816
  try {
18812
18817
  resolvedMeta = import_node_fs3.default.realpathSync(metaCwd);
@@ -27496,6 +27501,26 @@ var emitPreviewEvent = (args2) => {
27496
27501
  payload: { ...args2.payload ?? {}, missingServices: missing }
27497
27502
  });
27498
27503
  };
27504
+ var PREVIEW_DETECT_TIMEOUT_MS = 12e4;
27505
+ async function runDetectOneShot(generate, timeoutMs = PREVIEW_DETECT_TIMEOUT_MS) {
27506
+ let timer;
27507
+ const deadline = new Promise((resolve11) => {
27508
+ timer = setTimeout(() => resolve11({ raw: null, timedOut: true }), timeoutMs);
27509
+ timer.unref?.();
27510
+ });
27511
+ const oneShot = generate(PREVIEW_DETECT_PROMPT, { timeoutMs }).then(
27512
+ (raw) => ({ raw, timedOut: false }),
27513
+ (err) => {
27514
+ log.info("preview", `detect: generateOneShot threw: ${String(err)}`);
27515
+ return { raw: null, timedOut: false };
27516
+ }
27517
+ );
27518
+ try {
27519
+ return await Promise.race([oneShot, deadline]);
27520
+ } finally {
27521
+ if (timer) clearTimeout(timer);
27522
+ }
27523
+ }
27499
27524
  var requestPreviewDetectH = (ctx) => {
27500
27525
  if (!ctx.pluginAuthToken) {
27501
27526
  log.info("preview", "no pluginAuthToken \u2014 skipping detect");
@@ -27516,6 +27541,7 @@ var requestPreviewDetectH = (ctx) => {
27516
27541
  return;
27517
27542
  }
27518
27543
  const pluginAuthToken = ctx.pluginAuthToken;
27544
+ const generateOneShot = ctx.runtime.generateOneShot.bind(ctx.runtime);
27519
27545
  void (async () => {
27520
27546
  const fromFile = await readPreviewConfig(process.cwd());
27521
27547
  if (fromFile) {
@@ -27537,11 +27563,22 @@ var requestPreviewDetectH = (ctx) => {
27537
27563
  });
27538
27564
  log.info("preview", "detect: invoking generateOneShot");
27539
27565
  const startedAt = Date.now();
27540
- const raw = await ctx.runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch((err) => {
27541
- log.info("preview", `detect: generateOneShot threw: ${String(err)}`);
27542
- return null;
27543
- });
27566
+ const { raw, timedOut } = await runDetectOneShot(generateOneShot);
27544
27567
  const tookMs = Date.now() - startedAt;
27568
+ if (timedOut) {
27569
+ log.info("preview", `detect: timed out after ${tookMs}ms \u2014 emitting preview_error`);
27570
+ emitPreviewEvent({
27571
+ sessionId: ctx.sessionId,
27572
+ pluginId: ctx.pluginId,
27573
+ pluginAuthToken,
27574
+ type: USER_EVENTS.PREVIEW_ERROR,
27575
+ payload: {
27576
+ stage: "detection",
27577
+ message: `Project detection timed out after ${Math.round(PREVIEW_DETECT_TIMEOUT_MS / 1e3)} s \u2014 the agent did not answer. Try again, or add a .codeam/preview.json override.`
27578
+ }
27579
+ });
27580
+ return;
27581
+ }
27545
27582
  const detection = safeParseDetection(raw);
27546
27583
  if (!detection) {
27547
27584
  const failure = describeDetectionFailure(raw);
@@ -27622,11 +27659,12 @@ function prewarmPreviewDetection(runtime) {
27622
27659
  if (previewPrewarmStarted) return;
27623
27660
  previewPrewarmStarted = true;
27624
27661
  if (typeof runtime.generateOneShot !== "function") return;
27662
+ const generateOneShot = runtime.generateOneShot.bind(runtime);
27625
27663
  void (async () => {
27626
27664
  try {
27627
27665
  const cwd = process.cwd();
27628
27666
  if (await readPreviewConfig(cwd)) return;
27629
- const raw = await runtime.generateOneShot(PREVIEW_DETECT_PROMPT).catch(() => null);
27667
+ const { raw } = await runDetectOneShot(generateOneShot);
27630
27668
  const detection = safeParseDetection(raw);
27631
27669
  if (!detection) {
27632
27670
  const failure = describeDetectionFailure(raw);
@@ -35576,6 +35614,18 @@ function _get(url2, headers) {
35576
35614
  }
35577
35615
 
35578
35616
  // src/agents/acp/publisher.ts
35617
+ var STREAMING_CHUNK_SNAPSHOT_MAX_CHARS = 56 * 1024;
35618
+ var STREAMING_CHUNK_COALESCE_MIN_CHARS = 8 * 1024;
35619
+ var STREAMING_CHUNK_COALESCE_WINDOW_MS = 300;
35620
+ function boundStreamingChunkContent(content, max = STREAMING_CHUNK_SNAPSHOT_MAX_CHARS) {
35621
+ if (content.length <= max) return { content, truncated: false };
35622
+ const dropped = content.length - max;
35623
+ return {
35624
+ content: `${content.slice(0, max)}
35625
+ \u2026 [output truncated: ${dropped} more characters \u2014 the full text is in the conversation transcript]`,
35626
+ truncated: true
35627
+ };
35628
+ }
35579
35629
  var AcpPublisher = class {
35580
35630
  constructor(opts) {
35581
35631
  this.opts = opts;
@@ -35585,6 +35635,11 @@ var AcpPublisher = class {
35585
35635
  opts;
35586
35636
  apiBase;
35587
35637
  token;
35638
+ /** chunkIds whose truncated snapshot already went out — nothing new to show
35639
+ * until the terminal frame. */
35640
+ truncatedChunks = /* @__PURE__ */ new Set();
35641
+ lastChunkPostAt = /* @__PURE__ */ new Map();
35642
+ pendingChunkPosts = /* @__PURE__ */ new Map();
35588
35643
  /** Latched on an unrecoverable 401/403 — every surface stops posting
35589
35644
  * (2026-06-28 incident: a dead-token publisher spammed 401 ×34 while
35590
35645
  * the agent's replies silently never reached the phone). */
@@ -35613,7 +35668,10 @@ var AcpPublisher = class {
35613
35668
  const fresh = await this.opts.refreshAuthToken();
35614
35669
  if (fresh) {
35615
35670
  this.token = fresh;
35616
- log.info("acpPublisher", `plugin-auth token refreshed after ${first.statusCode}; retrying POST`);
35671
+ log.info(
35672
+ "acpPublisher",
35673
+ `plugin-auth token refreshed after ${first.statusCode}; retrying POST`
35674
+ );
35617
35675
  const second = await _transport4.post(url2, this.authHeaders(), payload);
35618
35676
  if (second.statusCode !== 401 && second.statusCode !== 403) return second;
35619
35677
  this.markPairingInvalid(second.statusCode);
@@ -35722,17 +35780,51 @@ var AcpPublisher = class {
35722
35780
  * richer SessionDetail surface.
35723
35781
  */
35724
35782
  async publishStreamingChunk(event) {
35783
+ const { chunkId, isFinal } = event;
35784
+ const pending = this.pendingChunkPosts.get(chunkId);
35785
+ if (pending) {
35786
+ clearTimeout(pending.timer);
35787
+ this.pendingChunkPosts.delete(chunkId);
35788
+ }
35789
+ const bounded = boundStreamingChunkContent(event.content);
35790
+ if (bounded.truncated) {
35791
+ if (!isFinal && this.truncatedChunks.has(chunkId)) return;
35792
+ if (!this.truncatedChunks.has(chunkId)) {
35793
+ log.warn(
35794
+ "acpPublisher",
35795
+ `streaming-chunk ${chunkId.slice(0, 12)} (${event.kind}) exceeds ${STREAMING_CHUNK_SNAPSHOT_MAX_CHARS} chars \u2014 sending a truncated snapshot once`
35796
+ );
35797
+ }
35798
+ this.truncatedChunks.add(chunkId);
35799
+ }
35800
+ if (isFinal) {
35801
+ this.truncatedChunks.delete(chunkId);
35802
+ this.lastChunkPostAt.delete(chunkId);
35803
+ } else if (!bounded.truncated && bounded.content.length >= STREAMING_CHUNK_COALESCE_MIN_CHARS) {
35804
+ const last = this.lastChunkPostAt.get(chunkId) ?? 0;
35805
+ const wait = STREAMING_CHUNK_COALESCE_WINDOW_MS - (Date.now() - last);
35806
+ if (wait > 0) {
35807
+ const timer = setTimeout(() => {
35808
+ const latest = this.pendingChunkPosts.get(chunkId);
35809
+ this.pendingChunkPosts.delete(chunkId);
35810
+ if (latest) void this.postStreamingChunk(latest.event);
35811
+ }, wait);
35812
+ this.pendingChunkPosts.set(chunkId, { timer, event });
35813
+ return;
35814
+ }
35815
+ }
35816
+ await this.postStreamingChunk({ ...event, content: bounded.content });
35817
+ }
35818
+ async postStreamingChunk(event) {
35725
35819
  const url2 = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
35820
+ if (!event.isFinal) this.lastChunkPostAt.set(event.chunkId, Date.now());
35726
35821
  try {
35727
35822
  const { statusCode, body } = await this.postWithReauth(
35728
35823
  url2,
35729
35824
  this.envelope(event)
35730
35825
  );
35731
35826
  if (statusCode < 200 || statusCode >= 300) {
35732
- log.warn(
35733
- "acpPublisher",
35734
- `streaming-chunk status=${statusCode} body=${body.slice(0, 200)}`
35735
- );
35827
+ log.warn("acpPublisher", `streaming-chunk status=${statusCode} body=${body.slice(0, 200)}`);
35736
35828
  }
35737
35829
  } catch (err) {
35738
35830
  log.trace("acpPublisher", "streaming-chunk post failed", err);
@@ -47060,7 +47152,7 @@ async function autoUpgradeBeforeCriticalCommand() {
47060
47152
  if (process.env.NODE_ENV === "test") return;
47061
47153
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47062
47154
  if (process.env.CI) return;
47063
- const current2 = true ? "2.75.10" : null;
47155
+ const current2 = true ? "2.75.12" : null;
47064
47156
  if (!current2) return;
47065
47157
  const cache = readCache();
47066
47158
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47077,7 +47169,7 @@ function checkForUpdates() {
47077
47169
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47078
47170
  if (process.env.CI) return;
47079
47171
  if (!process.stdout.isTTY) return;
47080
- const current2 = true ? "2.75.10" : null;
47172
+ const current2 = true ? "2.75.12" : null;
47081
47173
  if (!current2) return;
47082
47174
  const cache = readCache();
47083
47175
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47100,7 +47192,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
47100
47192
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
47101
47193
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
47102
47194
  function currentCliVersion() {
47103
- return true ? "2.75.10" : null;
47195
+ return true ? "2.75.12" : null;
47104
47196
  }
47105
47197
  async function installedVersion(deps) {
47106
47198
  const ls = await deps.run(
@@ -48840,7 +48932,7 @@ function checkChokidar() {
48840
48932
  }
48841
48933
  async function doctor(args2 = []) {
48842
48934
  const json = args2.includes("--json");
48843
- const cliVersion = true ? "2.75.10" : "0.0.0-dev";
48935
+ const cliVersion = true ? "2.75.12" : "0.0.0-dev";
48844
48936
  const apiBase2 = resolveApiBaseUrl();
48845
48937
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
48846
48938
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -49364,7 +49456,7 @@ async function mcpWarm(argv = [], opts = {}) {
49364
49456
  // src/commands/version.ts
49365
49457
  var import_picocolors15 = __toESM(require("picocolors"));
49366
49458
  function version2() {
49367
- const v = true ? "2.75.10" : "unknown";
49459
+ const v = true ? "2.75.12" : "unknown";
49368
49460
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
49369
49461
  }
49370
49462
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.10",
3
+ "version": "2.75.12",
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",