usebeeline 0.0.49 → 0.0.52

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/README.md CHANGED
@@ -148,7 +148,7 @@ Two MCP surfaces are mounted into every agent session.
148
148
  Beeline is on both stores:
149
149
 
150
150
  - [App Store](https://apps.apple.com/app/id6803948500)
151
- - [Google Play](https://play.google.com/store/apps/details?id=app.usebeeline.mobile)
151
+ - [Google Play](https://play.google.com/store/apps/details?id=app.usebeeline)
152
152
 
153
153
  Sign in with GitHub, and the app hands you the pairing code that `npx usebeeline connect` asks for.
154
154
 
@@ -309,12 +309,12 @@ __export(self_update_exports, {
309
309
  });
310
310
  import { createHash as createHash5 } from "node:crypto";
311
311
  import { constants as fsConstants } from "node:fs";
312
- import { access, chmod as chmod3, lstat as lstat2, mkdir as mkdir12, open, readFile as readFile9, rename as rename3, rm as rm5, symlink as symlink2, writeFile as writeFile8 } from "node:fs/promises";
312
+ import { access, chmod as chmod4, lstat as lstat2, mkdir as mkdir13, open, readFile as readFile9, rename as rename3, rm as rm5, symlink as symlink2, writeFile as writeFile9 } from "node:fs/promises";
313
313
  import { spawn as spawn4 } from "node:child_process";
314
314
  import { homedir as homedir9 } from "node:os";
315
- import { dirname as dirname9, join as join7, resolve as resolve20 } from "node:path";
315
+ import { dirname as dirname9, join as join7, resolve as resolve21 } from "node:path";
316
316
  function anchorLayout(rawLibDir) {
317
- const libDir = resolve20(rawLibDir);
317
+ const libDir = resolve21(rawLibDir);
318
318
  const segments = libDir.split(/[/\\]/);
319
319
  const idx = segments.lastIndexOf(RELEASES_SEGMENT);
320
320
  if (idx >= 2 && segments[idx - 1] === "lib") {
@@ -330,9 +330,9 @@ function anchorLayout(rawLibDir) {
330
330
  // prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
331
331
  // defect that made activateRelease write its stable forwarders where
332
332
  // nothing executed them, leaving stale raw wrappers in <prefix>/bin.
333
- binDir: resolve20(libDir, "../../bin"),
333
+ binDir: resolve21(libDir, "../../bin"),
334
334
  libDir,
335
- releasesRoot: resolve20(libDir, `../${RELEASES_SEGMENT}`)
335
+ releasesRoot: resolve21(libDir, `../${RELEASES_SEGMENT}`)
336
336
  };
337
337
  }
338
338
  function beelineInstallLayout(env = process.env) {
@@ -343,7 +343,7 @@ function beelineInstallLayout(env = process.env) {
343
343
  }
344
344
  function defaultBeelineInstallLayout(env = process.env) {
345
345
  const home = env.HOME?.trim() || homedir9();
346
- return anchorLayout(resolve20(home, ".local", "lib", "beeline"));
346
+ return anchorLayout(resolve21(home, ".local", "lib", "beeline"));
347
347
  }
348
348
  function hostPlatformKey() {
349
349
  const os = process.platform === "linux" ? "linux" : process.platform === "darwin" ? "darwin" : "";
@@ -387,8 +387,8 @@ async function readUpdateState(layout) {
387
387
  }
388
388
  }
389
389
  async function writeUpdateState(layout, state) {
390
- await mkdir12(join7(layout.releasesRoot, ".state"), { recursive: true });
391
- await writeFile8(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
390
+ await mkdir13(join7(layout.releasesRoot, ".state"), { recursive: true });
391
+ await writeFile9(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
392
392
  `, "utf8");
393
393
  }
394
394
  async function readInstalledBundleIdentity(layout, _state = {}) {
@@ -501,7 +501,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
501
501
  previouslyVerified = true;
502
502
  } catch {
503
503
  }
504
- await mkdir12(releaseDir, { recursive: true });
504
+ await mkdir13(releaseDir, { recursive: true });
505
505
  const tempArchive = join7(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
506
506
  try {
507
507
  log2(`downloading ${published.file}`);
@@ -522,7 +522,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
522
522
  if (actual !== published.sha256.toLowerCase()) {
523
523
  throw new Error(`checksum mismatch for ${published.file}: expected ${published.sha256}, got ${actual} \u2014 aborting without touching the installed bundle`);
524
524
  }
525
- await writeFile8(tempArchive, Buffer.concat(chunks), { mode: 384 });
525
+ await writeFile9(tempArchive, Buffer.concat(chunks), { mode: 384 });
526
526
  const entries = (await new Promise((resolveList, rejectList) => {
527
527
  const child = spawn4("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
528
528
  let out = "";
@@ -553,7 +553,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
553
553
  throw new Error(`staged bundle failed its startup smoke test (--version exited ${probe.status})${probe.stderr ? `: ${probe.stderr.trim()}` : ""}`);
554
554
  }
555
555
  }
556
- await writeFile8(okMarker, `${published.sha256}
556
+ await writeFile9(okMarker, `${published.sha256}
557
557
  `, "utf8");
558
558
  log2(`staged release ${releaseId} (sha256 verified)`);
559
559
  return releaseId;
@@ -581,15 +581,15 @@ function forwarderScript(tool) {
581
581
  }
582
582
  async function replaceFile(path, contents, mode) {
583
583
  const temp = `${path}.new-${process.pid}`;
584
- await writeFile8(temp, contents, { mode });
585
- await chmod3(temp, mode);
584
+ await writeFile9(temp, contents, { mode });
585
+ await chmod4(temp, mode);
586
586
  await rename3(temp, path);
587
587
  }
588
588
  async function activateRelease(layout, releaseId) {
589
589
  const releaseDir = join7(layout.releasesRoot, releaseId);
590
590
  await access(join7(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
591
- await mkdir12(layout.releasesRoot, { recursive: true });
592
- await mkdir12(layout.binDir, { recursive: true });
591
+ await mkdir13(layout.releasesRoot, { recursive: true });
592
+ await mkdir13(layout.binDir, { recursive: true });
593
593
  let previousReleaseId = await activeReleaseId(layout);
594
594
  const kind = await pathKind(layout.libDir);
595
595
  if (kind === "directory") {
@@ -628,7 +628,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
628
628
  }
629
629
  if (!anyFlat)
630
630
  return;
631
- await mkdir12(innerLib, { recursive: true });
631
+ await mkdir13(innerLib, { recursive: true });
632
632
  for (const name of LEGACY_FLAT_BUNDLE_FILES) {
633
633
  try {
634
634
  await access(join7(innerLib, name), fsConstants.F_OK);
@@ -661,7 +661,7 @@ async function repairInstallForwarders(layout, opts = {}) {
661
661
  }
662
662
  if (current === forwarderScript("beeline"))
663
663
  return false;
664
- await mkdir12(layout.binDir, { recursive: true });
664
+ await mkdir13(layout.binDir, { recursive: true });
665
665
  await writeBinForwarders(layout, layout.libDir);
666
666
  opts.logger?.(`[body] self-update: repaired <prefix>/bin forwarders to follow the active-bundle anchor (${layout.libDir})`);
667
667
  return true;
@@ -693,10 +693,10 @@ async function readUpdateAttempt(layout) {
693
693
  }
694
694
  }
695
695
  async function writeUpdateAttempt(layout, record2) {
696
- await mkdir12(join7(layout.releasesRoot, ".state"), { recursive: true });
696
+ await mkdir13(join7(layout.releasesRoot, ".state"), { recursive: true });
697
697
  const path = updateAttemptPath(layout);
698
698
  const staged = `${path}.${process.pid}.tmp`;
699
- await writeFile8(staged, `${JSON.stringify(record2, null, 2)}
699
+ await writeFile9(staged, `${JSON.stringify(record2, null, 2)}
700
700
  `, { mode: 384 });
701
701
  await rename3(staged, path);
702
702
  }
@@ -955,8 +955,8 @@ var init_self_update = __esm({
955
955
  });
956
956
 
957
957
  // apps/body/dist/cli.js
958
- import { dirname as dirname15, resolve as resolve27 } from "node:path";
959
- import { readFile as readFile14, unlink as unlink3, writeFile as writeFile14 } from "node:fs/promises";
958
+ import { dirname as dirname15, resolve as resolve28 } from "node:path";
959
+ import { readFile as readFile14, unlink as unlink3, writeFile as writeFile15 } from "node:fs/promises";
960
960
  import { stdin as stdin4, stdout as stdout5 } from "node:process";
961
961
 
962
962
  // node_modules/@clack/core/dist/index.mjs
@@ -3832,7 +3832,7 @@ var AcpClient = class extends EventEmitter {
3832
3832
  const current = this.activeRunIds.get(sessionId);
3833
3833
  if (current)
3834
3834
  return Promise.resolve(current);
3835
- return new Promise((resolve28, reject) => {
3835
+ return new Promise((resolve29, reject) => {
3836
3836
  const onUpdate = (update) => {
3837
3837
  if (update.sessionId !== sessionId)
3838
3838
  return;
@@ -3840,7 +3840,7 @@ var AcpClient = class extends EventEmitter {
3840
3840
  if (!runId)
3841
3841
  return;
3842
3842
  cleanup();
3843
- resolve28(runId);
3843
+ resolve29(runId);
3844
3844
  };
3845
3845
  const timer = setTimeout(() => {
3846
3846
  cleanup();
@@ -3924,13 +3924,13 @@ var AcpClient = class extends EventEmitter {
3924
3924
  }
3925
3925
  const id = this.nextId++;
3926
3926
  const payload = { jsonrpc: "2.0", id, method, params };
3927
- return new Promise((resolve28, reject) => {
3927
+ return new Promise((resolve29, reject) => {
3928
3928
  const timer = setTimeout(() => {
3929
3929
  this.pending.delete(id);
3930
3930
  reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
3931
3931
  }, timeoutMs);
3932
3932
  this.pending.set(id, {
3933
- resolve: resolve28,
3933
+ resolve: resolve29,
3934
3934
  reject,
3935
3935
  timer,
3936
3936
  method,
@@ -13283,8 +13283,8 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
13283
13283
  import { execFile as execFile4 } from "node:child_process";
13284
13284
  import { createHash as createHash4 } from "node:crypto";
13285
13285
  import { existsSync as existsSync4, mkdirSync } from "node:fs";
13286
- import { mkdir as mkdir10, rm as rm4 } from "node:fs/promises";
13287
- import { dirname as dirname6, resolve as resolve18 } from "node:path";
13286
+ import { mkdir as mkdir11, rm as rm4 } from "node:fs/promises";
13287
+ import { dirname as dirname6, resolve as resolve19 } from "node:path";
13288
13288
  import { promisify as promisify3 } from "node:util";
13289
13289
 
13290
13290
  // apps/body/dist/grant-runner.js
@@ -14126,7 +14126,7 @@ var GrantRunnerServer = class {
14126
14126
 
14127
14127
  // apps/body/dist/monolith-corner-turn.js
14128
14128
  import { execFile as execFile3 } from "node:child_process";
14129
- import { mkdir as mkdir8 } from "node:fs/promises";
14129
+ import { mkdir as mkdir9 } from "node:fs/promises";
14130
14130
  import { homedir as homedir6 } from "node:os";
14131
14131
  import { join as join5 } from "node:path";
14132
14132
  import { promisify as promisify2 } from "node:util";
@@ -14141,6 +14141,39 @@ import { basename as basename3, dirname as dirname5, join as join3, relative as
14141
14141
  // apps/body/dist/beeline-skill.js
14142
14142
  import { readFileSync as readFileSync3 } from "node:fs";
14143
14143
  import { resolve as resolve11 } from "node:path";
14144
+
14145
+ // packages/api-contract/dist/system-events.js
14146
+ var SERVER_EVENT_KINDS = [
14147
+ "joined",
14148
+ "schedule-ran",
14149
+ "corner-opened",
14150
+ "check-passed",
14151
+ "check-failed",
14152
+ "merged",
14153
+ "grant-decided"
14154
+ ];
14155
+ function isServerEventKind(value) {
14156
+ return SERVER_EVENT_KINDS.includes(value);
14157
+ }
14158
+ var RESUME_KINDS = ["grant-decided"];
14159
+ function isResumeKind(value) {
14160
+ return RESUME_KINDS.includes(value);
14161
+ }
14162
+
14163
+ // packages/api-contract/dist/agent-pairing-code.js
14164
+ var CURRENT_AGENT_PAIRING_CODE = /^[0-9A-F]{8}-[0-9A-F]{8}$/;
14165
+ var LEGACY_AGENT_PAIRING_CODE = /^BUZZ-(?:[0-9A-F]{8}-[0-9A-F]{8}|[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4})$/;
14166
+ function normalizeAgentPairingCode(value) {
14167
+ if (typeof value !== "string")
14168
+ return void 0;
14169
+ const normalized = value.trim().toUpperCase();
14170
+ return isAgentPairingCode(normalized) ? normalized : void 0;
14171
+ }
14172
+ function isAgentPairingCode(value) {
14173
+ return typeof value === "string" && (CURRENT_AGENT_PAIRING_CODE.test(value) || LEGACY_AGENT_PAIRING_CODE.test(value));
14174
+ }
14175
+
14176
+ // apps/body/dist/beeline-skill.js
14144
14177
  var USING_BEELINE_SKILL_NAME = "using-beeline";
14145
14178
  var BEELINE_ROOM_CAPABILITIES = [
14146
14179
  "The repository filesystem is read-only in this Room session.",
@@ -14151,7 +14184,7 @@ var BEELINE_ROOM_CAPABILITIES = [
14151
14184
  "Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
14152
14185
  "To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent attach_file with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote); it is attached to your reply. write_scratch_file produces the file, not a picture - turning it into a raster image needs a converter, which needs shell, which this Room does not have.",
14153
14186
  "To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
14154
- "To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (joined, schedule-ran, corner-opened, check-passed, check-failed, merged); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you.",
14187
+ `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
14155
14188
  "To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
14156
14189
  "When repository work is needed, you MUST call beeline-agent open_corner with a name of at most three words - it titles the corner everywhere - and a complete objective of no more than 24 words. The host-governed call is the only way to start write work.",
14157
14190
  "When open_corner succeeds, the server posts the corner card: do not announce or restate the opening. End the turn with nothing more unless the person asked something else.",
@@ -15111,6 +15144,7 @@ async function resolveSharedSkillSources(operatorHome, names) {
15111
15144
  return resolveExplicitSkillSources(operatorHome, names);
15112
15145
  const seen = /* @__PURE__ */ new Set();
15113
15146
  const resolved = [];
15147
+ const skipped = [];
15114
15148
  for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
15115
15149
  const sourceRoot = resolve13(operatorHome, relativeRoot);
15116
15150
  const rootStats = await lstat(sourceRoot).catch(() => void 0);
@@ -15122,23 +15156,61 @@ async function resolveSharedSkillSources(operatorHome, names) {
15122
15156
  const candidate = resolve13(sourceRoot, entry);
15123
15157
  try {
15124
15158
  const candidateStats = await lstat(candidate);
15125
- if (!candidateStats.isDirectory() || candidateStats.isSymbolicLink())
15159
+ if (candidateStats.isSymbolicLink()) {
15160
+ const reason = await realpath(candidate).then(() => "symlinked directory", (error) => isMissingPathError(error) ? "dangling symlink" : "symlinked directory");
15161
+ skipped.push({
15162
+ path: candidate,
15163
+ reason
15164
+ });
15165
+ continue;
15166
+ }
15167
+ if (!candidateStats.isDirectory()) {
15168
+ skipped.push({ path: candidate, reason: "not an ordinary directory" });
15126
15169
  continue;
15170
+ }
15127
15171
  assertContained(sourceRoot, candidate);
15128
15172
  const skillMd = resolve13(candidate, "SKILL.md");
15129
- const skillStats = await lstat(skillMd);
15173
+ const skillStats = await lstat(skillMd).catch((error) => {
15174
+ if (isMissingPathError(error)) {
15175
+ skipped.push({ path: candidate, reason: "missing SKILL.md" });
15176
+ return void 0;
15177
+ }
15178
+ throw error;
15179
+ });
15180
+ if (!skillStats)
15181
+ continue;
15130
15182
  if (!skillStats.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
15131
- throw new Error(`shared skill requires an ordinary SKILL.md: ${entry}`);
15183
+ skipped.push({ path: candidate, reason: "SKILL.md is not an ordinary file" });
15184
+ continue;
15132
15185
  }
15133
15186
  seen.add(entry);
15134
15187
  resolved.push({ name: entry, source: candidate });
15135
15188
  } catch (error) {
15189
+ if (isMissingPathError(error)) {
15190
+ skipped.push({ path: candidate, reason: "missing during discovery" });
15191
+ continue;
15192
+ }
15136
15193
  console.warn(`[body] skipping operator skill ${entry}:`, error);
15137
15194
  }
15138
15195
  }
15139
15196
  }
15197
+ logSkippedOperatorSkills(skipped);
15140
15198
  return resolved;
15141
15199
  }
15200
+ function isMissingPathError(error) {
15201
+ return typeof error === "object" && error !== null && error.code === "ENOENT";
15202
+ }
15203
+ function logSkippedOperatorSkills(skipped) {
15204
+ const totals = /* @__PURE__ */ new Map();
15205
+ for (const { path, reason } of skipped) {
15206
+ console.warn(`[body] skipping operator skill ${path}: ${reason}`);
15207
+ totals.set(reason, (totals.get(reason) ?? 0) + 1);
15208
+ }
15209
+ for (const [reason, count] of totals) {
15210
+ if (count > 1)
15211
+ console.warn(`[body] ${count} skill entries skipped: ${reason}`);
15212
+ }
15213
+ }
15142
15214
  async function resolveExplicitSkillSources(operatorHome, names) {
15143
15215
  const unique = [...new Set(names)];
15144
15216
  for (const name of unique) {
@@ -16610,6 +16682,57 @@ var TurnTraceFile = class {
16610
16682
  }
16611
16683
  };
16612
16684
 
16685
+ // apps/body/dist/corner-github-auth.js
16686
+ import { chmod as chmod3, mkdir as mkdir8, writeFile as writeFile7 } from "node:fs/promises";
16687
+ import { delimiter as delimiter2, resolve as resolve18 } from "node:path";
16688
+ async function installCornerGitHubWrappers(input) {
16689
+ const bin = resolve18(input.root, "beeline-github-bin");
16690
+ await mkdir8(bin, { recursive: true, mode: 448 });
16691
+ const common = {
16692
+ node: process.execPath,
16693
+ cli: input.cliEntrypoint,
16694
+ config: input.runtimeConfigPath,
16695
+ room: input.roomId
16696
+ };
16697
+ await writeLauncher(resolve18(bin, "git"), { ...common, command: input.gitBinary });
16698
+ if (input.ghBinary)
16699
+ await writeLauncher(resolve18(bin, "gh"), { ...common, command: input.ghBinary });
16700
+ return {
16701
+ PATH: [bin, input.inheritedPath].filter(Boolean).join(delimiter2),
16702
+ // Static startup tokens take precedence over the refreshed token in gh.
16703
+ GH_TOKEN: "",
16704
+ GITHUB_TOKEN: ""
16705
+ };
16706
+ }
16707
+ async function writeLauncher(path, config) {
16708
+ const source = `#!/usr/bin/env node
16709
+ import { spawnSync } from 'node:child_process';
16710
+ const config = ${JSON.stringify(config)};
16711
+ const authFailure = /(?:authentication failed|bad credentials|could not read username|http(?:\\/\\d(?:\\.\\d)?)? 40[13]|status (?:code )?40[13])/i;
16712
+ function token() {
16713
+ const result = spawnSync(config.node, [config.cli, 'corner-read-token', '--config', config.config, '--room', config.room], { encoding: 'utf8' });
16714
+ if (result.status !== 0) {
16715
+ process.stderr.write(result.stderr || 'Beeline could not refresh the repository credential.\\n');
16716
+ process.exit(result.status || 1);
16717
+ }
16718
+ return result.stdout.trim();
16719
+ }
16720
+ function run(value) {
16721
+ const env = { ...process.env, GH_TOKEN: value, GITHUB_TOKEN: value, GIT_TERMINAL_PROMPT: '0' };
16722
+ return spawnSync(config.command, process.argv.slice(2), { env, encoding: 'buffer', stdio: ['inherit', 'pipe', 'pipe'] });
16723
+ }
16724
+ let result = run(token());
16725
+ const diagnostic = Buffer.concat([result.stdout || Buffer.alloc(0), result.stderr || Buffer.alloc(0)]).toString('utf8');
16726
+ if (result.status !== 0 && authFailure.test(diagnostic)) result = run(token());
16727
+ if (result.stdout) process.stdout.write(result.stdout);
16728
+ if (result.stderr) process.stderr.write(result.stderr);
16729
+ if (result.error) throw result.error;
16730
+ process.exit(result.status ?? 1);
16731
+ `;
16732
+ await writeFile7(path, source, { mode: 448 });
16733
+ await chmod3(path, 448);
16734
+ }
16735
+
16613
16736
  // apps/body/dist/monolith-corner-turn.js
16614
16737
  var execFileAsync2 = promisify2(execFile3);
16615
16738
  var TOOL_ARGUMENT_MAX_BYTES = 1200;
@@ -16866,7 +16989,7 @@ var MonolithCornerTurnLoop = class {
16866
16989
  soul: configuration.soul ?? self?.soul,
16867
16990
  agentName: self?.name ?? this.agent.name
16868
16991
  });
16869
- await mkdir8(this.options.worktreePath, { recursive: true });
16992
+ await mkdir9(this.options.worktreePath, { recursive: true });
16870
16993
  const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
16871
16994
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
16872
16995
  root: this.options.config.agentHomeRoot,
@@ -16883,11 +17006,27 @@ var MonolithCornerTurnLoop = class {
16883
17006
  })
16884
17007
  }) : {};
16885
17008
  const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
17009
+ let githubEnv = {
17010
+ GH_TOKEN: this.options.githubToken,
17011
+ GITHUB_TOKEN: this.options.githubToken
17012
+ };
17013
+ if (this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
17014
+ const gitBinary = (await execFileAsync2("which", ["git"])).stdout.trim();
17015
+ const ghBinary = await execFileAsync2("which", ["gh"]).then((result) => result.stdout.trim()).catch(() => void 0);
17016
+ githubEnv = await installCornerGitHubWrappers({
17017
+ root: this.options.config.agentHomeRoot,
17018
+ runtimeConfigPath: this.options.config.runtimeConfigPath,
17019
+ roomId: this.options.parentRoomId,
17020
+ cliEntrypoint: process.argv[1],
17021
+ gitBinary,
17022
+ ...ghBinary ? { ghBinary } : {},
17023
+ inheritedPath: this.options.config.agentEnv.PATH ?? process.env.PATH
17024
+ });
17025
+ }
16886
17026
  const agentEnv = {
16887
17027
  ...this.options.config.agentEnv,
16888
17028
  ...homeOverlay,
16889
- GH_TOKEN: this.options.githubToken,
16890
- GITHUB_TOKEN: this.options.githubToken
17029
+ ...githubEnv
16891
17030
  };
16892
17031
  this.agentEnv = agentEnv;
16893
17032
  const agentArgs = agentArgsWithModelSelection({
@@ -16900,7 +17039,7 @@ var MonolithCornerTurnLoop = class {
16900
17039
  this.attachmentDir = tmpDir ? join5(tmpDir, "beeline-attachments") : void 0;
16901
17040
  this.sessionScratchDir = tmpDir;
16902
17041
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
16903
- await Promise.all(homeStateDirs.map((dir) => mkdir8(dir, { recursive: true })));
17042
+ await Promise.all(homeStateDirs.map((dir) => mkdir9(dir, { recursive: true })));
16904
17043
  const spawnCommand = wrapAgentCommand({
16905
17044
  bwrapPath: this.options.config.bwrapPath,
16906
17045
  spec: {
@@ -17336,7 +17475,7 @@ async function wait(ms, signal) {
17336
17475
  }
17337
17476
 
17338
17477
  // apps/body/dist/monolith-room-turn.js
17339
- import { mkdir as mkdir9 } from "node:fs/promises";
17478
+ import { mkdir as mkdir10 } from "node:fs/promises";
17340
17479
  import { homedir as homedir7 } from "node:os";
17341
17480
  import { join as join6 } from "node:path";
17342
17481
 
@@ -17344,24 +17483,6 @@ import { join as join6 } from "node:path";
17344
17483
  var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
17345
17484
  var SCHEDULE_RAN_VERB = "ran a schedule for";
17346
17485
 
17347
- // packages/api-contract/dist/system-events.js
17348
- var SERVER_EVENT_KINDS = [
17349
- "joined",
17350
- "schedule-ran",
17351
- "corner-opened",
17352
- "check-passed",
17353
- "check-failed",
17354
- "merged",
17355
- "grant-decided"
17356
- ];
17357
- function isServerEventKind(value) {
17358
- return SERVER_EVENT_KINDS.includes(value);
17359
- }
17360
- var RESUME_KINDS = ["grant-decided"];
17361
- function isResumeKind(value) {
17362
- return RESUME_KINDS.includes(value);
17363
- }
17364
-
17365
17486
  // apps/body/dist/monolith-room-turn.js
17366
17487
  function isRoomMcpPermissionRequest(request, mountedServers = ROOM_MOUNTED_MCP_SERVERS) {
17367
17488
  if (isSquireMcpPermissionRequest(request))
@@ -17434,6 +17555,15 @@ function agentReplyMentionIds(text2, roster, authorId) {
17434
17555
  aliases.set(key, entry);
17435
17556
  }
17436
17557
  }
17558
+ for (const member of roster.members) {
17559
+ if (member.identityId === authorId || !member.handle)
17560
+ continue;
17561
+ const handle = member.handle.trim().replace(/^@/, "").toLocaleLowerCase();
17562
+ const canonical = aliases.get(handle);
17563
+ const legacy = `a_${handle}`;
17564
+ if (canonical && !aliases.has(legacy))
17565
+ aliases.set(legacy, { ...canonical, display: legacy });
17566
+ }
17437
17567
  const mentioned = [];
17438
17568
  for (const { display, ids } of [...aliases.values()].sort((left, right) => right.display.length - left.display.length)) {
17439
17569
  if (ids.size !== 1)
@@ -17651,7 +17781,7 @@ var MonolithRoomTurnLoop = class {
17651
17781
  agentName: self?.name ?? this.agent.name
17652
17782
  });
17653
17783
  const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
17654
- await mkdir9(this.options.cwd, { recursive: true });
17784
+ await mkdir10(this.options.cwd, { recursive: true });
17655
17785
  const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
17656
17786
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
17657
17787
  root: this.options.config.agentHomeRoot,
@@ -17681,7 +17811,7 @@ var MonolithRoomTurnLoop = class {
17681
17811
  this.sessionScratchDir = tmpDir;
17682
17812
  this.sessionStateDirs = stateDirs;
17683
17813
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
17684
- await Promise.all(homeStateDirs.map((dir) => mkdir9(dir, { recursive: true })));
17814
+ await Promise.all(homeStateDirs.map((dir) => mkdir10(dir, { recursive: true })));
17685
17815
  const spawnCommand = wrapAgentCommand({
17686
17816
  bwrapPath: this.options.config.bwrapPath,
17687
17817
  spec: {
@@ -18730,13 +18860,13 @@ var RoomRuntimeCoordinator = class {
18730
18860
  return this.runtime.rooms.find((room) => room.channelId === roomId);
18731
18861
  }
18732
18862
  roomRoot(roomId) {
18733
- return this.roomRecord(roomId)?.root ?? resolve18(dirname6(this.configPath), "rooms", roomId);
18863
+ return this.roomRecord(roomId)?.root ?? resolve19(dirname6(this.configPath), "rooms", roomId);
18734
18864
  }
18735
18865
  roomAgentHomeRoot(workspaceRoot, required = false) {
18736
18866
  const flag = process.env.BUZZY_BODY_ROOM_HOME;
18737
18867
  if (!required && flag === "0")
18738
18868
  return void 0;
18739
- const home = resolve18(workspaceRoot, "agent-home");
18869
+ const home = resolve19(workspaceRoot, "agent-home");
18740
18870
  if (!required && flag !== "1" && !existsSync4(home) && existsSync4(workspaceRoot))
18741
18871
  return void 0;
18742
18872
  try {
@@ -18753,8 +18883,8 @@ var RoomRuntimeCoordinator = class {
18753
18883
  return {
18754
18884
  ...this.baseConfig,
18755
18885
  workspaceRoot,
18756
- agentPrivateRoot: resolve18(workspaceRoot, "agent-private"),
18757
- agentMemoryRoot: resolve18(dirname6(this.configPath), "memory"),
18886
+ agentPrivateRoot: resolve19(workspaceRoot, "agent-private"),
18887
+ agentMemoryRoot: resolve19(dirname6(this.configPath), "memory"),
18758
18888
  openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname6(this.configPath)),
18759
18889
  turnTraceDir: turnTraceDirectory(dirname6(this.configPath)),
18760
18890
  ...agentHomeRoot ? { agentHomeRoot } : {}
@@ -18823,11 +18953,11 @@ var RoomRuntimeCoordinator = class {
18823
18953
  const remote = roomCheckoutRemote(repository.remote);
18824
18954
  const targetBranch = repository.targetBranch || "main";
18825
18955
  const checkoutId = createHash4("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
18826
- const path = resolve18(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
18827
- await mkdir10(dirname6(path), { recursive: true, mode: 448 });
18956
+ const path = resolve19(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
18957
+ await mkdir11(dirname6(path), { recursive: true, mode: 448 });
18828
18958
  const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
18829
18959
  const env = token ? githubGitEnv(token.token) : process.env;
18830
- if (!existsSync4(resolve18(path, ".git"))) {
18960
+ if (!existsSync4(resolve19(path, ".git"))) {
18831
18961
  await execFileAsync3("git", ["clone", "--no-checkout", remote, path], {
18832
18962
  env,
18833
18963
  maxBuffer: 4 * 1024 * 1024
@@ -18952,12 +19082,12 @@ var RoomRuntimeCoordinator = class {
18952
19082
  async materializeCornerWorktree(input) {
18953
19083
  const remote = githubHttpsRemote(input.remote);
18954
19084
  const repositoryHash = createHash4("sha256").update(remote).digest("hex").slice(0, 24);
18955
- const gitCommonDir = resolve18(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
18956
- const path = resolve18(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
18957
- await mkdir10(dirname6(gitCommonDir), { recursive: true, mode: 448 });
18958
- await mkdir10(dirname6(path), { recursive: true, mode: 448 });
19085
+ const gitCommonDir = resolve19(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
19086
+ const path = resolve19(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
19087
+ await mkdir11(dirname6(gitCommonDir), { recursive: true, mode: 448 });
19088
+ await mkdir11(dirname6(path), { recursive: true, mode: 448 });
18959
19089
  const authEnv = githubGitEnv(input.token);
18960
- if (!existsSync4(resolve18(gitCommonDir, "HEAD"))) {
19090
+ if (!existsSync4(resolve19(gitCommonDir, "HEAD"))) {
18961
19091
  await execFileAsync3("git", ["clone", "--bare", remote, gitCommonDir], {
18962
19092
  env: authEnv,
18963
19093
  maxBuffer: 4 * 1024 * 1024
@@ -18970,7 +19100,7 @@ var RoomRuntimeCoordinator = class {
18970
19100
  "origin",
18971
19101
  `+refs/heads/${input.targetBranch}:refs/remotes/origin/${input.targetBranch}`
18972
19102
  ], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
18973
- if (!existsSync4(resolve18(path, ".git"))) {
19103
+ if (!existsSync4(resolve19(path, ".git"))) {
18974
19104
  await rm4(path, { recursive: true, force: true });
18975
19105
  await execFileAsync3("git", [
18976
19106
  `--git-dir=${gitCommonDir}`,
@@ -19007,7 +19137,7 @@ var RoomRuntimeCoordinator = class {
19007
19137
  `${this.agent.publicKey.slice(0, 16)}@users.noreply.github.com`
19008
19138
  ]);
19009
19139
  const top = await execFileAsync3("git", ["-C", path, "rev-parse", "--show-toplevel"]);
19010
- if (resolve18(top.stdout.trim()) !== resolve18(path)) {
19140
+ if (resolve19(top.stdout.trim()) !== resolve19(path)) {
19011
19141
  throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
19012
19142
  }
19013
19143
  return { path, gitCommonDir };
@@ -19185,9 +19315,9 @@ var ThinDaemonCore = class {
19185
19315
 
19186
19316
  // apps/body/dist/systemd.js
19187
19317
  import { execFile as execFile5 } from "node:child_process";
19188
- import { mkdir as mkdir11, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
19318
+ import { mkdir as mkdir12, readFile as readFile8, writeFile as writeFile8 } from "node:fs/promises";
19189
19319
  import { homedir as homedir8 } from "node:os";
19190
- import { dirname as dirname7, resolve as resolve19 } from "node:path";
19320
+ import { dirname as dirname7, resolve as resolve20 } from "node:path";
19191
19321
  import { setTimeout as sleep } from "node:timers/promises";
19192
19322
  import { promisify as promisify4 } from "node:util";
19193
19323
  var execFileAsync4 = promisify4(execFile5);
@@ -19229,9 +19359,9 @@ WantedBy=default.target
19229
19359
  }
19230
19360
  function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
19231
19361
  const home = env.HOME?.trim() || homedir8();
19232
- const expectedLibDir = resolve19(home, ".local", "lib", "beeline");
19362
+ const expectedLibDir = resolve20(home, ".local", "lib", "beeline");
19233
19363
  const expectedPrefix = `${expectedLibDir}/`;
19234
- return resolve19(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve19(invocationPath).startsWith(expectedPrefix);
19364
+ return resolve20(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve20(invocationPath).startsWith(expectedPrefix);
19235
19365
  }
19236
19366
  function assertCanonicalInstalledLauncher(env, invocationPath) {
19237
19367
  if (isCanonicalInstalledLauncher(env, invocationPath))
@@ -19239,8 +19369,8 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
19239
19369
  throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
19240
19370
  }
19241
19371
  function systemdUserUnitPath(env = process.env) {
19242
- const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve19(homedir8(), ".config");
19243
- return resolve19(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
19372
+ const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve20(homedir8(), ".config");
19373
+ return resolve20(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
19244
19374
  }
19245
19375
  var runSystemctl = async (args) => {
19246
19376
  const result = await execFileAsync4("systemctl", ["--user", ...args], {
@@ -19258,8 +19388,8 @@ async function installAgentService(publicKey, options = {}) {
19258
19388
  const content = agentServiceUnit();
19259
19389
  const existing = await readFile8(path, "utf8").catch(() => "");
19260
19390
  if (existing !== content) {
19261
- await mkdir11(dirname7(path), { recursive: true, mode: 448 });
19262
- await writeFile7(path, content, { mode: 384 });
19391
+ await mkdir12(dirname7(path), { recursive: true, mode: 448 });
19392
+ await writeFile8(path, content, { mode: 384 });
19263
19393
  }
19264
19394
  const run2 = options.run ?? runSystemctl;
19265
19395
  await run2(["daemon-reload"]);
@@ -19431,23 +19561,10 @@ async function runStartCommand(args, interactiveUi) {
19431
19561
  // apps/body/dist/connect-command.js
19432
19562
  import { spawn as spawn5 } from "node:child_process";
19433
19563
  import { createHash as createHash6 } from "node:crypto";
19434
- import { chmod as chmod4, mkdir as mkdir13, readFile as readFile10, unlink as unlink2, writeFile as writeFile9 } from "node:fs/promises";
19435
- import { dirname as dirname10, resolve as resolve21 } from "node:path";
19564
+ import { chmod as chmod5, mkdir as mkdir14, readFile as readFile10, unlink as unlink2, writeFile as writeFile10 } from "node:fs/promises";
19565
+ import { dirname as dirname10, resolve as resolve22 } from "node:path";
19436
19566
  import { stdin as stdin3, stdout as stdout4 } from "node:process";
19437
19567
 
19438
- // packages/api-contract/dist/agent-pairing-code.js
19439
- var CURRENT_AGENT_PAIRING_CODE = /^[0-9A-F]{8}-[0-9A-F]{8}$/;
19440
- var LEGACY_AGENT_PAIRING_CODE = /^BUZZ-(?:[0-9A-F]{8}-[0-9A-F]{8}|[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4})$/;
19441
- function normalizeAgentPairingCode(value) {
19442
- if (typeof value !== "string")
19443
- return void 0;
19444
- const normalized = value.trim().toUpperCase();
19445
- return isAgentPairingCode(normalized) ? normalized : void 0;
19446
- }
19447
- function isAgentPairingCode(value) {
19448
- return typeof value === "string" && (CURRENT_AGENT_PAIRING_CODE.test(value) || LEGACY_AGENT_PAIRING_CODE.test(value));
19449
- }
19450
-
19451
19568
  // apps/body/dist/clack-support.js
19452
19569
  import { stdout as stdout2 } from "node:process";
19453
19570
  var FALLBACK_TERMINAL_COLUMNS = 80;
@@ -20066,7 +20183,7 @@ async function installCurrentRelease(fetchImpl) {
20066
20183
  });
20067
20184
  await activateRelease(layout, releaseId);
20068
20185
  return {
20069
- binary: resolve21(layout.binDir, "beeline"),
20186
+ binary: resolve22(layout.binDir, "beeline"),
20070
20187
  version: published.version ?? releaseId
20071
20188
  };
20072
20189
  }
@@ -20089,21 +20206,21 @@ function providerEnvironment(selection) {
20089
20206
  };
20090
20207
  }
20091
20208
  async function writePrivateJson(path, value) {
20092
- await mkdir13(dirname10(path), { recursive: true, mode: 448 });
20093
- await writeFile9(path, `${JSON.stringify(value, null, 2)}
20209
+ await mkdir14(dirname10(path), { recursive: true, mode: 448 });
20210
+ await writeFile10(path, `${JSON.stringify(value, null, 2)}
20094
20211
  `, { mode: 384 });
20095
- await chmod4(path, 384);
20212
+ await chmod5(path, 384);
20096
20213
  }
20097
20214
  async function writeProviderEnv(selection, agentPubkey) {
20098
20215
  const values = providerEnvironment(selection);
20099
20216
  if (Object.keys(values).length === 0)
20100
20217
  return void 0;
20101
- const path = resolve21(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
20102
- await mkdir13(dirname10(path), { recursive: true, mode: 448 });
20218
+ const path = resolve22(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
20219
+ await mkdir14(dirname10(path), { recursive: true, mode: 448 });
20103
20220
  const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
20104
- await writeFile9(path, `${contents}
20221
+ await writeFile10(path, `${contents}
20105
20222
  `, { mode: 384 });
20106
- await chmod4(path, 384);
20223
+ await chmod5(path, 384);
20107
20224
  return path;
20108
20225
  }
20109
20226
  async function runInstalledFinish(binary, grantPath) {
@@ -20164,7 +20281,7 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
20164
20281
  await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
20165
20282
  const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
20166
20283
  const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
20167
- const grantPath = resolve21(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
20284
+ const grantPath = resolve22(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
20168
20285
  await writePrivateJson(grantPath, {
20169
20286
  agentSecretKey: grant.agent_secret_key,
20170
20287
  bodySecretKey: grant.body_secret_key,
@@ -20246,11 +20363,11 @@ async function runConnectFinishCommand(path) {
20246
20363
  throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
20247
20364
  }
20248
20365
  try {
20249
- const grant = JSON.parse(await readFile10(resolve21(path), "utf8"));
20366
+ const grant = JSON.parse(await readFile10(resolve22(path), "utf8"));
20250
20367
  if (!isDevicePairingGrant(grant))
20251
20368
  throw new Error("device connection grant is invalid");
20252
20369
  const connected = await completeDevicePairing(grant);
20253
- await unlink2(resolve21(path));
20370
+ await unlink2(resolve22(path));
20254
20371
  const providerEnv = grant.llmEnvFile ? await readFile10(grant.llmEnvFile, "utf8").catch(() => "") : "";
20255
20372
  const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
20256
20373
  const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
@@ -20279,20 +20396,20 @@ init_self_update_manifest();
20279
20396
  // apps/body/dist/managed-update.js
20280
20397
  init_self_update();
20281
20398
  import { spawn as spawn6 } from "node:child_process";
20282
- import { mkdir as mkdir15, rm as rm6, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
20283
- import { dirname as dirname12, resolve as resolve23 } from "node:path";
20399
+ import { mkdir as mkdir16, rm as rm6, stat as stat2, writeFile as writeFile12 } from "node:fs/promises";
20400
+ import { dirname as dirname12, resolve as resolve24 } from "node:path";
20284
20401
 
20285
20402
  // apps/body/dist/update-rollback-alert.js
20286
- import { mkdir as mkdir14, readFile as readFile11, rename as rename4, writeFile as writeFile10 } from "node:fs/promises";
20287
- import { dirname as dirname11, resolve as resolve22 } from "node:path";
20403
+ import { mkdir as mkdir15, readFile as readFile11, rename as rename4, writeFile as writeFile11 } from "node:fs/promises";
20404
+ import { dirname as dirname11, resolve as resolve23 } from "node:path";
20288
20405
  function updateRollbackAlertPath(runtimeDir) {
20289
- return resolve22(runtimeDir, "update-rollback-alert.json");
20406
+ return resolve23(runtimeDir, "update-rollback-alert.json");
20290
20407
  }
20291
20408
  async function writeAlert(runtimeDir, alert) {
20292
20409
  const path = updateRollbackAlertPath(runtimeDir);
20293
20410
  const staged = `${path}.${process.pid}.tmp`;
20294
- await mkdir14(dirname11(path), { recursive: true });
20295
- await writeFile10(staged, `${JSON.stringify(alert, null, 2)}
20411
+ await mkdir15(dirname11(path), { recursive: true });
20412
+ await writeFile11(staged, `${JSON.stringify(alert, null, 2)}
20296
20413
  `, { mode: 384 });
20297
20414
  await rename4(staged, path);
20298
20415
  }
@@ -20329,13 +20446,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
20329
20446
  var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
20330
20447
  async function withInstallLock(layout, work, options = {}) {
20331
20448
  const now2 = options.now ?? Date.now;
20332
- const lock = resolve23(layout.releasesRoot, ".state", "install.lock");
20449
+ const lock = resolve24(layout.releasesRoot, ".state", "install.lock");
20333
20450
  const deadline = now2() + (options.waitMs ?? 1e4);
20334
- await mkdir15(dirname12(lock), { recursive: true });
20451
+ await mkdir16(dirname12(lock), { recursive: true });
20335
20452
  for (; ; ) {
20336
20453
  try {
20337
- await mkdir15(lock);
20338
- await writeFile11(resolve23(lock, "owner"), `${process.pid}
20454
+ await mkdir16(lock);
20455
+ await writeFile12(resolve24(lock, "owner"), `${process.pid}
20339
20456
  ${now2()}
20340
20457
  `, "utf8");
20341
20458
  break;
@@ -20458,7 +20575,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
20458
20575
  if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
20459
20576
  const from = await readInstalledBundleIdentity({
20460
20577
  ...this.#layout,
20461
- libDir: resolve23(this.#layout.releasesRoot, this.#loadedRelease)
20578
+ libDir: resolve24(this.#layout.releasesRoot, this.#loadedRelease)
20462
20579
  }).catch(() => void 0) ?? {};
20463
20580
  const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
20464
20581
  const record2 = {
@@ -20745,7 +20862,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
20745
20862
  });
20746
20863
  if (!accepted)
20747
20864
  return false;
20748
- await writeFile11(resolve23(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
20865
+ await writeFile12(resolve24(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
20749
20866
  readyAt: (options.now ?? Date.now)(),
20750
20867
  loadedRelease,
20751
20868
  functionalProof: options.functionalProof
@@ -20753,39 +20870,59 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
20753
20870
  `, { mode: 384 });
20754
20871
  return true;
20755
20872
  }
20873
+ function siblingRevertNote(attempt, loadedRelease) {
20874
+ if (!loadedRelease || attempt?.releaseId !== loadedRelease || attempt.status !== "reverted") {
20875
+ return void 0;
20876
+ }
20877
+ return `shared update attempt reverted by sibling ${attempt.revertedBy ?? "unknown"}: ${attempt.failure ?? "no failure recorded"}`;
20878
+ }
20879
+ function describeOwnProof(proof) {
20880
+ return proof.modelAnswer === "unavailable" ? `this daemon's own probe reached the model boundary without an answer (${proof.modelAnswerReason ?? "no reason recorded"})` : "this daemon's own probe passed";
20881
+ }
20882
+ function attemptFailureText(error) {
20883
+ const message = error instanceof Error ? error.message : String(error);
20884
+ return message.replace(/\s+/g, " ").trim().slice(0, 300);
20885
+ }
20756
20886
  async function gateManagedSuccessor(input) {
20757
20887
  try {
20758
20888
  const attempt = await readUpdateAttempt(input.layout);
20759
20889
  const desiredRelease = attempt?.status === "pending" ? attempt.releaseId : void 0;
20760
20890
  if (!desiredRelease || !input.loadedRelease || desiredRelease !== input.loadedRelease) {
20761
- throw new Error(`successor loaded release ${input.loadedRelease ?? "unknown"}, not the pending desired release`);
20891
+ const reverted = siblingRevertNote(attempt, input.loadedRelease);
20892
+ throw new Error(reverted ? `successor release ${input.loadedRelease} was reverted before this daemon probed it: ${reverted}` : `successor loaded release ${input.loadedRelease ?? "unknown"}, not the pending desired release`);
20762
20893
  }
20763
20894
  const proof = await input.probe();
20764
20895
  if (!await proveLoadedReleaseReady(input.layout, input.runtimeDir, input.loadedRelease, {
20765
20896
  probeId: input.probeId,
20766
20897
  functionalProof: proof
20767
20898
  })) {
20768
- throw new Error(`successor release ${input.loadedRelease} did not produce functional proof`);
20899
+ const reverted = siblingRevertNote(await readUpdateAttempt(input.layout), input.loadedRelease);
20900
+ throw new Error(reverted ? `successor release ${input.loadedRelease} lost its attempt while this daemon proved it: ${describeOwnProof(proof)}; ${reverted}` : `successor release ${input.loadedRelease} did not produce functional proof`);
20769
20901
  }
20770
20902
  return { kind: "passed", proof };
20771
20903
  } catch (error) {
20772
20904
  return {
20773
20905
  kind: "failed",
20774
20906
  error,
20775
- rolledBack: await rollbackFailedSuccessor(input.layout, input.runtimeDir)
20907
+ rolledBack: await rollbackFailedSuccessor(input.layout, input.runtimeDir, {
20908
+ probeId: input.probeId,
20909
+ failure: attemptFailureText(error)
20910
+ })
20776
20911
  };
20777
20912
  }
20778
20913
  }
20779
- async function rollbackFailedSuccessor(layout, runtimeDir) {
20914
+ async function rollbackFailedSuccessor(layout, runtimeDir, cause = {}) {
20780
20915
  return withInstallLock(layout, async () => {
20781
20916
  const attempt = await readUpdateAttempt(layout);
20782
20917
  if (!attempt || attempt.status !== "pending")
20783
20918
  return false;
20919
+ const attribution = cause.probeId ? { revertedBy: cause.probeId } : {};
20784
20920
  if (!attempt.previousReleaseId) {
20785
20921
  await replaceUpdateAttempt(layout, {
20786
20922
  ...attempt,
20787
20923
  status: "reverted",
20788
- failure: "functional served-turn proof failed, but no previous release exists"
20924
+ failure: cause.failure ?? "functional served-turn proof failed, but no previous release exists",
20925
+ ...attribution
20789
20926
  });
20790
20927
  return false;
20791
20928
  }
@@ -20793,7 +20930,8 @@ async function rollbackFailedSuccessor(layout, runtimeDir) {
20793
20930
  await replaceUpdateAttempt(layout, {
20794
20931
  ...attempt,
20795
20932
  status: "reverted",
20796
- failure: "functional served-turn proof failed before confirmation"
20933
+ failure: cause.failure ?? "functional served-turn proof failed before confirmation",
20934
+ ...attribution
20797
20935
  });
20798
20936
  if (runtimeDir)
20799
20937
  await queueUpdateRollbackAlert(runtimeDir, attempt.releaseId);
@@ -20950,12 +21088,12 @@ async function runUpdateCommand(args) {
20950
21088
  init_self_update();
20951
21089
 
20952
21090
  // apps/body/dist/daemon-failure.js
20953
- import { mkdir as mkdir16, readFile as readFile12, rename as rename5, rm as rm7, writeFile as writeFile12 } from "node:fs/promises";
20954
- import { dirname as dirname13, resolve as resolve24 } from "node:path";
21091
+ import { mkdir as mkdir17, readFile as readFile12, rename as rename5, rm as rm7, writeFile as writeFile13 } from "node:fs/promises";
21092
+ import { dirname as dirname13, resolve as resolve25 } from "node:path";
20955
21093
  var DAEMON_FAILURE_LIMIT = 3;
20956
21094
  var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
20957
21095
  function daemonFailurePath(runtimeDir) {
20958
- return resolve24(runtimeDir, "daemon-distress.json");
21096
+ return resolve25(runtimeDir, "daemon-distress.json");
20959
21097
  }
20960
21098
  async function readFailureRecord(runtimeDir) {
20961
21099
  try {
@@ -20971,8 +21109,8 @@ async function readFailureRecord(runtimeDir) {
20971
21109
  async function writeFailureRecord(runtimeDir, record2) {
20972
21110
  const path = daemonFailurePath(runtimeDir);
20973
21111
  const staged = `${path}.${process.pid}.tmp`;
20974
- await mkdir16(dirname13(path), { recursive: true, mode: 448 });
20975
- await writeFile12(staged, `${JSON.stringify(record2, null, 2)}
21112
+ await mkdir17(dirname13(path), { recursive: true, mode: 448 });
21113
+ await writeFile13(staged, `${JSON.stringify(record2, null, 2)}
20976
21114
  `, { mode: 384 });
20977
21115
  await rename5(staged, path);
20978
21116
  }
@@ -20994,12 +21132,34 @@ async function clearDaemonStartFailures(runtimeDir) {
20994
21132
  }
20995
21133
 
20996
21134
  // apps/body/dist/update-functional-probe.js
20997
- import { mkdir as mkdir17, rm as rm8 } from "node:fs/promises";
21135
+ import { mkdir as mkdir18, rm as rm8 } from "node:fs/promises";
20998
21136
  import { homedir as homedir10 } from "node:os";
20999
- import { resolve as resolve25 } from "node:path";
21137
+ import { resolve as resolve26 } from "node:path";
21000
21138
  var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
21001
21139
  var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
21002
21140
  var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
21141
+ var ACP_ERROR_CODE = /\bACP error (-\d+):/;
21142
+ var ACP_PROMPT_INACTIVITY = /\bACP session\/prompt timed out after \d+ms of inactivity\b/;
21143
+ function isServerInternalCode(code) {
21144
+ return code === -32603 || code <= -32e3 && code >= -32099;
21145
+ }
21146
+ function classifyAcpTurnFailure(error) {
21147
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
21148
+ if (!message)
21149
+ return void 0;
21150
+ if (ACP_PROMPT_INACTIVITY.test(message))
21151
+ return { kind: "prompt-inactivity" };
21152
+ const code = Number(ACP_ERROR_CODE.exec(message)?.[1]);
21153
+ if (Number.isFinite(code) && isServerInternalCode(code))
21154
+ return { kind: "server-internal", code };
21155
+ return void 0;
21156
+ }
21157
+ function sameAcpTurnFailure(one, other) {
21158
+ if (one.kind === "server-internal") {
21159
+ return other.kind === "server-internal" && one.code === other.code;
21160
+ }
21161
+ return other.kind === one.kind;
21162
+ }
21003
21163
  var UpdateFunctionalProbeError = class extends Error {
21004
21164
  reason;
21005
21165
  code = "BEELINE_UPDATE_FUNCTIONAL_PROBE_FAILED";
@@ -21042,11 +21202,11 @@ async function runUpdateFunctionalProbe(input) {
21042
21202
  if (input.sandboxRequired && !input.config.bwrapPath) {
21043
21203
  throw new UpdateFunctionalProbeError("sandbox-unavailable", "the configured bubblewrap boundary did not pass its startup self-test");
21044
21204
  }
21045
- const root = input.probeRoot ?? resolve25(input.runtimeDir, "update-functional-probe");
21046
- const cwd = resolve25(root, "checkout");
21047
- const homeRoot = resolve25(root, "agent-home");
21205
+ const root = input.probeRoot ?? resolve26(input.runtimeDir, "update-functional-probe");
21206
+ const cwd = resolve26(root, "checkout");
21207
+ const homeRoot = resolve26(root, "agent-home");
21048
21208
  await rm8(root, { recursive: true, force: true });
21049
- await mkdir17(cwd, { recursive: true, mode: 448 });
21209
+ await mkdir18(cwd, { recursive: true, mode: 448 });
21050
21210
  let client;
21051
21211
  try {
21052
21212
  const agentEnv = {
@@ -21071,11 +21231,12 @@ async function runUpdateFunctionalProbe(input) {
21071
21231
  args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
21072
21232
  };
21073
21233
  let modelAnswer = {};
21234
+ let turnCompleted = true;
21074
21235
  if (input.config.bwrapPath) {
21075
21236
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
21076
21237
  const operatorHome = input.config.operatorHome ?? homedir10();
21077
21238
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
21078
- await Promise.all(homeStateDirs.map((dir) => mkdir17(dir, { recursive: true })));
21239
+ await Promise.all(homeStateDirs.map((dir) => mkdir18(dir, { recursive: true })));
21079
21240
  spawnCommand = wrapAgentCommand({
21080
21241
  bwrapPath: input.config.bwrapPath,
21081
21242
  spec: {
@@ -21132,7 +21293,11 @@ async function runUpdateFunctionalProbe(input) {
21132
21293
  ...refusal ? { providerRefusal: refusal } : {}
21133
21294
  });
21134
21295
  }
21135
- const current = await input.compareWithCurrentRelease(refusal);
21296
+ const current = await input.compareWithCurrentRelease({
21297
+ kind: "provider-refusal",
21298
+ reason: refusal.reason,
21299
+ refusal
21300
+ });
21136
21301
  if (current.kind !== "refused" || current.status !== refusal.status) {
21137
21302
  throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { providerRefusal: refusal });
21138
21303
  }
@@ -21147,7 +21312,26 @@ async function runUpdateFunctionalProbe(input) {
21147
21312
  } catch (error) {
21148
21313
  if (error instanceof UpdateFunctionalProbeError)
21149
21314
  throw error;
21150
- throw new UpdateFunctionalProbeError("turn-failed", error instanceof Error ? error.message : String(error), { cause: error });
21315
+ const detail = error instanceof Error ? error.message : String(error);
21316
+ const failure = classifyAcpTurnFailure(error);
21317
+ if (!failure || !input.compareWithCurrentRelease) {
21318
+ throw new UpdateFunctionalProbeError("turn-failed", detail, { cause: error });
21319
+ }
21320
+ const current = await input.compareWithCurrentRelease({
21321
+ kind: "acp-turn-failure",
21322
+ reason: detail,
21323
+ failure
21324
+ });
21325
+ const currentFailure = current.kind === "unavailable" ? classifyAcpTurnFailure(current.reason) : void 0;
21326
+ if (!currentFailure || !sameAcpTurnFailure(failure, currentFailure)) {
21327
+ throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { cause: error });
21328
+ }
21329
+ console.warn(`[body] update probe: the probe turn failed the same way on this release and the current release (${detail}); that failure is not this bundle's doing, so the probe passes as inconclusive`);
21330
+ turnCompleted = false;
21331
+ modelAnswer = {
21332
+ modelAnswer: "unavailable",
21333
+ modelAnswerReason: `${detail} (the current release fails the same way)`
21334
+ };
21151
21335
  }
21152
21336
  } catch (error) {
21153
21337
  if (error instanceof UpdateFunctionalProbeError)
@@ -21158,7 +21342,7 @@ async function runUpdateFunctionalProbe(input) {
21158
21342
  harness,
21159
21343
  sandboxed: Boolean(input.config.bwrapPath),
21160
21344
  sessionStarted: true,
21161
- turnCompleted: true,
21345
+ turnCompleted,
21162
21346
  nativeTools: [],
21163
21347
  ...modelAnswer
21164
21348
  };
@@ -21172,7 +21356,7 @@ async function runUpdateFunctionalProbe(input) {
21172
21356
  import { spawn as spawn7 } from "node:child_process";
21173
21357
  import { dirname as dirname14, join as join8 } from "node:path";
21174
21358
  init_self_update();
21175
- var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 8e4;
21359
+ var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
21176
21360
  var UPDATE_PROBE_COMMAND = "update-probe";
21177
21361
  function parseReport(line) {
21178
21362
  let parsed;
@@ -21211,7 +21395,7 @@ async function probeReleaseInSubprocess(input) {
21211
21395
  return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
21212
21396
  }
21213
21397
  const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
21214
- return new Promise((resolve28) => {
21398
+ return new Promise((resolve29) => {
21215
21399
  const child = spawn7(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
21216
21400
  let stdout6 = "";
21217
21401
  let stderr = "";
@@ -21221,7 +21405,7 @@ async function probeReleaseInSubprocess(input) {
21221
21405
  return;
21222
21406
  settled = true;
21223
21407
  clearTimeout(timer);
21224
- resolve28(outcome);
21408
+ resolve29(outcome);
21225
21409
  };
21226
21410
  const timer = setTimeout(() => {
21227
21411
  child.kill("SIGKILL");
@@ -21300,8 +21484,8 @@ async function runUpdateProbeCommand(args, options = {}) {
21300
21484
  }
21301
21485
 
21302
21486
  // apps/body/dist/release-status.js
21303
- import { readFile as readFile13, readdir as readdir5, rename as rename6, writeFile as writeFile13 } from "node:fs/promises";
21304
- import { resolve as resolve26 } from "node:path";
21487
+ import { readFile as readFile13, readdir as readdir5, rename as rename6, writeFile as writeFile14 } from "node:fs/promises";
21488
+ import { resolve as resolve27 } from "node:path";
21305
21489
  var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
21306
21490
  var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
21307
21491
  var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
@@ -21318,9 +21502,9 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
21318
21502
  pid: options.pid ?? process.pid,
21319
21503
  readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
21320
21504
  };
21321
- const target = resolve26(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
21505
+ const target = resolve27(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
21322
21506
  const temporary = `${target}.${status.pid}.tmp`;
21323
- await writeFile13(temporary, `${JSON.stringify(status, null, 2)}
21507
+ await writeFile14(temporary, `${JSON.stringify(status, null, 2)}
21324
21508
  `, { mode: 384 });
21325
21509
  await rename6(temporary, target);
21326
21510
  return status;
@@ -21378,7 +21562,7 @@ async function runStoredDaemon(pathOrPointer) {
21378
21562
  runtime = activated.runtime;
21379
21563
  const daemonApi = activated.client;
21380
21564
  const agent = runtimeAgentCommand(runtime);
21381
- await writeFile14(resolve27(dirname15(configPath), "daemon.pid"), `${process.pid}
21565
+ await writeFile15(resolve28(dirname15(configPath), "daemon.pid"), `${process.pid}
21382
21566
  `, { mode: 384 });
21383
21567
  const env = {
21384
21568
  ...process.env,
@@ -21386,7 +21570,7 @@ async function runStoredDaemon(pathOrPointer) {
21386
21570
  BUZZ_DEV_MCP_BIN: runtime.mcpBinary
21387
21571
  };
21388
21572
  const config = loadBodyConfig({
21389
- workspaceRoot: resolve27(dirname15(configPath), "workspace"),
21573
+ workspaceRoot: resolve28(dirname15(configPath), "workspace"),
21390
21574
  llmEnvFile: runtime.llmEnvFile,
21391
21575
  env,
21392
21576
  agent
@@ -21500,8 +21684,8 @@ async function runStoredDaemon(pathOrPointer) {
21500
21684
  releaseId: loadedRelease ?? "unknown",
21501
21685
  sandboxRequired: runtime.sandbox !== "off",
21502
21686
  ...currentReleaseId ? {
21503
- compareWithCurrentRelease: async (refusal) => {
21504
- console.warn(`[thin-core] successor probe refused by the provider (${refusal.reason}); probing the current release ${currentReleaseId} for the same refusal`);
21687
+ compareWithCurrentRelease: async (appeal) => {
21688
+ console.warn(`[thin-core] successor probe got no answer from the provider (${appeal.reason}); probing the current release ${currentReleaseId} for the same outcome`);
21505
21689
  await extendSystemdStartTimeout(CURRENT_RELEASE_PROBE_TIMEOUT_MS + 15e3);
21506
21690
  return probeReleaseInSubprocess({
21507
21691
  layout,
@@ -21547,7 +21731,12 @@ async function runStoredDaemon(pathOrPointer) {
21547
21731
  console.log(`[beeline] agent ${runtime.agent.publicKey} removed; runtime archived at ${archivedRuntime}`);
21548
21732
  }
21549
21733
  } catch (error) {
21550
- const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && await rollbackFailedSuccessor(layout, runtimeDir);
21734
+ const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && // Named, so a sibling daemon's journal can say whose failure reverted
21735
+ // the attempt it shares with this one.
21736
+ await rollbackFailedSuccessor(layout, runtimeDir, {
21737
+ probeId: runtime.agent.publicKey,
21738
+ failure: attemptFailureText(error)
21739
+ });
21551
21740
  if (rolledBack) {
21552
21741
  console.error("[thin-core] successor failed before READY; previous release restored once");
21553
21742
  const alertRoom = runtime.rooms[0]?.channelId;
@@ -21556,7 +21745,7 @@ async function runStoredDaemon(pathOrPointer) {
21556
21745
  throw error;
21557
21746
  } finally {
21558
21747
  await notifier.stopping(stoppingStatus).catch(() => void 0);
21559
- const pidPath = resolve27(dirname15(configPath), "daemon.pid");
21748
+ const pidPath = resolve28(dirname15(configPath), "daemon.pid");
21560
21749
  const recorded = Number((await readFile14(pidPath, "utf8").catch(() => "")).trim());
21561
21750
  if (recorded === process.pid) {
21562
21751
  await unlink3(pidPath).catch(() => void 0);
@@ -21577,6 +21766,21 @@ async function main() {
21577
21766
  console.log(JSON.stringify(await runManagedUpdateWorker()));
21578
21767
  return;
21579
21768
  }
21769
+ if (command === "corner-read-token") {
21770
+ const configFlag = args.indexOf("--config");
21771
+ const roomFlag = args.indexOf("--room");
21772
+ const configPath = configFlag >= 0 ? args[configFlag + 1] : void 0;
21773
+ const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
21774
+ if (!configPath || !roomId)
21775
+ throw new Error("corner-read-token requires --config and --room");
21776
+ const activated = await activateDaemonTransport(resolve28(configPath));
21777
+ if (!activated)
21778
+ throw new Error("corner-read-token requires monolith transport");
21779
+ const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
21780
+ process.stdout.write(`${credential.token}
21781
+ `);
21782
+ return;
21783
+ }
21580
21784
  if (command === "connect") {
21581
21785
  const subscribeFlag = args.indexOf("--subscribe");
21582
21786
  const subscribe = subscribeFlag >= 0 ? parseConnectSubscriptions(args[subscribeFlag + 1]) : [];
@@ -21631,7 +21835,7 @@ async function main() {
21631
21835
  }
21632
21836
  if (!configPath)
21633
21837
  throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
21634
- await runStoredDaemon(resolve27(configPath));
21838
+ await runStoredDaemon(resolve28(configPath));
21635
21839
  return;
21636
21840
  }
21637
21841
  if (command === "update") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.49",
3
+ "version": "0.0.52",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {