codeam-cli 2.33.0 → 2.34.0

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 +11 -0
  2. package/dist/index.js +366 -295
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ 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.33.0] — 2026-06-09
8
+
9
+ ### Added
10
+
11
+ - **shared:** Beads wire types (ingest payload + action)
12
+ - **cli:** Bundle @beads/bd + cross-OS adapter, installer & projectKey
13
+ - **cli:** Beads bootstrap + issues.jsonl watcher -> backend
14
+ - **cli:** Apply queued beads actions as native bd commands
15
+ - **cli:** Gated beads orchestrator + barrel exports
16
+ - **cli:** Wire always-on beads into the live start + command-relay path
17
+
7
18
  ## [2.32.10] — 2026-06-09
8
19
 
9
20
  ### 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.33.0",
501
+ version: "2.34.0",
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",
@@ -852,6 +852,28 @@ async function postPreviewEvent(input) {
852
852
  };
853
853
  }
854
854
  }
855
+ async function postBeadsProvisioning(input) {
856
+ try {
857
+ await _transport.postJsonAuthed(
858
+ `${API_BASE}/api/beads/provisioning`,
859
+ {
860
+ sessionId: input.sessionId,
861
+ pluginId: input.pluginId,
862
+ status: input.status,
863
+ ...input.projectKey ? { projectKey: input.projectKey } : {}
864
+ },
865
+ input.pluginAuthToken
866
+ );
867
+ return { ok: true };
868
+ } catch (err) {
869
+ const e = err;
870
+ return {
871
+ ok: false,
872
+ status: typeof e.statusCode === "number" ? e.statusCode : 0,
873
+ message: e.message || "unknown"
874
+ };
875
+ }
876
+ }
855
877
  async function _postJsonAuthed(url, body, pluginAuthToken) {
856
878
  return new Promise((resolve7, reject) => {
857
879
  const data = JSON.stringify(body);
@@ -1162,8 +1184,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1162
1184
  return decodedFile;
1163
1185
  };
1164
1186
  }
1165
- function normalizeWindowsPath(path49) {
1166
- return path49.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1187
+ function normalizeWindowsPath(path50) {
1188
+ return path50.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1167
1189
  }
1168
1190
 
1169
1191
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3643,9 +3665,9 @@ async function addSourceContext(frames) {
3643
3665
  LRU_FILE_CONTENTS_CACHE.reduce();
3644
3666
  return frames;
3645
3667
  }
3646
- function getContextLinesFromFile(path49, ranges, output) {
3668
+ function getContextLinesFromFile(path50, ranges, output) {
3647
3669
  return new Promise((resolve7) => {
3648
- const stream = (0, import_node_fs.createReadStream)(path49);
3670
+ const stream = (0, import_node_fs.createReadStream)(path50);
3649
3671
  const lineReaded = (0, import_node_readline.createInterface)({
3650
3672
  input: stream
3651
3673
  });
@@ -3660,7 +3682,7 @@ function getContextLinesFromFile(path49, ranges, output) {
3660
3682
  let rangeStart = range[0];
3661
3683
  let rangeEnd = range[1];
3662
3684
  function onStreamError() {
3663
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path49, 1);
3685
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path50, 1);
3664
3686
  lineReaded.close();
3665
3687
  lineReaded.removeAllListeners();
3666
3688
  destroyStreamAndResolve();
@@ -3721,8 +3743,8 @@ function clearLineContext(frame) {
3721
3743
  delete frame.context_line;
3722
3744
  delete frame.post_context;
3723
3745
  }
3724
- function shouldSkipContextLinesForFile(path49) {
3725
- return path49.startsWith("node:") || path49.endsWith(".min.js") || path49.endsWith(".min.cjs") || path49.endsWith(".min.mjs") || path49.startsWith("data:");
3746
+ function shouldSkipContextLinesForFile(path50) {
3747
+ return path50.startsWith("node:") || path50.endsWith(".min.js") || path50.endsWith(".min.cjs") || path50.endsWith(".min.mjs") || path50.startsWith("data:");
3726
3748
  }
3727
3749
  function shouldSkipContextLinesForFrame(frame) {
3728
3750
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5876,7 +5898,7 @@ function readAnonId() {
5876
5898
  }
5877
5899
  function superProperties() {
5878
5900
  return {
5879
- cliVersion: true ? "2.33.0" : "0.0.0-dev",
5901
+ cliVersion: true ? "2.34.0" : "0.0.0-dev",
5880
5902
  nodeVersion: process.version,
5881
5903
  platform: process.platform,
5882
5904
  arch: process.arch,
@@ -9948,13 +9970,13 @@ function detectStartupBanner(lines) {
9948
9970
  while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9949
9971
  if (metaIdx - artStart < 2) return null;
9950
9972
  const pathLine = (lines[metaIdx + 1] ?? "").trim();
9951
- const path49 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9973
+ const path50 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9952
9974
  return {
9953
9975
  title: "",
9954
9976
  subtitle: lines[metaIdx].trim(),
9955
- path: path49,
9977
+ path: path50,
9956
9978
  startIdx: artStart,
9957
- endIdx: metaIdx + (path49 ? 1 : 0)
9979
+ endIdx: metaIdx + (path50 ? 1 : 0)
9958
9980
  };
9959
9981
  }
9960
9982
 
@@ -11290,11 +11312,11 @@ function parseReview(stdout) {
11290
11312
  for (const line of lines) {
11291
11313
  const m = line.match(HUNK_LINE_RE);
11292
11314
  if (!m) continue;
11293
- const [, path49, lineNo, sevToken, message] = m;
11294
- if (!path49 || !lineNo || !message) continue;
11315
+ const [, path50, lineNo, sevToken, message] = m;
11316
+ if (!path50 || !lineNo || !message) continue;
11295
11317
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11296
11318
  hunks.push({
11297
- path: path49.trim(),
11319
+ path: path50.trim(),
11298
11320
  line: Number(lineNo),
11299
11321
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11300
11322
  message: cleanedMessage
@@ -15688,9 +15710,9 @@ function extractSelectPrompt(text) {
15688
15710
  }
15689
15711
 
15690
15712
  // src/commands/start/handlers.ts
15691
- var fs31 = __toESM(require("fs"));
15713
+ var fs32 = __toESM(require("fs"));
15692
15714
  var os25 = __toESM(require("os"));
15693
- var path38 = __toESM(require("path"));
15715
+ var path39 = __toESM(require("path"));
15694
15716
  var import_crypto3 = require("crypto");
15695
15717
  var import_child_process15 = require("child_process");
15696
15718
 
@@ -17282,10 +17304,12 @@ var BdAdapter = class {
17282
17304
  return this.resolveBinary() !== null;
17283
17305
  }
17284
17306
  /**
17285
- * Run an arbitrary bd subcommand. Always injects `--global` (the home brain)
17286
- * unless a `beadsDir` override is set, in which case `BEADS_DIR` redirects bd
17287
- * to that dir instead (tests). Returns the raw result; callers decide how to
17288
- * interpret exit codes (e.g. `bd setup --check` uses 0/nonzero).
17307
+ * Run an arbitrary bd subcommand against the home brain. `BEADS_DIR` is set
17308
+ * to the configured `beadsDir` (tests) or `~/.beads` (the verified embedded
17309
+ * home brain) so every command resolves to the same single graph regardless
17310
+ * of the process cwd. Returns the raw result; callers decide how to interpret
17311
+ * exit codes — note bd exits 0 even for some error states (e.g. "no beads
17312
+ * database found"), so JSON-parsing callers must also inspect the payload.
17289
17313
  */
17290
17314
  async run(args2) {
17291
17315
  const binary = this.resolveBinary();
@@ -17293,14 +17317,9 @@ var BdAdapter = class {
17293
17317
  return { code: -1, stdout: "", stderr: "bd binary not resolved" };
17294
17318
  }
17295
17319
  const env = { ...process.env };
17296
- const finalArgs = [...args2];
17297
- if (this.opts.beadsDir) {
17298
- env.BEADS_DIR = this.opts.beadsDir;
17299
- } else if (!finalArgs.includes("--global")) {
17300
- finalArgs.push("--global");
17301
- }
17302
- log.trace("beads", `bd ${finalArgs.join(" ")}`);
17303
- return _spawnSeam.run(binary, finalArgs, { cwd: this.opts.cwd, env });
17320
+ env.BEADS_DIR = this.opts.beadsDir ?? defaultBeadsHomeDir();
17321
+ log.trace("beads", `bd ${args2.join(" ")} (BEADS_DIR=${env.BEADS_DIR})`);
17322
+ return _spawnSeam.run(binary, args2, { cwd: this.opts.cwd, env });
17304
17323
  }
17305
17324
  /**
17306
17325
  * `bd ready --json` → typed issue array. `bd list --json` shares the shape,
@@ -17372,92 +17391,179 @@ function defaultBeadsHomeDir() {
17372
17391
  return path35.join(os24.homedir(), ".beads");
17373
17392
  }
17374
17393
 
17375
- // src/beads/bootstrap.ts
17376
- var SETUP_RECIPE = {
17394
+ // src/beads/provisioner.ts
17395
+ var fs30 = __toESM(require("fs"));
17396
+ var path36 = __toESM(require("path"));
17397
+
17398
+ // src/beads/install-bd.ts
17399
+ var import_child_process13 = require("child_process");
17400
+ var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17401
+ var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17402
+ function resolveInstallStrategy(platform2) {
17403
+ if (platform2 === "win32") {
17404
+ return {
17405
+ command: "powershell.exe",
17406
+ args: [
17407
+ "-NoProfile",
17408
+ "-NonInteractive",
17409
+ "-ExecutionPolicy",
17410
+ "Bypass",
17411
+ "-Command",
17412
+ `irm ${INSTALL_PS1_URL} | iex`
17413
+ ],
17414
+ description: `PowerShell: irm ${INSTALL_PS1_URL} | iex (requires Go 1.24+ and Git for Windows on PATH)`
17415
+ };
17416
+ }
17417
+ return {
17418
+ command: "bash",
17419
+ args: ["-c", `curl -fsSL ${INSTALL_SH_URL} | bash`],
17420
+ description: `curl -fsSL ${INSTALL_SH_URL} | bash`
17421
+ };
17422
+ }
17423
+ var _installSpawnSeam = {
17424
+ run: _defaultInstallSpawn
17425
+ };
17426
+ function _defaultInstallSpawn(strategy) {
17427
+ return new Promise((resolve7) => {
17428
+ let proc;
17429
+ try {
17430
+ proc = (0, import_child_process13.spawn)(strategy.command, strategy.args, { env: process.env });
17431
+ } catch (err) {
17432
+ resolve7({ ok: false, code: -1, stderr: err.message });
17433
+ return;
17434
+ }
17435
+ let stderr = "";
17436
+ proc.stderr?.on("data", (c2) => {
17437
+ stderr += c2.toString();
17438
+ });
17439
+ proc.on("error", (err) => resolve7({ ok: false, code: -1, stderr: err.message }));
17440
+ proc.on(
17441
+ "close",
17442
+ (code) => resolve7({ ok: code === 0, code: code ?? -1, stderr })
17443
+ );
17444
+ });
17445
+ }
17446
+ async function installBd(platform2 = process.platform) {
17447
+ const strategy = resolveInstallStrategy(platform2);
17448
+ log.info("beads", `installing bd via ${strategy.description}`);
17449
+ const result = await _installSpawnSeam.run(strategy);
17450
+ if (!result.ok) {
17451
+ log.warn(
17452
+ "beads",
17453
+ `bd install failed (code=${result.code}): ${result.stderr.slice(0, 200)}`
17454
+ );
17455
+ }
17456
+ return result;
17457
+ }
17458
+
17459
+ // src/beads/provisioner.ts
17460
+ var AGENT_SETUP_RECIPE = {
17377
17461
  claude: "claude",
17378
17462
  codex: "codex",
17463
+ copilot: "copilot",
17379
17464
  cursor: "cursor",
17380
- gemini: "gemini",
17381
17465
  aider: "aider",
17382
- copilot: "copilot"
17466
+ gemini: "gemini",
17467
+ coderabbit: null
17468
+ };
17469
+ var _provisionSeam = {
17470
+ install: installBd,
17471
+ homeBrainInitialized
17383
17472
  };
17384
- async function bootstrapBeads(opts) {
17473
+ function homeBrainInitialized(beadsDir) {
17474
+ try {
17475
+ return fs30.statSync(path36.join(beadsDir, "embeddeddolt")).isDirectory();
17476
+ } catch {
17477
+ return false;
17478
+ }
17479
+ }
17480
+ async function provisionBeads(opts = {}) {
17385
17481
  const bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
17482
+ const beadsDir = opts.beadsDir ?? defaultBeadsHomeDir();
17386
17483
  const result = {
17387
17484
  bdAvailable: false,
17388
- serverUp: false,
17389
- agentsConfigured: [],
17390
- exportEnabled: false
17485
+ initialized: false,
17486
+ exportEnabled: false,
17487
+ agentsWired: []
17391
17488
  };
17392
17489
  if (!bd.isAvailable()) {
17393
- log.warn("beads", "bd binary unavailable \u2014 skipping bootstrap");
17394
- return result;
17490
+ log.info("beads", "bd binary missing \u2014 running OS installer fallback");
17491
+ const install = await _provisionSeam.install();
17492
+ if (!install.ok) {
17493
+ log.warn("beads", `bd install failed (code=${install.code}) \u2014 beads disabled this run`);
17494
+ return result;
17495
+ }
17395
17496
  }
17396
- result.bdAvailable = true;
17397
- result.serverUp = await ensureServer(bd);
17398
- if (!result.serverUp) {
17399
- log.warn("beads", "dolt sql-server not up after start \u2014 skipping wiring this run");
17497
+ if (!bd.isAvailable()) {
17498
+ log.warn("beads", "bd still unavailable after install \u2014 beads disabled this run");
17400
17499
  return result;
17401
17500
  }
17402
- for (const agent of opts.agents) {
17403
- const recipe = SETUP_RECIPE[agent];
17404
- if (!recipe) {
17405
- log.trace("beads", `no bd setup recipe for agent ${agent} \u2014 skipping`);
17406
- continue;
17501
+ result.bdAvailable = true;
17502
+ if (_provisionSeam.homeBrainInitialized(beadsDir)) {
17503
+ log.trace("beads", `home brain already initialized at ${beadsDir}`);
17504
+ result.initialized = true;
17505
+ } else {
17506
+ log.info("beads", `initializing home brain at ${beadsDir}`);
17507
+ const init = await bd.run(["init", "--skip-agents", "--skip-hooks", "--non-interactive"]);
17508
+ if (init.code !== 0) {
17509
+ log.warn("beads", `bd init failed (code=${init.code}): ${init.stderr.slice(0, 200)}`);
17510
+ return result;
17407
17511
  }
17408
- const applied = await ensureRecipe(bd, recipe);
17409
- if (applied) result.agentsConfigured.push(agent);
17512
+ result.initialized = true;
17410
17513
  }
17411
- result.exportEnabled = await ensureAutoExport(bd);
17514
+ const exp = await bd.run(["config", "set", "export.auto", "true"]);
17515
+ result.exportEnabled = exp.code === 0;
17516
+ result.agentsWired = await setupAgents(bd, opts.agents ?? []);
17412
17517
  log.info(
17413
17518
  "beads",
17414
- `bootstrap done server=${result.serverUp} agents=[${result.agentsConfigured.join(",")}] export=${result.exportEnabled}`
17519
+ `provision done initialized=${result.initialized} export=${result.exportEnabled} agentsWired=[${result.agentsWired.join(",")}]`
17415
17520
  );
17416
17521
  return result;
17417
17522
  }
17418
- async function ensureServer(bd) {
17419
- const status2 = await bd.run(["dolt", "status"]);
17420
- if (status2.code === 0) {
17421
- log.trace("beads", "dolt sql-server already running");
17422
- return true;
17423
- }
17424
- log.info("beads", "dolt sql-server down \u2014 starting detached");
17425
- const start2 = await bd.run(["dolt", "start"]);
17426
- if (start2.code !== 0) {
17427
- log.warn("beads", `bd dolt start failed (code=${start2.code}): ${start2.stderr.slice(0, 200)}`);
17428
- return false;
17523
+ async function setupAgents(bd, agents) {
17524
+ const wired = [];
17525
+ for (const recipe of dedupeRecipes(agents)) {
17526
+ try {
17527
+ const check = await bd.run(["setup", recipe, "--global", "--check"]);
17528
+ if (check.code === 0) {
17529
+ log.trace("beads", `bd setup ${recipe} --global already installed \u2014 skipping`);
17530
+ wired.push(recipe);
17531
+ continue;
17532
+ }
17533
+ log.info("beads", `wiring agent natively: bd setup ${recipe} --global`);
17534
+ const setup = await bd.run(["setup", recipe, "--global"]);
17535
+ if (setup.code === 0) {
17536
+ wired.push(recipe);
17537
+ } else {
17538
+ log.warn(
17539
+ "beads",
17540
+ `bd setup ${recipe} --global failed (code=${setup.code}): ${setup.stderr.slice(0, 200)} \u2014 non-fatal, agent runs without native bd wiring`
17541
+ );
17542
+ }
17543
+ } catch (err) {
17544
+ log.warn("beads", `bd setup ${recipe} --global threw (non-fatal)`, err);
17545
+ }
17429
17546
  }
17430
- const recheck = await bd.run(["dolt", "status"]);
17431
- return recheck.code === 0;
17547
+ return wired;
17432
17548
  }
17433
- async function ensureRecipe(bd, recipe) {
17434
- const check = await bd.run(["setup", recipe, "--check"]);
17435
- if (check.code === 0) {
17436
- log.trace("beads", `recipe ${recipe} already configured`);
17437
- return false;
17438
- }
17439
- const setup = await bd.run(["setup", recipe]);
17440
- if (setup.code !== 0) {
17441
- log.warn("beads", `bd setup ${recipe} failed (code=${setup.code})`);
17442
- return false;
17549
+ function dedupeRecipes(agents) {
17550
+ const seen = /* @__PURE__ */ new Set();
17551
+ for (const agent of agents) {
17552
+ const recipe = AGENT_SETUP_RECIPE[agent];
17553
+ if (recipe) seen.add(recipe);
17443
17554
  }
17444
- log.info("beads", `configured agent recipe: ${recipe}`);
17445
- return true;
17446
- }
17447
- async function ensureAutoExport(bd) {
17448
- const res = await bd.run(["config", "set", "export.jsonl", "true"]);
17449
- return res.code === 0;
17555
+ return [...seen];
17450
17556
  }
17451
17557
 
17452
17558
  // src/beads/watcher.ts
17453
17559
  var crypto3 = __toESM(require("crypto"));
17454
- var path37 = __toESM(require("path"));
17560
+ var path38 = __toESM(require("path"));
17455
17561
 
17456
17562
  // src/beads/project-key.ts
17457
- var import_child_process13 = require("child_process");
17563
+ var import_child_process14 = require("child_process");
17458
17564
  var crypto2 = __toESM(require("crypto"));
17459
- var fs30 = __toESM(require("fs"));
17460
- var path36 = __toESM(require("path"));
17565
+ var fs31 = __toESM(require("fs"));
17566
+ var path37 = __toESM(require("path"));
17461
17567
  function normalizeOrigin(raw) {
17462
17568
  const trimmed = raw.trim();
17463
17569
  if (!trimmed) return null;
@@ -17483,17 +17589,17 @@ function normalizeOrigin(raw) {
17483
17589
  return `${host}/${pathPart}`;
17484
17590
  }
17485
17591
  function findRepoRoot(cwd) {
17486
- let dir = path36.resolve(cwd);
17592
+ let dir = path37.resolve(cwd);
17487
17593
  const seen = /* @__PURE__ */ new Set();
17488
17594
  for (let i = 0; i < 256; i++) {
17489
17595
  if (seen.has(dir)) return null;
17490
17596
  seen.add(dir);
17491
17597
  try {
17492
- const stat3 = fs30.statSync(path36.join(dir, ".git"), { throwIfNoEntry: false });
17598
+ const stat3 = fs31.statSync(path37.join(dir, ".git"), { throwIfNoEntry: false });
17493
17599
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
17494
17600
  } catch {
17495
17601
  }
17496
- const parent = path36.dirname(dir);
17602
+ const parent = path37.dirname(dir);
17497
17603
  if (parent === dir) return null;
17498
17604
  dir = parent;
17499
17605
  }
@@ -17501,10 +17607,10 @@ function findRepoRoot(cwd) {
17501
17607
  }
17502
17608
  var _execSeam2 = {
17503
17609
  exec: (file, args2, opts) => {
17504
- const out2 = (0, import_child_process13.execFileSync)(file, args2, opts);
17610
+ const out2 = (0, import_child_process14.execFileSync)(file, args2, opts);
17505
17611
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
17506
17612
  },
17507
- realpath: (p2) => fs30.realpathSync(p2)
17613
+ realpath: (p2) => fs31.realpathSync(p2)
17508
17614
  };
17509
17615
  function readOrigin(cwd) {
17510
17616
  try {
@@ -17533,7 +17639,7 @@ function deriveProjectIdentity(cwd = process.cwd()) {
17533
17639
  } catch {
17534
17640
  }
17535
17641
  const hash = crypto2.createHash("sha256").update(real).digest("hex");
17536
- return { projectKey: `path:${hash}`, projectLabel: path36.basename(real) || "project" };
17642
+ return { projectKey: `path:${hash}`, projectLabel: path37.basename(real) || "project" };
17537
17643
  }
17538
17644
 
17539
17645
  // src/services/file-watcher/transport.ts
@@ -17601,7 +17707,7 @@ var BeadsWatcher = class {
17601
17707
  constructor(opts) {
17602
17708
  this.opts = opts;
17603
17709
  this.bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
17604
- this.feedPath = opts.feedPath ?? path37.join(defaultBeadsHomeDir(), "issues.jsonl");
17710
+ this.feedPath = opts.feedPath ?? path38.join(defaultBeadsHomeDir(), "issues.jsonl");
17605
17711
  this.apiBase = opts.apiBaseUrl ?? API_BASE4;
17606
17712
  }
17607
17713
  opts;
@@ -17772,85 +17878,12 @@ async function applyBeadsAction(action, deps) {
17772
17878
  return { ok: true, action: action.kind, code: 0 };
17773
17879
  }
17774
17880
 
17775
- // src/beads/install-bd.ts
17776
- var import_child_process14 = require("child_process");
17777
- var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17778
- var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17779
- function resolveInstallStrategy(platform2) {
17780
- if (platform2 === "win32") {
17781
- return {
17782
- command: "powershell.exe",
17783
- args: [
17784
- "-NoProfile",
17785
- "-NonInteractive",
17786
- "-ExecutionPolicy",
17787
- "Bypass",
17788
- "-Command",
17789
- `irm ${INSTALL_PS1_URL} | iex`
17790
- ],
17791
- description: `PowerShell: irm ${INSTALL_PS1_URL} | iex (requires Go 1.24+ and Git for Windows on PATH)`
17792
- };
17793
- }
17794
- return {
17795
- command: "bash",
17796
- args: ["-c", `curl -fsSL ${INSTALL_SH_URL} | bash`],
17797
- description: `curl -fsSL ${INSTALL_SH_URL} | bash`
17798
- };
17799
- }
17800
- var _installSpawnSeam = {
17801
- run: _defaultInstallSpawn
17802
- };
17803
- function _defaultInstallSpawn(strategy) {
17804
- return new Promise((resolve7) => {
17805
- let proc;
17806
- try {
17807
- proc = (0, import_child_process14.spawn)(strategy.command, strategy.args, { env: process.env });
17808
- } catch (err) {
17809
- resolve7({ ok: false, code: -1, stderr: err.message });
17810
- return;
17811
- }
17812
- let stderr = "";
17813
- proc.stderr?.on("data", (c2) => {
17814
- stderr += c2.toString();
17815
- });
17816
- proc.on("error", (err) => resolve7({ ok: false, code: -1, stderr: err.message }));
17817
- proc.on(
17818
- "close",
17819
- (code) => resolve7({ ok: code === 0, code: code ?? -1, stderr })
17820
- );
17821
- });
17822
- }
17823
- async function installBd(platform2 = process.platform) {
17824
- const strategy = resolveInstallStrategy(platform2);
17825
- log.info("beads", `installing bd via ${strategy.description}`);
17826
- const result = await _installSpawnSeam.run(strategy);
17827
- if (!result.ok) {
17828
- log.warn(
17829
- "beads",
17830
- `bd install failed (code=${result.code}): ${result.stderr.slice(0, 200)}`
17831
- );
17832
- }
17833
- return result;
17834
- }
17835
-
17836
17881
  // src/beads/index.ts
17837
- async function maybeStartBeads(opts) {
17838
- if (!opts.enabled) {
17839
- log.trace("beads", "beads flag off \u2014 skipping");
17840
- return null;
17841
- }
17882
+ async function startBeads(opts) {
17842
17883
  const adapter = new BdAdapter({ cwd: opts.cwd });
17843
- if (!adapter.isAvailable() && opts.allowInstall) {
17844
- log.info("beads", "bd not found \u2014 running OS installer");
17845
- await installBd();
17846
- }
17847
- if (!adapter.isAvailable()) {
17848
- log.warn("beads", "bd unavailable \u2014 beads disabled for this session");
17849
- return null;
17850
- }
17851
- const boot = await bootstrapBeads({ cwd: opts.cwd, agents: opts.agents, adapter });
17852
- if (!boot.serverUp) {
17853
- log.warn("beads", "beads server not up \u2014 watcher not started this run");
17884
+ const provision = await provisionBeads({ cwd: opts.cwd, adapter, agents: opts.agents });
17885
+ if (!provision.bdAvailable || !provision.initialized) {
17886
+ log.warn("beads", "home brain not provisioned \u2014 watcher not started this run");
17854
17887
  return null;
17855
17888
  }
17856
17889
  const watcher = new BeadsWatcher({
@@ -17876,28 +17909,42 @@ function beadsKilled() {
17876
17909
  const v = process.env.CODEAM_BEADS_DISABLED;
17877
17910
  return !!v && v !== "0" && v.toLowerCase() !== "false";
17878
17911
  }
17879
- async function startBeadsForSession(ctx) {
17912
+ async function provisionBeadsForStart(ctx) {
17880
17913
  if (beadsKilled()) {
17881
- log.trace("beads", "CODEAM_BEADS_DISABLED set \u2014 beads off for this session");
17914
+ log.trace("beads", "CODEAM_BEADS_DISABLED set \u2014 beads off this run");
17882
17915
  return null;
17883
17916
  }
17884
17917
  if (!ctx.pluginAuthToken) {
17885
- log.trace("beads", "no pluginAuthToken on session \u2014 beads off");
17918
+ log.trace("beads", "no pluginAuthToken \u2014 beads off");
17886
17919
  return null;
17887
17920
  }
17921
+ const pluginAuthToken = ctx.pluginAuthToken;
17922
+ let started = null;
17888
17923
  try {
17889
- return await maybeStartBeads({
17890
- enabled: true,
17924
+ started = await startBeads({
17891
17925
  sessionId: ctx.sessionId,
17892
17926
  pluginId: ctx.pluginId,
17893
- pluginAuthToken: ctx.pluginAuthToken,
17894
- agents: ctx.agents,
17895
- cwd: ctx.cwd
17927
+ pluginAuthToken,
17928
+ cwd: ctx.cwd,
17929
+ agents: ctx.agents
17896
17930
  });
17897
17931
  } catch (err) {
17898
- log.warn("beads", "startBeadsForSession failed (non-fatal)", err);
17899
- return null;
17900
- }
17932
+ log.warn("beads", "provisionBeadsForStart failed (non-fatal)", err);
17933
+ started = null;
17934
+ }
17935
+ const { projectKey } = deriveProjectIdentity(ctx.cwd);
17936
+ void postBeadsProvisioning({
17937
+ sessionId: ctx.sessionId,
17938
+ pluginId: ctx.pluginId,
17939
+ pluginAuthToken,
17940
+ status: started ? "ready" : "failed",
17941
+ projectKey
17942
+ }).then((res) => {
17943
+ if (!res.ok) {
17944
+ log.trace("beads", `provisioning signal POST non-ok (status=${res.status}) \u2014 ignoring`);
17945
+ }
17946
+ });
17947
+ return started;
17901
17948
  }
17902
17949
  var ACTION_KINDS = /* @__PURE__ */ new Set([
17903
17950
  "claim",
@@ -17930,7 +17977,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
17930
17977
  function cleanupAttachmentTempFiles() {
17931
17978
  for (const p2 of pendingAttachmentFiles) {
17932
17979
  try {
17933
- fs31.unlinkSync(p2);
17980
+ fs32.unlinkSync(p2);
17934
17981
  } catch {
17935
17982
  }
17936
17983
  }
@@ -17939,8 +17986,8 @@ function cleanupAttachmentTempFiles() {
17939
17986
  function saveFilesTemp(files) {
17940
17987
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
17941
17988
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
17942
- const tmpPath = path38.join(os25.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17943
- fs31.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17989
+ const tmpPath = path39.join(os25.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17990
+ fs32.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17944
17991
  pendingAttachmentFiles.add(tmpPath);
17945
17992
  return tmpPath;
17946
17993
  });
@@ -17960,7 +18007,7 @@ var startTask = (ctx, _cmd, parsed) => {
17960
18007
  setTimeout(() => {
17961
18008
  for (const p2 of paths) {
17962
18009
  try {
17963
- fs31.unlinkSync(p2);
18010
+ fs32.unlinkSync(p2);
17964
18011
  } catch {
17965
18012
  }
17966
18013
  pendingAttachmentFiles.delete(p2);
@@ -18533,8 +18580,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
18533
18580
  if (args2.length === 0) return detection;
18534
18581
  const binName = args2[0];
18535
18582
  if (binName.startsWith("-")) return detection;
18536
- const binPath = path38.join(cwd, "node_modules", ".bin", binName);
18537
- if (!fs31.existsSync(binPath)) return detection;
18583
+ const binPath = path39.join(cwd, "node_modules", ".bin", binName);
18584
+ if (!fs32.existsSync(binPath)) return detection;
18538
18585
  return {
18539
18586
  ...detection,
18540
18587
  command: binPath,
@@ -18945,9 +18992,9 @@ async function dispatchCommand(ctx, cmd) {
18945
18992
 
18946
18993
  // src/services/file-watcher.service.ts
18947
18994
  var import_child_process16 = require("child_process");
18948
- var fs32 = __toESM(require("fs"));
18995
+ var fs33 = __toESM(require("fs"));
18949
18996
  var os26 = __toESM(require("os"));
18950
- var path39 = __toESM(require("path"));
18997
+ var path40 = __toESM(require("path"));
18951
18998
  var import_ignore = __toESM(require("ignore"));
18952
18999
 
18953
19000
  // src/services/file-watcher/diff-parser.ts
@@ -19090,18 +19137,18 @@ var _findGitRootSeam = {
19090
19137
  resolve: _defaultFindGitRoot
19091
19138
  };
19092
19139
  function _defaultFindGitRoot(startDir) {
19093
- let dir = path39.resolve(startDir);
19140
+ let dir = path40.resolve(startDir);
19094
19141
  const seen = /* @__PURE__ */ new Set();
19095
19142
  for (let i = 0; i < 256; i++) {
19096
19143
  if (seen.has(dir)) return null;
19097
19144
  seen.add(dir);
19098
19145
  try {
19099
- const gitPath = path39.join(dir, ".git");
19100
- const stat3 = fs32.statSync(gitPath, { throwIfNoEntry: false });
19146
+ const gitPath = path40.join(dir, ".git");
19147
+ const stat3 = fs33.statSync(gitPath, { throwIfNoEntry: false });
19101
19148
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
19102
19149
  } catch {
19103
19150
  }
19104
- const parent = path39.dirname(dir);
19151
+ const parent = path40.dirname(dir);
19105
19152
  if (parent === dir) return null;
19106
19153
  dir = parent;
19107
19154
  }
@@ -19346,7 +19393,7 @@ var FileWatcherService = class {
19346
19393
  }
19347
19394
  async emitForFile(absPath, changeType) {
19348
19395
  if (this.stopped) return;
19349
- const fileDir = path39.dirname(absPath);
19396
+ const fileDir = path40.dirname(absPath);
19350
19397
  let gitRoot = this.gitRootByDir.get(fileDir);
19351
19398
  if (gitRoot === void 0) {
19352
19399
  gitRoot = findGitRoot2(fileDir);
@@ -19359,19 +19406,19 @@ var FileWatcherService = class {
19359
19406
  );
19360
19407
  return;
19361
19408
  }
19362
- const relPathInRepo = path39.relative(gitRoot, absPath);
19409
+ const relPathInRepo = path40.relative(gitRoot, absPath);
19363
19410
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
19364
19411
  const matcher = this.getGitIgnoreMatcher(gitRoot);
19365
19412
  if (matcher && matcher.ignores(relPathInRepo)) {
19366
19413
  log.trace(
19367
19414
  "fileWatcher",
19368
- `${relPathInRepo} ignored by ${path39.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
19415
+ `${relPathInRepo} ignored by ${path40.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
19369
19416
  );
19370
19417
  return;
19371
19418
  }
19372
19419
  this.opts.onRepoDirty?.(gitRoot);
19373
- const repoPath = path39.relative(this.opts.workingDir, gitRoot);
19374
- const repoName = path39.basename(gitRoot);
19420
+ const repoPath = path40.relative(this.opts.workingDir, gitRoot);
19421
+ const repoName = path40.basename(gitRoot);
19375
19422
  let diffText = "";
19376
19423
  let fileStatus = "modified";
19377
19424
  if (changeType === "unlink") {
@@ -19546,7 +19593,7 @@ var FileWatcherService = class {
19546
19593
  collectGitignoreFiles(repoRoot, dir, matcher) {
19547
19594
  let entries;
19548
19595
  try {
19549
- entries = fs32.readdirSync(dir, { withFileTypes: true });
19596
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
19550
19597
  } catch {
19551
19598
  return;
19552
19599
  }
@@ -19555,16 +19602,16 @@ var FileWatcherService = class {
19555
19602
  );
19556
19603
  if (gitignoreEntry) {
19557
19604
  try {
19558
- const body = fs32.readFileSync(path39.join(dir, ".gitignore"), "utf8");
19559
- const rel = path39.relative(repoRoot, dir).replace(/\\/g, "/");
19605
+ const body = fs33.readFileSync(path40.join(dir, ".gitignore"), "utf8");
19606
+ const rel = path40.relative(repoRoot, dir).replace(/\\/g, "/");
19560
19607
  const prefixed = body.split(/\r?\n/).map((line) => {
19561
19608
  const trimmed = line.trim();
19562
19609
  if (!trimmed || trimmed.startsWith("#")) return line;
19563
19610
  if (!rel) return line;
19564
19611
  if (trimmed.startsWith("!")) {
19565
- return "!" + path39.posix.join(rel, trimmed.slice(1));
19612
+ return "!" + path40.posix.join(rel, trimmed.slice(1));
19566
19613
  }
19567
- return path39.posix.join(rel, trimmed);
19614
+ return path40.posix.join(rel, trimmed);
19568
19615
  }).join("\n");
19569
19616
  matcher.add(prefixed);
19570
19617
  } catch {
@@ -19573,7 +19620,7 @@ var FileWatcherService = class {
19573
19620
  for (const entry of entries) {
19574
19621
  if (!entry.isDirectory()) continue;
19575
19622
  if (entry.name === ".git") continue;
19576
- const childAbs = path39.join(dir, entry.name);
19623
+ const childAbs = path40.join(dir, entry.name);
19577
19624
  if (isIgnoredFilePath(childAbs)) continue;
19578
19625
  this.collectGitignoreFiles(repoRoot, childAbs, matcher);
19579
19626
  }
@@ -19743,7 +19790,7 @@ var import_crypto4 = require("crypto");
19743
19790
 
19744
19791
  // src/services/turn-files/git-changeset.ts
19745
19792
  var import_child_process17 = require("child_process");
19746
- var path40 = __toESM(require("path"));
19793
+ var path41 = __toESM(require("path"));
19747
19794
  async function collectRepoChangeset(opts) {
19748
19795
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
19749
19796
  if (status2 === null) return null;
@@ -19854,7 +19901,7 @@ function defaultRunGit(cwd, args2) {
19854
19901
  });
19855
19902
  }
19856
19903
  async function discoverRepos(workingDir, maxDepth = 4) {
19857
- const fs38 = await import("fs/promises");
19904
+ const fs39 = await import("fs/promises");
19858
19905
  const out2 = [];
19859
19906
  await walk(workingDir, 0);
19860
19907
  return out2;
@@ -19862,7 +19909,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19862
19909
  if (depth > maxDepth) return;
19863
19910
  let entries = [];
19864
19911
  try {
19865
- const dirents = await fs38.readdir(dir, { withFileTypes: true });
19912
+ const dirents = await fs39.readdir(dir, { withFileTypes: true });
19866
19913
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
19867
19914
  } catch {
19868
19915
  return;
@@ -19873,8 +19920,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19873
19920
  if (hasGit) {
19874
19921
  out2.push({
19875
19922
  repoRoot: dir,
19876
- repoPath: path40.relative(workingDir, dir),
19877
- repoName: path40.basename(dir)
19923
+ repoPath: path41.relative(workingDir, dir),
19924
+ repoName: path41.basename(dir)
19878
19925
  });
19879
19926
  return;
19880
19927
  }
@@ -19882,14 +19929,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19882
19929
  if (!entry.isDirectory) continue;
19883
19930
  if (entry.name === "node_modules") continue;
19884
19931
  if (entry.name === "dist" || entry.name === "build") continue;
19885
- await walk(path40.join(dir, entry.name), depth + 1);
19932
+ await walk(path41.join(dir, entry.name), depth + 1);
19886
19933
  }
19887
19934
  }
19888
19935
  }
19889
19936
 
19890
19937
  // src/services/turn-files/files-outbox.ts
19891
- var fs33 = __toESM(require("fs/promises"));
19892
- var path41 = __toESM(require("path"));
19938
+ var fs34 = __toESM(require("fs/promises"));
19939
+ var path42 = __toESM(require("path"));
19893
19940
  var import_os7 = require("os");
19894
19941
  var HOME_OUTBOX_DIR = ".codeam/outbox";
19895
19942
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -19922,16 +19969,16 @@ var FilesOutbox = class {
19922
19969
  backoffIndex = 0;
19923
19970
  stopped = false;
19924
19971
  constructor(opts) {
19925
- const base = opts.baseDir ?? path41.join(homeDir(), HOME_OUTBOX_DIR);
19926
- this.filePath = path41.join(base, `${opts.sessionId}.jsonl`);
19972
+ const base = opts.baseDir ?? path42.join(homeDir(), HOME_OUTBOX_DIR);
19973
+ this.filePath = path42.join(base, `${opts.sessionId}.jsonl`);
19927
19974
  this.post = opts.post;
19928
19975
  this.autoSchedule = opts.autoSchedule !== false;
19929
19976
  }
19930
19977
  /** Persist the entry to disk and trigger a flush. Returns once the
19931
19978
  * line is durable on disk (not once the POST succeeds). */
19932
19979
  async enqueue(entry) {
19933
- await fs33.mkdir(path41.dirname(this.filePath), { recursive: true });
19934
- await fs33.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
19980
+ await fs34.mkdir(path42.dirname(this.filePath), { recursive: true });
19981
+ await fs34.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
19935
19982
  this.backoffIndex = 0;
19936
19983
  if (this.autoSchedule) this.scheduleFlush(0);
19937
19984
  }
@@ -20020,7 +20067,7 @@ var FilesOutbox = class {
20020
20067
  async readAll() {
20021
20068
  let raw = "";
20022
20069
  try {
20023
- raw = await fs33.readFile(this.filePath, "utf8");
20070
+ raw = await fs34.readFile(this.filePath, "utf8");
20024
20071
  } catch {
20025
20072
  return [];
20026
20073
  }
@@ -20044,12 +20091,12 @@ var FilesOutbox = class {
20044
20091
  async rewrite(entries) {
20045
20092
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
20046
20093
  if (entries.length === 0) {
20047
- await fs33.unlink(this.filePath).catch(() => void 0);
20094
+ await fs34.unlink(this.filePath).catch(() => void 0);
20048
20095
  return;
20049
20096
  }
20050
20097
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
20051
- await fs33.writeFile(tmpPath, payload, "utf8");
20052
- await fs33.rename(tmpPath, this.filePath);
20098
+ await fs34.writeFile(tmpPath, payload, "utf8");
20099
+ await fs34.rename(tmpPath, this.filePath);
20053
20100
  }
20054
20101
  };
20055
20102
  function applyJitter(ms) {
@@ -20670,16 +20717,7 @@ async function runAcpSession(opts) {
20670
20717
  }).catch((err) => {
20671
20718
  log.warn("acpRunner", `fileWatcher.start failed: ${describeError(err)}`);
20672
20719
  });
20673
- let beads = null;
20674
- void startBeadsForSession({
20675
- sessionId: opts.sessionId,
20676
- pluginId: opts.pluginId,
20677
- pluginAuthToken: opts.pluginAuthToken,
20678
- agents: [opts.agent],
20679
- cwd: opts.cwd
20680
- }).then((started) => {
20681
- beads = started;
20682
- });
20720
+ const getBeads = opts.getBeads ?? (() => null);
20683
20721
  const relay = new CommandRelayService(
20684
20722
  opts.pluginId,
20685
20723
  async (cmd) => {
@@ -20694,7 +20732,7 @@ async function runAcpSession(opts) {
20694
20732
  history,
20695
20733
  initialize.agentCapabilities,
20696
20734
  turnFiles,
20697
- () => beads
20735
+ getBeads
20698
20736
  );
20699
20737
  },
20700
20738
  { id: opts.agent, name: opts.agent, displayName: opts.agent }
@@ -20705,7 +20743,6 @@ async function runAcpSession(opts) {
20705
20743
  relay.stop();
20706
20744
  void fileWatcher.stop();
20707
20745
  turnFiles.stop();
20708
- void beads?.watcher.stop();
20709
20746
  closeAllTerminals();
20710
20747
  await client2.stop();
20711
20748
  process.exit(0);
@@ -21813,8 +21850,8 @@ var OutputService = class _OutputService {
21813
21850
  };
21814
21851
 
21815
21852
  // src/services/history.service.ts
21816
- var fs34 = __toESM(require("fs"));
21817
- var path42 = __toESM(require("path"));
21853
+ var fs35 = __toESM(require("fs"));
21854
+ var path43 = __toESM(require("path"));
21818
21855
  var os27 = __toESM(require("os"));
21819
21856
  var https7 = __toESM(require("https"));
21820
21857
  var http7 = __toESM(require("http"));
@@ -21842,7 +21879,7 @@ function parseJsonl(filePath) {
21842
21879
  const messages = [];
21843
21880
  let raw;
21844
21881
  try {
21845
- raw = fs34.readFileSync(filePath, "utf8");
21882
+ raw = fs35.readFileSync(filePath, "utf8");
21846
21883
  } catch (err) {
21847
21884
  if (err.code !== "ENOENT") {
21848
21885
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -21977,7 +22014,7 @@ var HistoryService = class _HistoryService {
21977
22014
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
21978
22015
  }
21979
22016
  get projectDir() {
21980
- return this.runtime.resolveHistoryDir(this.cwd) ?? path42.join(os27.homedir(), ".claude", "projects", encodeCwd(this.cwd));
22017
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path43.join(os27.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21981
22018
  }
21982
22019
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
21983
22020
  setCurrentConversationId(id) {
@@ -21989,7 +22026,7 @@ var HistoryService = class _HistoryService {
21989
22026
  /** Return the current message count in the active conversation. */
21990
22027
  getCurrentMessageCount() {
21991
22028
  if (!this.currentConversationId) return 0;
21992
- const filePath = path42.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22029
+ const filePath = path43.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21993
22030
  return parseJsonl(filePath).length;
21994
22031
  }
21995
22032
  /**
@@ -22000,7 +22037,7 @@ var HistoryService = class _HistoryService {
22000
22037
  const deadline = Date.now() + timeoutMs;
22001
22038
  while (Date.now() < deadline) {
22002
22039
  if (!this.currentConversationId) return null;
22003
- const filePath = path42.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22040
+ const filePath = path43.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22004
22041
  const messages = parseJsonl(filePath);
22005
22042
  if (messages.length > previousCount) {
22006
22043
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -22026,16 +22063,16 @@ var HistoryService = class _HistoryService {
22026
22063
  const dir = this.projectDir;
22027
22064
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22028
22065
  try {
22029
- const files = fs34.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22066
+ const files = fs35.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22030
22067
  try {
22031
- const stat3 = fs34.statSync(path42.join(dir, e.name));
22068
+ const stat3 = fs35.statSync(path43.join(dir, e.name));
22032
22069
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22033
22070
  } catch {
22034
22071
  return { name: e.name, mtime: 0, birthtime: 0 };
22035
22072
  }
22036
22073
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
22037
22074
  if (files.length > 0) {
22038
- this.currentConversationId = path42.basename(files[0].name, ".jsonl");
22075
+ this.currentConversationId = path43.basename(files[0].name, ".jsonl");
22039
22076
  }
22040
22077
  } catch {
22041
22078
  }
@@ -22069,13 +22106,13 @@ var HistoryService = class _HistoryService {
22069
22106
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22070
22107
  let entries;
22071
22108
  try {
22072
- entries = fs34.readdirSync(dir, { withFileTypes: true });
22109
+ entries = fs35.readdirSync(dir, { withFileTypes: true });
22073
22110
  } catch {
22074
22111
  return null;
22075
22112
  }
22076
22113
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22077
22114
  try {
22078
- const stat3 = fs34.statSync(path42.join(dir, e.name));
22115
+ const stat3 = fs35.statSync(path43.join(dir, e.name));
22079
22116
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22080
22117
  } catch {
22081
22118
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -22084,12 +22121,12 @@ var HistoryService = class _HistoryService {
22084
22121
  if (files.length === 0) return null;
22085
22122
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
22086
22123
  if (!files.some((f) => f.name === targetFile)) return null;
22087
- return this.extractUsageFromFile(path42.join(dir, targetFile));
22124
+ return this.extractUsageFromFile(path43.join(dir, targetFile));
22088
22125
  }
22089
22126
  extractUsageFromFile(filePath) {
22090
22127
  let raw;
22091
22128
  try {
22092
- raw = fs34.readFileSync(filePath, "utf8");
22129
+ raw = fs35.readFileSync(filePath, "utf8");
22093
22130
  } catch {
22094
22131
  return null;
22095
22132
  }
@@ -22134,9 +22171,9 @@ var HistoryService = class _HistoryService {
22134
22171
  let totalCost = 0;
22135
22172
  let files;
22136
22173
  try {
22137
- files = fs34.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
22174
+ files = fs35.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
22138
22175
  try {
22139
- return fs34.statSync(path42.join(projectDir, f)).mtimeMs >= monthStartMs;
22176
+ return fs35.statSync(path43.join(projectDir, f)).mtimeMs >= monthStartMs;
22140
22177
  } catch {
22141
22178
  return false;
22142
22179
  }
@@ -22147,7 +22184,7 @@ var HistoryService = class _HistoryService {
22147
22184
  for (const file of files) {
22148
22185
  let raw;
22149
22186
  try {
22150
- raw = fs34.readFileSync(path42.join(projectDir, file), "utf8");
22187
+ raw = fs35.readFileSync(path43.join(projectDir, file), "utf8");
22151
22188
  } catch {
22152
22189
  continue;
22153
22190
  }
@@ -22211,7 +22248,7 @@ var HistoryService = class _HistoryService {
22211
22248
  * showing an empty conversation.
22212
22249
  */
22213
22250
  async loadConversation(sessionId) {
22214
- const filePath = path42.join(this.projectDir, `${sessionId}.jsonl`);
22251
+ const filePath = path43.join(this.projectDir, `${sessionId}.jsonl`);
22215
22252
  const messages = parseJsonl(filePath);
22216
22253
  if (messages.length === 0) return;
22217
22254
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -22265,7 +22302,7 @@ var HistoryService = class _HistoryService {
22265
22302
  if (!this.currentConversationId) return 0;
22266
22303
  }
22267
22304
  const sessionId = this.currentConversationId;
22268
- const filePath = path42.join(this.projectDir, `${sessionId}.jsonl`);
22305
+ const filePath = path43.join(this.projectDir, `${sessionId}.jsonl`);
22269
22306
  const messages = parseJsonl(filePath);
22270
22307
  if (messages.length === 0) return 0;
22271
22308
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -22787,6 +22824,20 @@ async function start(requestedAgent) {
22787
22824
  "pluginAuth",
22788
22825
  `boot triple sessionId=${session.id} pluginId=${pluginId} tokenLen=${tokenForLog.length} tokenHead=${tokenForLog.slice(0, 12)} tokenTail=${tokenForLog.slice(-8)} mintedEqualsCached=${refreshed === session.pluginAuthToken}`
22789
22826
  );
22827
+ let beads = null;
22828
+ const getBeads = () => beads;
22829
+ const beadsReady = provisionBeadsForStart({
22830
+ sessionId: session.id,
22831
+ pluginId,
22832
+ pluginAuthToken: session.pluginAuthToken ?? void 0,
22833
+ cwd,
22834
+ // Wire this session's agent natively via `bd setup <recipe> --global` so
22835
+ // the agent actually uses bd (D12 — REVISED). Covers BOTH ACP and PTY.
22836
+ agents: [session.agent]
22837
+ }).then((started) => {
22838
+ beads = started;
22839
+ return started;
22840
+ });
22790
22841
  const acpDisabled = process.env.CODEAM_ACP_DISABLED === "1";
22791
22842
  if (!acpDisabled && session.pluginAuthToken) {
22792
22843
  const adapter = getAcpAdapter(session.agent);
@@ -22797,7 +22848,8 @@ async function start(requestedAgent) {
22797
22848
  pluginId,
22798
22849
  pluginAuthToken: session.pluginAuthToken,
22799
22850
  adapter,
22800
- cwd
22851
+ cwd,
22852
+ getBeads
22801
22853
  });
22802
22854
  return;
22803
22855
  }
@@ -22859,7 +22911,6 @@ async function start(requestedAgent) {
22859
22911
  dirtyTracker: dirtyTracker ?? void 0
22860
22912
  }) : null;
22861
22913
  let streamingEmitter = null;
22862
- let beads = null;
22863
22914
  const agent = new AgentService(
22864
22915
  runtime,
22865
22916
  {
@@ -22910,14 +22961,7 @@ async function start(requestedAgent) {
22910
22961
  await dispatchCommand(ctx, cmd);
22911
22962
  }, runtime.meta);
22912
22963
  ctx.relay = relay;
22913
- void startBeadsForSession({
22914
- sessionId: session.id,
22915
- pluginId,
22916
- pluginAuthToken: session.pluginAuthToken ?? void 0,
22917
- agents: [session.agent],
22918
- cwd
22919
- }).then((started) => {
22920
- beads = started;
22964
+ void beadsReady.then((started) => {
22921
22965
  ctx.beads = started;
22922
22966
  });
22923
22967
  registerTerminalHandlers({
@@ -23175,7 +23219,7 @@ async function autoLinkAfterPair(opts) {
23175
23219
  }
23176
23220
 
23177
23221
  // src/commands/pair-auto.ts
23178
- var fs35 = __toESM(require("fs"));
23222
+ var fs36 = __toESM(require("fs"));
23179
23223
  var os28 = __toESM(require("os"));
23180
23224
  var import_crypto7 = require("crypto");
23181
23225
 
@@ -23236,6 +23280,15 @@ async function startInfraOnly(agentId) {
23236
23280
  pluginId,
23237
23281
  pluginAuthToken: session.pluginAuthToken
23238
23282
  }) : null;
23283
+ let beads = null;
23284
+ void provisionBeadsForStart({
23285
+ sessionId: session.id,
23286
+ pluginId,
23287
+ pluginAuthToken: session.pluginAuthToken ?? void 0,
23288
+ cwd
23289
+ }).then((started) => {
23290
+ beads = started;
23291
+ });
23239
23292
  let relayRef = null;
23240
23293
  const ctx = {
23241
23294
  // Agent-touching fields are not used by any of the commands
@@ -23257,6 +23310,23 @@ async function startInfraOnly(agentId) {
23257
23310
  const relay = new CommandRelayService(
23258
23311
  pluginId,
23259
23312
  async (cmd) => {
23313
+ if (cmd.type === "beads_action") {
23314
+ if (!beads) {
23315
+ log.trace("infra-only", "beads_action received but beads not running \u2014 dropping");
23316
+ return;
23317
+ }
23318
+ const action = beadsActionFromPayload(cmd.payload);
23319
+ if (!action) {
23320
+ log.warn("infra-only", "malformed beads_action payload \u2014 dropping");
23321
+ return;
23322
+ }
23323
+ try {
23324
+ await handleBeadsActionCommand(action, beads);
23325
+ } catch (err) {
23326
+ log.warn("infra-only", "handleBeadsActionCommand failed (non-fatal)", err);
23327
+ }
23328
+ return;
23329
+ }
23260
23330
  if (!INFRA_ONLY_COMMAND_TYPES.has(cmd.type)) {
23261
23331
  log.trace("infra-only", `dropping agent-only command type=${cmd.type}`);
23262
23332
  return;
@@ -23317,6 +23387,7 @@ async function startInfraOnly(agentId) {
23317
23387
  } catch {
23318
23388
  }
23319
23389
  void fileWatcher?.stop();
23390
+ void beads?.watcher.stop();
23320
23391
  closeAllTerminals();
23321
23392
  cleanupAttachmentTempFiles();
23322
23393
  process.exit(0);
@@ -23344,12 +23415,12 @@ function readTokenFromArgs(args2) {
23344
23415
  }
23345
23416
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
23346
23417
  if (fileFlag) {
23347
- const path49 = fileFlag.slice("--token-file=".length);
23418
+ const path50 = fileFlag.slice("--token-file=".length);
23348
23419
  try {
23349
- const content = fs35.readFileSync(path49, "utf8").trim();
23350
- if (content.length === 0) fail(`--token-file ${path49} is empty`);
23420
+ const content = fs36.readFileSync(path50, "utf8").trim();
23421
+ if (content.length === 0) fail(`--token-file ${path50} is empty`);
23351
23422
  try {
23352
- fs35.unlinkSync(path49);
23423
+ fs36.unlinkSync(path50);
23353
23424
  } catch {
23354
23425
  }
23355
23426
  return content;
@@ -23615,7 +23686,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
23615
23686
  var import_child_process19 = require("child_process");
23616
23687
  var import_util4 = require("util");
23617
23688
  var import_picocolors8 = __toESM(require("picocolors"));
23618
- var path43 = __toESM(require("path"));
23689
+ var path44 = __toESM(require("path"));
23619
23690
  var execFileP5 = (0, import_util4.promisify)(import_child_process19.execFile);
23620
23691
  var MAX_BUFFER = 8 * 1024 * 1024;
23621
23692
  function resetStdinForChild() {
@@ -24104,7 +24175,7 @@ var GitHubCodespacesProvider = class {
24104
24175
  });
24105
24176
  }
24106
24177
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24107
- const remoteDir = path43.posix.dirname(remotePath);
24178
+ const remoteDir = path44.posix.dirname(remotePath);
24108
24179
  const parts = [
24109
24180
  `mkdir -p ${shellQuote(remoteDir)}`,
24110
24181
  `cat > ${shellQuote(remotePath)}`
@@ -24174,7 +24245,7 @@ function shellQuote(s) {
24174
24245
  // src/services/providers/gitpod.ts
24175
24246
  var import_child_process20 = require("child_process");
24176
24247
  var import_util5 = require("util");
24177
- var path44 = __toESM(require("path"));
24248
+ var path45 = __toESM(require("path"));
24178
24249
  var import_picocolors9 = __toESM(require("picocolors"));
24179
24250
  var execFileP6 = (0, import_util5.promisify)(import_child_process20.execFile);
24180
24251
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -24414,7 +24485,7 @@ var GitpodProvider = class {
24414
24485
  });
24415
24486
  }
24416
24487
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24417
- const remoteDir = path44.posix.dirname(remotePath);
24488
+ const remoteDir = path45.posix.dirname(remotePath);
24418
24489
  const parts = [
24419
24490
  `mkdir -p ${shellQuote2(remoteDir)}`,
24420
24491
  `cat > ${shellQuote2(remotePath)}`
@@ -24450,7 +24521,7 @@ function shellQuote2(s) {
24450
24521
  // src/services/providers/gitlab-workspaces.ts
24451
24522
  var import_child_process21 = require("child_process");
24452
24523
  var import_util6 = require("util");
24453
- var path45 = __toESM(require("path"));
24524
+ var path46 = __toESM(require("path"));
24454
24525
  var execFileP7 = (0, import_util6.promisify)(import_child_process21.execFile);
24455
24526
  var MAX_BUFFER3 = 8 * 1024 * 1024;
24456
24527
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -24710,7 +24781,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
24710
24781
  }
24711
24782
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24712
24783
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
24713
- const remoteDir = path45.posix.dirname(remotePath);
24784
+ const remoteDir = path46.posix.dirname(remotePath);
24714
24785
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
24715
24786
  if (options.mode != null) {
24716
24787
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -24778,7 +24849,7 @@ function shellQuote3(s) {
24778
24849
  // src/services/providers/railway.ts
24779
24850
  var import_child_process22 = require("child_process");
24780
24851
  var import_util7 = require("util");
24781
- var path46 = __toESM(require("path"));
24852
+ var path47 = __toESM(require("path"));
24782
24853
  var execFileP8 = (0, import_util7.promisify)(import_child_process22.execFile);
24783
24854
  var MAX_BUFFER4 = 8 * 1024 * 1024;
24784
24855
  function resetStdinForChild4() {
@@ -25014,7 +25085,7 @@ var RailwayProvider = class {
25014
25085
  if (!projectId || !serviceId) {
25015
25086
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
25016
25087
  }
25017
- const remoteDir = path46.posix.dirname(remotePath);
25088
+ const remoteDir = path47.posix.dirname(remotePath);
25018
25089
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
25019
25090
  if (options.mode != null) {
25020
25091
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -25557,8 +25628,8 @@ async function stopWorkspaceFromLocal(target) {
25557
25628
  var import_node_dns = require("dns");
25558
25629
  var import_node_util4 = require("util");
25559
25630
  var import_node_crypto8 = require("crypto");
25560
- var fs36 = __toESM(require("fs"));
25561
- var path47 = __toESM(require("path"));
25631
+ var fs37 = __toESM(require("fs"));
25632
+ var path48 = __toESM(require("path"));
25562
25633
  var import_picocolors12 = __toESM(require("picocolors"));
25563
25634
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
25564
25635
  async function checkDns(apiBase) {
@@ -25614,13 +25685,13 @@ async function checkHealth(apiBase) {
25614
25685
  }
25615
25686
  }
25616
25687
  function checkConfigDir() {
25617
- const dir = path47.join(require("os").homedir(), ".codeam");
25688
+ const dir = path48.join(require("os").homedir(), ".codeam");
25618
25689
  try {
25619
- fs36.mkdirSync(dir, { recursive: true, mode: 448 });
25620
- const probe = path47.join(dir, ".doctor-probe");
25621
- fs36.writeFileSync(probe, "ok", { mode: 384 });
25622
- const read = fs36.readFileSync(probe, "utf8");
25623
- fs36.unlinkSync(probe);
25690
+ fs37.mkdirSync(dir, { recursive: true, mode: 448 });
25691
+ const probe = path48.join(dir, ".doctor-probe");
25692
+ fs37.writeFileSync(probe, "ok", { mode: 384 });
25693
+ const read = fs37.readFileSync(probe, "utf8");
25694
+ fs37.unlinkSync(probe);
25624
25695
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
25625
25696
  return {
25626
25697
  id: "config-dir",
@@ -25684,7 +25755,7 @@ function checkNodePty() {
25684
25755
  detail: "not required on this platform"
25685
25756
  };
25686
25757
  }
25687
- const vendoredPath = path47.join(__dirname, "vendor", "node-pty");
25758
+ const vendoredPath = path48.join(__dirname, "vendor", "node-pty");
25688
25759
  for (const target of [vendoredPath, "node-pty"]) {
25689
25760
  try {
25690
25761
  require(target);
@@ -25726,7 +25797,7 @@ function checkChokidar() {
25726
25797
  }
25727
25798
  async function doctor(args2 = []) {
25728
25799
  const json = args2.includes("--json");
25729
- const cliVersion = true ? "2.33.0" : "0.0.0-dev";
25800
+ const cliVersion = true ? "2.34.0" : "0.0.0-dev";
25730
25801
  const apiBase = resolveApiBaseUrl();
25731
25802
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
25732
25803
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -25925,7 +25996,7 @@ async function completion(args2) {
25925
25996
  // src/commands/version.ts
25926
25997
  var import_picocolors13 = __toESM(require("picocolors"));
25927
25998
  function version2() {
25928
- const v = true ? "2.33.0" : "unknown";
25999
+ const v = true ? "2.34.0" : "unknown";
25929
26000
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
25930
26001
  }
25931
26002
 
@@ -26053,9 +26124,9 @@ function tryShowSubcommandHelp(cmd, args2) {
26053
26124
  var _subcommandHelpKeys = Object.keys(HELPS);
26054
26125
 
26055
26126
  // src/lib/updateNotifier.ts
26056
- var fs37 = __toESM(require("fs"));
26127
+ var fs38 = __toESM(require("fs"));
26057
26128
  var os29 = __toESM(require("os"));
26058
- var path48 = __toESM(require("path"));
26129
+ var path49 = __toESM(require("path"));
26059
26130
  var https8 = __toESM(require("https"));
26060
26131
  var import_node_child_process12 = require("child_process");
26061
26132
  var import_picocolors16 = __toESM(require("picocolors"));
@@ -26064,12 +26135,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
26064
26135
  var TTL_MS = 24 * 60 * 60 * 1e3;
26065
26136
  var REQUEST_TIMEOUT_MS = 1500;
26066
26137
  function cachePath() {
26067
- const dir = path48.join(os29.homedir(), ".codeam");
26068
- return path48.join(dir, "update-check.json");
26138
+ const dir = path49.join(os29.homedir(), ".codeam");
26139
+ return path49.join(dir, "update-check.json");
26069
26140
  }
26070
26141
  function readCache() {
26071
26142
  try {
26072
- const raw = fs37.readFileSync(cachePath(), "utf8");
26143
+ const raw = fs38.readFileSync(cachePath(), "utf8");
26073
26144
  const parsed = JSON.parse(raw);
26074
26145
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
26075
26146
  return parsed;
@@ -26080,10 +26151,10 @@ function readCache() {
26080
26151
  function writeCache(cache) {
26081
26152
  try {
26082
26153
  const file = cachePath();
26083
- fs37.mkdirSync(path48.dirname(file), { recursive: true });
26154
+ fs38.mkdirSync(path49.dirname(file), { recursive: true });
26084
26155
  const tmp = `${file}.${process.pid}.tmp`;
26085
- fs37.writeFileSync(tmp, JSON.stringify(cache));
26086
- fs37.renameSync(tmp, file);
26156
+ fs38.writeFileSync(tmp, JSON.stringify(cache));
26157
+ fs38.renameSync(tmp, file);
26087
26158
  } catch {
26088
26159
  }
26089
26160
  }
@@ -26157,8 +26228,8 @@ function isLinkedInstall() {
26157
26228
  timeout: 2e3
26158
26229
  }).trim();
26159
26230
  if (!root) return false;
26160
- const pkgPath = path48.join(root, PKG_NAME);
26161
- return fs37.lstatSync(pkgPath).isSymbolicLink();
26231
+ const pkgPath = path49.join(root, PKG_NAME);
26232
+ return fs38.lstatSync(pkgPath).isSymbolicLink();
26162
26233
  } catch {
26163
26234
  return false;
26164
26235
  }
@@ -26194,7 +26265,7 @@ function maybeAutoUpdate(currentVersion, latest) {
26194
26265
  return;
26195
26266
  }
26196
26267
  try {
26197
- fs37.unlinkSync(cachePath());
26268
+ fs38.unlinkSync(cachePath());
26198
26269
  } catch {
26199
26270
  }
26200
26271
  process.stderr.write(` ${import_picocolors16.default.green("\u2713")} Updated. Resuming session...
@@ -26211,7 +26282,7 @@ function checkForUpdates() {
26211
26282
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
26212
26283
  if (process.env.CI) return;
26213
26284
  if (!process.stdout.isTTY) return;
26214
- const current = true ? "2.33.0" : null;
26285
+ const current = true ? "2.34.0" : null;
26215
26286
  if (!current) return;
26216
26287
  const cache = readCache();
26217
26288
  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.33.0",
3
+ "version": "2.34.0",
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",