codeam-cli 2.75.9 → 2.75.10

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.75.9] — 2026-09-23
8
+
9
+ ### Added
10
+
11
+ - **cli:** Agent packs — structured Reviewer findings, in-place resume, rehydration after restart
12
+
7
13
  ## [2.75.8] — 2026-09-23
8
14
 
9
15
  ### 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.9" : "0.0.0-dev",
10512
+ cliVersion: true ? "2.75.10" : "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.9",
10754
+ version: "2.75.10",
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.9" ? { ideVersion: "2.75.9" } : {}
12084
+ ..."2.75.10" ? { ideVersion: "2.75.10" } : {}
12085
12085
  };
12086
12086
  try {
12087
12087
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -33795,7 +33795,7 @@ function internalPathPermissionOutcome(request2, homeDir2) {
33795
33795
  var INTERNAL_BLOCK_REASON = "This path is a CodeAgent platform internal (CodeAgent's own runtime plumbing, not part of the user's project) and is off-limits. Continue with the user's own project files.";
33796
33796
 
33797
33797
  // src/agents/acp/guardrails.ts
33798
- var SECRET_RE = /(^|[\s"'`=(/\\])(\.env(\.[\w.-]+)?|[\w.-]+\.(pem|key|pfx|p12|jks|keystore)|id_rsa|id_ed25519|\.npmrc|\.pgpass|\.netrc|\.git-credentials|kubeconfig|credentials(\.[\w-]+)?)\b/i;
33798
+ var SECRET_RE = /(^|[\s"'`=(/\\])(\.env(\.[\w.-]+)?|[\w.-]+\.(pem|key|pfx|p12|jks|keystore)|id_rsa|id_ed25519|\.npmrc|\.pgpass|\.netrc|\.git-credentials|kubeconfig|\.credentials(\.[\w-]+)?)\b|[/\\]credentials(\.[\w-]+)?\b/i;
33799
33799
  var DESTRUCTIVE_RES = [
33800
33800
  /\brm\s+-[a-z]*f/i,
33801
33801
  // rm -rf / -fr / -f
@@ -33821,6 +33821,15 @@ var OUTWARD_RES = [
33821
33821
  /\bnetlify\s+deploy\b/i,
33822
33822
  /\beas\s+(submit|build)\b/i
33823
33823
  ];
33824
+ function editTargetHaystack(call) {
33825
+ const parts = [typeof call.title === "string" ? call.title : ""];
33826
+ if (call.rawInput && typeof call.rawInput === "object" && !Array.isArray(call.rawInput)) {
33827
+ for (const [key, value] of Object.entries(call.rawInput)) {
33828
+ if (typeof value === "string" && /path|file|dest|target/i.test(key)) parts.push(value);
33829
+ }
33830
+ }
33831
+ return parts.join("\n");
33832
+ }
33824
33833
  function haystack(call) {
33825
33834
  let s = typeof call.title === "string" ? call.title : "";
33826
33835
  if (call.rawInput != null) {
@@ -33849,7 +33858,7 @@ function toolPathIsSecret(p2) {
33849
33858
  var GUARDRAIL_SECRET_READ_BLOCK_REASON = "Reading this secret file is blocked by this session's guardrails (Reading secrets = Deny). Adjust it in the session Guardrails settings if intended.";
33850
33859
  function guardrailDecision(request2, policy) {
33851
33860
  if (isProseOnlyToolKind(request2.toolCall.kind)) return null;
33852
- const hay = haystack(request2.toolCall);
33861
+ const hay = request2.toolCall.kind === "edit" ? editTargetHaystack(request2.toolCall) : haystack(request2.toolCall);
33853
33862
  if (!hay) return null;
33854
33863
  const matched = matchedCategories(hay);
33855
33864
  if (matched.size === 0) return null;
@@ -33873,7 +33882,11 @@ function guardrailDecision(request2, policy) {
33873
33882
  };
33874
33883
  }
33875
33884
  if (confirmCat) {
33876
- return { kind: "confirm", category: confirmCat, reason: GUARDRAIL_CATEGORY_META[confirmCat].description };
33885
+ return {
33886
+ kind: "confirm",
33887
+ category: confirmCat,
33888
+ reason: GUARDRAIL_CATEGORY_META[confirmCat].description
33889
+ };
33877
33890
  }
33878
33891
  return null;
33879
33892
  }
@@ -38054,7 +38067,7 @@ var PackRunner = class _PackRunner {
38054
38067
  previousAttemptError
38055
38068
  });
38056
38069
  const displayLine = `\u25B6 ${this.pack.name} \u2014 stage ${index + 1}/${this.pack.stages.length}: ${stageDef.name}`;
38057
- turn = await this.drive(prompt, displayLine);
38070
+ turn = await this.drive(prompt, displayLine, index);
38058
38071
  }
38059
38072
  if (this.aborted()) {
38060
38073
  await this.settle("aborted");
@@ -38069,7 +38082,7 @@ var PackRunner = class _PackRunner {
38069
38082
  return this.recordHandoff(
38070
38083
  index,
38071
38084
  stageDef,
38072
- await this.noChangeHandoff(startSha, text, startedMs)
38085
+ await this.noChangeHandoff(stageDef, startSha, text, startedMs)
38073
38086
  );
38074
38087
  }
38075
38088
  if (!this.state.stages[index].conversationId) {
@@ -38078,7 +38091,7 @@ var PackRunner = class _PackRunner {
38078
38091
  "stage produced no commit \u2014 Retry stage to run it again in a fresh conversation"
38079
38092
  );
38080
38093
  }
38081
- const nudged = await this.drive(NUDGE_PROMPT, "\u25B6 Waiting for the stage commit\u2026");
38094
+ const nudged = await this.drive(NUDGE_PROMPT, "\u25B6 Waiting for the stage commit\u2026", index);
38082
38095
  if (this.aborted()) {
38083
38096
  await this.settle("aborted");
38084
38097
  return false;
@@ -38091,7 +38104,7 @@ var PackRunner = class _PackRunner {
38091
38104
  return this.recordHandoff(
38092
38105
  index,
38093
38106
  stageDef,
38094
- await this.noChangeHandoff(startSha, text, startedMs)
38107
+ await this.noChangeHandoff(stageDef, startSha, text, startedMs)
38095
38108
  );
38096
38109
  }
38097
38110
  return this.stall(index, "stage produced no commit", text);
@@ -38123,10 +38136,13 @@ var PackRunner = class _PackRunner {
38123
38136
  aborted() {
38124
38137
  return this.control === "abort";
38125
38138
  }
38126
- async drive(prompt, displayLine) {
38139
+ async drive(prompt, displayLine, index) {
38127
38140
  this.inTurn = true;
38128
38141
  try {
38129
- return await this.deps.driver.runTurn(prompt, displayLine);
38142
+ return await this.deps.driver.runTurn(prompt, displayLine, (pending) => {
38143
+ this.patchStage(index, { awaitingUser: pending ? true : void 0 });
38144
+ void this.publish();
38145
+ });
38130
38146
  } finally {
38131
38147
  this.inTurn = false;
38132
38148
  }
@@ -38138,13 +38154,19 @@ var PackRunner = class _PackRunner {
38138
38154
  if (fresh.length > 0) this.deps.driver.mountSkills(fresh);
38139
38155
  this.mountedSkills = next.slice();
38140
38156
  }
38141
- async noChangeHandoff(startSha, text, startedMs) {
38157
+ async noChangeHandoff(stageDef, startSha, text, startedMs) {
38142
38158
  const reviewedCommit = startSha ? await this.deps.gates.canonicalCommit(startSha) : null;
38143
38159
  return {
38144
38160
  commit: reviewedCommit ?? "(no changes)",
38145
38161
  summary: summarizeReply(text),
38146
38162
  diffStat: "reviewed \u2014 no changes needed",
38147
- durationMs: Date.now() - startedMs
38163
+ durationMs: Date.now() - startedMs,
38164
+ // A findings-producing stage that hands off without a commit never
38165
+ // wrote its findings file (its contract says: commit it even when
38166
+ // empty). Say so — the next stage must not assume a clean bill.
38167
+ ...stageDef.producesFindings ? {
38168
+ findingsNote: `no ${PACK_FINDINGS_FILE} committed \u2014 the stage handed off without structured findings`
38169
+ } : {}
38148
38170
  };
38149
38171
  }
38150
38172
  /**
@@ -38253,15 +38275,20 @@ function buildPackRunnerDeps(ctx) {
38253
38275
  ctx.onActiveSessionChanged?.(id);
38254
38276
  return id;
38255
38277
  },
38256
- runTurn: async (prompt, displayLine) => {
38278
+ runTurn: async (prompt, displayLine, onAwaiting) => {
38257
38279
  await ctx.streaming.beginTurn();
38258
38280
  ctx.history.appendUserPrompt(displayLine);
38259
- await ctx.client.prompt(prompt);
38260
- const text = ctx.streaming.getCurrentText();
38261
- const awaitingUser = await ctx.streaming.closeTurnWithInteractiveDetection();
38262
- ctx.history.appendAgentReply(text);
38263
- await ctx.history.flush();
38264
- return { text, awaitingUser };
38281
+ ctx.streaming.setPendingListener(onAwaiting ?? null);
38282
+ try {
38283
+ await ctx.client.prompt(prompt);
38284
+ const text = ctx.streaming.getCurrentText();
38285
+ const awaitingUser = await ctx.streaming.closeTurnWithInteractiveDetection();
38286
+ ctx.history.appendAgentReply(text);
38287
+ await ctx.history.flush();
38288
+ return { text, awaitingUser };
38289
+ } finally {
38290
+ ctx.streaming.setPendingListener(null);
38291
+ }
38265
38292
  },
38266
38293
  cancel: () => ctx.client.cancel(),
38267
38294
  mountSkills: (skillIds) => {
@@ -40064,6 +40091,25 @@ var StreamingState = class {
40064
40091
  */
40065
40092
  text = "";
40066
40093
  pending = null;
40094
+ /** Observer for "is a question pending?" flips. The Agent Packs runner marks
40095
+ * its stage `awaitingUser` while a permission prompt waits on the user
40096
+ * MID-turn, instead of a stage that looks silently "working" for the 5-min
40097
+ * permission TTL (live run 2026-09-23). Never throws into the turn. */
40098
+ pendingListener = null;
40099
+ setPendingListener(listener) {
40100
+ this.pendingListener = listener;
40101
+ }
40102
+ setPending(next) {
40103
+ const was = this.pending !== null;
40104
+ this.pending = next;
40105
+ const is = next !== null;
40106
+ if (was !== is) {
40107
+ try {
40108
+ this.pendingListener?.(is);
40109
+ } catch {
40110
+ }
40111
+ }
40112
+ }
40067
40113
  /**
40068
40114
  * While true, {@link append} drops incoming session/update deltas instead of
40069
40115
  * publishing them. Used by the baton {@link AcpDriver} to swallow the
@@ -40166,7 +40212,7 @@ var StreamingState = class {
40166
40212
  "acpRunner",
40167
40213
  `permission ${args2.questionId.slice(0, 8)} TTL expired \u2014 auto-cancel`
40168
40214
  );
40169
- this.pending = null;
40215
+ this.setPending(null);
40170
40216
  void this.publisher.publishOutput({
40171
40217
  type: "text",
40172
40218
  content: permissionExpiredNotice(),
@@ -40175,13 +40221,13 @@ var StreamingState = class {
40175
40221
  resolve11({ outcome: { outcome: "cancelled" } });
40176
40222
  }
40177
40223
  }, PERMISSION_TIMEOUT_MS);
40178
- this.pending = {
40224
+ this.setPending({
40179
40225
  kind: "permission",
40180
40226
  questionId: args2.questionId,
40181
40227
  options: args2.options,
40182
40228
  resolve: resolve11,
40183
40229
  timeoutTimer
40184
- };
40230
+ });
40185
40231
  });
40186
40232
  }
40187
40233
  /**
@@ -40190,7 +40236,7 @@ var StreamingState = class {
40190
40236
  * `resolveSelection()` (user picked).
40191
40237
  */
40192
40238
  registerFreeformOptions(options) {
40193
- this.pending = { kind: "free-form", options };
40239
+ this.setPending({ kind: "free-form", options });
40194
40240
  }
40195
40241
  /**
40196
40242
  * Apply a `select_option` command (delivered via the CLI's SSE
@@ -40217,7 +40263,7 @@ var StreamingState = class {
40217
40263
  const picked = byId ?? this.pending.options[index];
40218
40264
  clearTimeout(this.pending.timeoutTimer);
40219
40265
  const resolve11 = this.pending.resolve;
40220
- this.pending = null;
40266
+ this.setPending(null);
40221
40267
  if (!picked) {
40222
40268
  log.warn("acpRunner", `select_option index=${index} out of bounds \u2014 cancel`);
40223
40269
  resolve11({ outcome: { outcome: "cancelled" } });
@@ -40227,7 +40273,7 @@ var StreamingState = class {
40227
40273
  return { kind: "resolved", optionId: picked.optionId };
40228
40274
  }
40229
40275
  const text = this.pending.options[index];
40230
- this.pending = null;
40276
+ this.setPending(null);
40231
40277
  if (!text) {
40232
40278
  log.warn("acpRunner", `select_option index=${index} out of bounds (free-form) \u2014 drop`);
40233
40279
  return { kind: "none" };
@@ -40283,7 +40329,7 @@ var StreamingState = class {
40283
40329
  if (this.pending?.kind === "permission") {
40284
40330
  clearTimeout(this.pending.timeoutTimer);
40285
40331
  }
40286
- this.pending = null;
40332
+ this.setPending(null);
40287
40333
  if (opts?.clear !== false) {
40288
40334
  await this.publisher.publishOutput({ type: "clear" });
40289
40335
  }
@@ -40470,7 +40516,7 @@ var StreamingState = class {
40470
40516
  done: true
40471
40517
  });
40472
40518
  }
40473
- this.pending = { kind: "free-form", options: extracted.options };
40519
+ this.setPending({ kind: "free-form", options: extracted.options });
40474
40520
  await this.publisher.publishOutput({
40475
40521
  type: "select_prompt",
40476
40522
  content: extracted.question ?? "Pick an option",
@@ -47014,7 +47060,7 @@ async function autoUpgradeBeforeCriticalCommand() {
47014
47060
  if (process.env.NODE_ENV === "test") return;
47015
47061
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47016
47062
  if (process.env.CI) return;
47017
- const current2 = true ? "2.75.9" : null;
47063
+ const current2 = true ? "2.75.10" : null;
47018
47064
  if (!current2) return;
47019
47065
  const cache = readCache();
47020
47066
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47031,7 +47077,7 @@ function checkForUpdates() {
47031
47077
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
47032
47078
  if (process.env.CI) return;
47033
47079
  if (!process.stdout.isTTY) return;
47034
- const current2 = true ? "2.75.9" : null;
47080
+ const current2 = true ? "2.75.10" : null;
47035
47081
  if (!current2) return;
47036
47082
  const cache = readCache();
47037
47083
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -47054,7 +47100,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
47054
47100
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
47055
47101
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
47056
47102
  function currentCliVersion() {
47057
- return true ? "2.75.9" : null;
47103
+ return true ? "2.75.10" : null;
47058
47104
  }
47059
47105
  async function installedVersion(deps) {
47060
47106
  const ls = await deps.run(
@@ -48794,7 +48840,7 @@ function checkChokidar() {
48794
48840
  }
48795
48841
  async function doctor(args2 = []) {
48796
48842
  const json = args2.includes("--json");
48797
- const cliVersion = true ? "2.75.9" : "0.0.0-dev";
48843
+ const cliVersion = true ? "2.75.10" : "0.0.0-dev";
48798
48844
  const apiBase2 = resolveApiBaseUrl();
48799
48845
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
48800
48846
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -49318,7 +49364,7 @@ async function mcpWarm(argv = [], opts = {}) {
49318
49364
  // src/commands/version.ts
49319
49365
  var import_picocolors15 = __toESM(require("picocolors"));
49320
49366
  function version2() {
49321
- const v = true ? "2.75.9" : "unknown";
49367
+ const v = true ? "2.75.10" : "unknown";
49322
49368
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
49323
49369
  }
49324
49370
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.9",
3
+ "version": "2.75.10",
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",