usebeeline 0.0.37 → 0.0.40

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 (2) hide show
  1. package/dist/usebeeline.mjs +1425 -449
  2. package/package.json +1 -1
@@ -307,14 +307,14 @@ __export(self_update_exports, {
307
307
  writeUpdateAttemptFixture: () => writeUpdateAttemptFixture,
308
308
  writeUpdateState: () => writeUpdateState
309
309
  });
310
- import { createHash as createHash2 } from "node:crypto";
310
+ import { createHash as createHash3 } from "node:crypto";
311
311
  import { constants as fsConstants } from "node:fs";
312
- import { access, chmod as chmod3, lstat as lstat2, mkdir as mkdir9, open, readFile as readFile4, rename as rename3, rm as rm4, symlink as symlink2, writeFile as writeFile6 } from "node:fs/promises";
312
+ import { access, chmod as chmod3, lstat as lstat2, mkdir as mkdir10, open, readFile as readFile8, rename as rename3, rm as rm4, symlink as symlink2, writeFile as writeFile8 } 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 dirname8, join as join6, resolve as resolve14 } from "node:path";
315
+ import { dirname as dirname9, join as join6, resolve as resolve18 } from "node:path";
316
316
  function anchorLayout(rawLibDir) {
317
- const libDir = resolve14(rawLibDir);
317
+ const libDir = resolve18(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: resolve14(libDir, "../../bin"),
333
+ binDir: resolve18(libDir, "../../bin"),
334
334
  libDir,
335
- releasesRoot: resolve14(libDir, `../${RELEASES_SEGMENT}`)
335
+ releasesRoot: resolve18(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(resolve14(home, ".local", "lib", "beeline"));
346
+ return anchorLayout(resolve18(home, ".local", "lib", "beeline"));
347
347
  }
348
348
  function hostPlatformKey() {
349
349
  const os = process.platform === "linux" ? "linux" : process.platform === "darwin" ? "darwin" : "";
@@ -359,7 +359,7 @@ async function readBundleJson(bundleDir) {
359
359
  let raw;
360
360
  for (const candidate of bundleJsonCandidates(bundleDir)) {
361
361
  try {
362
- raw = await readFile4(candidate, "utf8");
362
+ raw = await readFile8(candidate, "utf8");
363
363
  break;
364
364
  } catch {
365
365
  }
@@ -381,14 +381,14 @@ function updateStatePath(layout) {
381
381
  }
382
382
  async function readUpdateState(layout) {
383
383
  try {
384
- return JSON.parse(await readFile4(updateStatePath(layout), "utf8"));
384
+ return JSON.parse(await readFile8(updateStatePath(layout), "utf8"));
385
385
  } catch {
386
386
  return {};
387
387
  }
388
388
  }
389
389
  async function writeUpdateState(layout, state) {
390
- await mkdir9(join6(layout.releasesRoot, ".state"), { recursive: true });
391
- await writeFile6(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
390
+ await mkdir10(join6(layout.releasesRoot, ".state"), { recursive: true });
391
+ await writeFile8(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
392
392
  `, "utf8");
393
393
  }
394
394
  async function readInstalledBundleIdentity(layout, _state = {}) {
@@ -495,13 +495,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
495
495
  const okMarker = join6(releaseDir, ".stage-ok");
496
496
  let previouslyVerified = false;
497
497
  try {
498
- const recorded = await readFile4(okMarker, "utf8");
498
+ const recorded = await readFile8(okMarker, "utf8");
499
499
  if (recorded.trim() === published.sha256)
500
500
  return releaseId;
501
501
  previouslyVerified = true;
502
502
  } catch {
503
503
  }
504
- await mkdir9(releaseDir, { recursive: true });
504
+ await mkdir10(releaseDir, { recursive: true });
505
505
  const tempArchive = join6(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
506
506
  try {
507
507
  log(`downloading ${published.file}`);
@@ -511,7 +511,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
511
511
  if (!response.ok || !response.body) {
512
512
  throw new Error(`downloading ${published.file} failed: HTTP ${response.status}`);
513
513
  }
514
- const hash = createHash2("sha256");
514
+ const hash = createHash3("sha256");
515
515
  const chunks = [];
516
516
  for await (const chunk of response.body) {
517
517
  const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
@@ -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 writeFile6(tempArchive, Buffer.concat(chunks), { mode: 384 });
525
+ await writeFile8(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 writeFile6(okMarker, `${published.sha256}
556
+ await writeFile8(okMarker, `${published.sha256}
557
557
  `, "utf8");
558
558
  log(`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 writeFile6(temp, contents, { mode });
584
+ await writeFile8(temp, contents, { mode });
585
585
  await chmod3(temp, mode);
586
586
  await rename3(temp, path);
587
587
  }
588
588
  async function activateRelease(layout, releaseId) {
589
589
  const releaseDir = join6(layout.releasesRoot, releaseId);
590
590
  await access(join6(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
591
- await mkdir9(layout.releasesRoot, { recursive: true });
592
- await mkdir9(layout.binDir, { recursive: true });
591
+ await mkdir10(layout.releasesRoot, { recursive: true });
592
+ await mkdir10(layout.binDir, { recursive: true });
593
593
  let previousReleaseId = await activeReleaseId(layout);
594
594
  const kind = await pathKind(layout.libDir);
595
595
  if (kind === "directory") {
@@ -611,7 +611,7 @@ async function activateRelease(layout, releaseId) {
611
611
  await rm4(tempLink, { force: true });
612
612
  await symlink2(join6("beeline-releases", releaseId), tempLink);
613
613
  await rename3(tempLink, layout.libDir);
614
- await fsyncDir(dirname8(layout.libDir));
614
+ await fsyncDir(dirname9(layout.libDir));
615
615
  await writeBinForwarders(layout, releaseDir);
616
616
  return { previousReleaseId };
617
617
  }
@@ -628,7 +628,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
628
628
  }
629
629
  if (!anyFlat)
630
630
  return;
631
- await mkdir9(innerLib, { recursive: true });
631
+ await mkdir10(innerLib, { recursive: true });
632
632
  for (const name of LEGACY_FLAT_BUNDLE_FILES) {
633
633
  try {
634
634
  await access(join6(innerLib, name), fsConstants.F_OK);
@@ -655,13 +655,13 @@ async function repairInstallForwarders(layout, opts = {}) {
655
655
  const forwarderPath = join6(layout.binDir, "beeline");
656
656
  let current;
657
657
  try {
658
- current = await readFile4(forwarderPath, "utf8");
658
+ current = await readFile8(forwarderPath, "utf8");
659
659
  } catch {
660
660
  current = void 0;
661
661
  }
662
662
  if (current === forwarderScript("beeline"))
663
663
  return false;
664
- await mkdir9(layout.binDir, { recursive: true });
664
+ await mkdir10(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;
@@ -676,14 +676,14 @@ async function rollbackToPreviousRelease(layout, previousReleaseId) {
676
676
  await rm4(tempLink, { force: true });
677
677
  await symlink2(join6("beeline-releases", previousReleaseId), tempLink);
678
678
  await rename3(tempLink, layout.libDir);
679
- await fsyncDir(dirname8(layout.libDir));
679
+ await fsyncDir(dirname9(layout.libDir));
680
680
  }
681
681
  function updateAttemptPath(layout) {
682
682
  return join6(layout.releasesRoot, ".state", "update-attempt.json");
683
683
  }
684
684
  async function readUpdateAttempt(layout) {
685
685
  try {
686
- const raw = JSON.parse(await readFile4(updateAttemptPath(layout), "utf8"));
686
+ const raw = JSON.parse(await readFile8(updateAttemptPath(layout), "utf8"));
687
687
  if (raw.version !== 1 || typeof raw.appliedAt !== "number" || typeof raw.confirmBy !== "number" || typeof raw.releaseId !== "string" || !["pending", "confirmed", "reverted"].includes(raw.status)) {
688
688
  return void 0;
689
689
  }
@@ -693,10 +693,10 @@ async function readUpdateAttempt(layout) {
693
693
  }
694
694
  }
695
695
  async function writeUpdateAttempt(layout, record2) {
696
- await mkdir9(join6(layout.releasesRoot, ".state"), { recursive: true });
696
+ await mkdir10(join6(layout.releasesRoot, ".state"), { recursive: true });
697
697
  const path = updateAttemptPath(layout);
698
698
  const staged = `${path}.${process.pid}.tmp`;
699
- await writeFile6(staged, `${JSON.stringify(record2, null, 2)}
699
+ await writeFile8(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 dirname13, resolve as resolve21 } from "node:path";
959
- import { readFile as readFile9, unlink as unlink3, writeFile as writeFile12 } from "node:fs/promises";
958
+ import { dirname as dirname14, resolve as resolve25 } from "node:path";
959
+ import { readFile as readFile13, unlink as unlink3, writeFile as writeFile14 } 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
@@ -3099,6 +3099,9 @@ function agentMessageChunkText(update) {
3099
3099
  }
3100
3100
  var CHUNK_CONTINUES_PREVIOUS_WORD = /^[\s'\u2018\u2019\u02bc.,!?;:%)\]}-]/;
3101
3101
  var PI_ACP_HARNESS = /(^|[/\\])pi-acp(?:\.[a-z]+)?$/i;
3102
+ function isPiAcpHarness(agentLabel) {
3103
+ return Boolean(agentLabel && PI_ACP_HARNESS.test(agentLabel));
3104
+ }
3102
3105
  function withoutOneTrailingLineEnding(text2) {
3103
3106
  if (/\r?\n\r?\n$/.test(text2))
3104
3107
  return text2;
@@ -3166,6 +3169,19 @@ function finalAgentMessageText(updates, agentLabel) {
3166
3169
  return "";
3167
3170
  return last;
3168
3171
  }
3172
+ function describeEmptyTurn(result, agentLabel) {
3173
+ const counts = /* @__PURE__ */ new Map();
3174
+ for (const { update } of result.updates) {
3175
+ const kind = typeof update.sessionUpdate === "string" ? update.sessionUpdate : "unknown";
3176
+ if (kind === "session_info_update" || kind === "available_commands_update")
3177
+ continue;
3178
+ counts.set(kind, (counts.get(kind) ?? 0) + 1);
3179
+ }
3180
+ const stream = counts.size ? `the stream carried only ${[...counts].map(([kind, count]) => `${kind}\xD7${count}`).join(", ")}` : "the stream carried no content updates";
3181
+ const lastRun = agentMessageRuns(result.updates, agentLabel).at(-1);
3182
+ const narration = lastRun && isPureRetryNarration(lastRun) ? `; the last message was retry narration "${lastRun.trim().slice(0, 80)}"` : "";
3183
+ return `harness ended the turn (${result.stopReason}) with no answer text; ${stream}${narration}`;
3184
+ }
3169
3185
  function updateText(update) {
3170
3186
  const content = update.content;
3171
3187
  if (typeof content === "string")
@@ -3186,7 +3202,7 @@ var THOUGHT_UPDATE_TYPES = /* @__PURE__ */ new Set([
3186
3202
  ]);
3187
3203
  function agentStreamSnapshot(updates, agentLabel) {
3188
3204
  const completedMessages = [];
3189
- let messageText = "";
3205
+ let messageText2 = "";
3190
3206
  let lastWasMessage = false;
3191
3207
  let thoughtText = "";
3192
3208
  let thoughtRunOpen = false;
@@ -3196,20 +3212,20 @@ function agentStreamSnapshot(updates, agentLabel) {
3196
3212
  const delta = normalizeStreamDelta(agentMessageChunkText(update), agentLabel);
3197
3213
  if (!delta)
3198
3214
  continue;
3199
- if (!lastWasMessage && messageText && !/\s$/.test(messageText) && !CHUNK_CONTINUES_PREVIOUS_WORD.test(delta)) {
3200
- if (!completedMessages.includes(messageText))
3201
- completedMessages.push(messageText);
3202
- messageText = "";
3215
+ if (!lastWasMessage && messageText2 && !/\s$/.test(messageText2) && !CHUNK_CONTINUES_PREVIOUS_WORD.test(delta)) {
3216
+ if (!completedMessages.includes(messageText2))
3217
+ completedMessages.push(messageText2);
3218
+ messageText2 = "";
3203
3219
  }
3204
- messageText += delta;
3220
+ messageText2 += delta;
3205
3221
  lastWasMessage = true;
3206
3222
  thoughtRunOpen = false;
3207
3223
  continue;
3208
3224
  }
3209
- if (lastWasMessage && messageText) {
3210
- if (!completedMessages.includes(messageText))
3211
- completedMessages.push(messageText);
3212
- messageText = "";
3225
+ if (lastWasMessage && messageText2) {
3226
+ if (!completedMessages.includes(messageText2))
3227
+ completedMessages.push(messageText2);
3228
+ messageText2 = "";
3213
3229
  }
3214
3230
  lastWasMessage = false;
3215
3231
  if (THOUGHT_UPDATE_TYPES.has(kind)) {
@@ -3225,7 +3241,7 @@ function agentStreamSnapshot(updates, agentLabel) {
3225
3241
  }
3226
3242
  }
3227
3243
  return {
3228
- messageText,
3244
+ messageText: messageText2,
3229
3245
  ...thoughtText || completedMessages.length ? { thoughtText: thoughtText || completedMessages.at(-1) } : {}
3230
3246
  };
3231
3247
  }
@@ -3720,7 +3736,7 @@ var AcpClient = class extends EventEmitter {
3720
3736
  const current = this.activeRunIds.get(sessionId);
3721
3737
  if (current)
3722
3738
  return Promise.resolve(current);
3723
- return new Promise((resolve22, reject) => {
3739
+ return new Promise((resolve26, reject) => {
3724
3740
  const onUpdate = (update) => {
3725
3741
  if (update.sessionId !== sessionId)
3726
3742
  return;
@@ -3728,7 +3744,7 @@ var AcpClient = class extends EventEmitter {
3728
3744
  if (!runId)
3729
3745
  return;
3730
3746
  cleanup();
3731
- resolve22(runId);
3747
+ resolve26(runId);
3732
3748
  };
3733
3749
  const timer = setTimeout(() => {
3734
3750
  cleanup();
@@ -3748,23 +3764,23 @@ var AcpClient = class extends EventEmitter {
3748
3764
  const tracked = metadataKey ? this.toolCallMetadata.get(metadataKey) : void 0;
3749
3765
  if (tracked)
3750
3766
  p.toolCall = { ...tracked, ...p.toolCall };
3751
- let decision = this.autoApprove ? "allow" : "reject";
3767
+ let decision2 = this.autoApprove ? "allow" : "reject";
3752
3768
  if (!this.autoApprove && this.permissionAllowlist) {
3753
3769
  try {
3754
- decision = this.permissionAllowlist(p) ? "allow" : "reject";
3770
+ decision2 = this.permissionAllowlist(p) ? "allow" : "reject";
3755
3771
  } catch (error) {
3756
3772
  this.emit("permission/error", error);
3757
- decision = "reject";
3773
+ decision2 = "reject";
3758
3774
  }
3759
3775
  } else if (this.permissionHandler) {
3760
3776
  try {
3761
- decision = await this.permissionHandler(p);
3777
+ decision2 = await this.permissionHandler(p);
3762
3778
  } catch (error) {
3763
3779
  this.emit("permission/error", error);
3764
- decision = "reject";
3780
+ decision2 = "reject";
3765
3781
  }
3766
3782
  }
3767
- if (decision === "allow") {
3783
+ if (decision2 === "allow") {
3768
3784
  const allow = p?.options?.find((o) => o.kind === "allow_once" || o.kind === "allow_always") ?? p?.options?.[0];
3769
3785
  if (allow?.optionId) {
3770
3786
  if (metadataKey)
@@ -3812,13 +3828,13 @@ var AcpClient = class extends EventEmitter {
3812
3828
  }
3813
3829
  const id = this.nextId++;
3814
3830
  const payload = { jsonrpc: "2.0", id, method, params };
3815
- return new Promise((resolve22, reject) => {
3831
+ return new Promise((resolve26, reject) => {
3816
3832
  const timer = setTimeout(() => {
3817
3833
  this.pending.delete(id);
3818
3834
  reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart)));
3819
3835
  }, timeoutMs);
3820
3836
  this.pending.set(id, {
3821
- resolve: resolve22,
3837
+ resolve: resolve26,
3822
3838
  reject,
3823
3839
  timer,
3824
3840
  method,
@@ -4236,31 +4252,433 @@ async function applyRuntimeModelPreflight(config, agent, selection, validate = v
4236
4252
  config.modelUnavailable = await revalidateRuntimeModelSelection(agent, config.agentEnv, selection, validate);
4237
4253
  }
4238
4254
 
4239
- // apps/body/dist/room-runtime.js
4240
- import { execFile as execFile3 } from "node:child_process";
4255
+ // apps/body/dist/model-catalog-sync.js
4241
4256
  import { createHash } from "node:crypto";
4257
+ import { readFile, writeFile } from "node:fs/promises";
4258
+ import { resolve as resolve5 } from "node:path";
4259
+ var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
4260
+ var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
4261
+ function modelCatalogHash(options, selection) {
4262
+ return createHash("sha256").update(JSON.stringify({ options, selection: selection ?? null })).digest("hex");
4263
+ }
4264
+ async function withTimeout(work, timeoutMs, what) {
4265
+ let timer;
4266
+ const expiry = new Promise((_, reject) => {
4267
+ timer = setTimeout(() => reject(new Error(`${what} timed out after ${timeoutMs}ms`)), timeoutMs);
4268
+ });
4269
+ try {
4270
+ return await Promise.race([work, expiry]);
4271
+ } finally {
4272
+ clearTimeout(timer);
4273
+ work.catch(() => void 0);
4274
+ }
4275
+ }
4276
+ async function syncAgentModelCatalog(input) {
4277
+ const log = input.log ?? ((line) => console.log(line));
4278
+ const fetchCatalog = input.fetchCatalog ?? fetchAgentModelCatalog;
4279
+ const hashPath = resolve5(input.runtimeDir, MODEL_CATALOG_HASH_FILE);
4280
+ try {
4281
+ const configuration = await input.api.execute("getAgentConfiguration", {
4282
+ agentId: input.agentId
4283
+ });
4284
+ const selection = configuration.model || configuration.effort ? {
4285
+ ...configuration.model ? { model: configuration.model } : {},
4286
+ ...configuration.effort ? { effort: configuration.effort } : {}
4287
+ } : input.runtimeSelection;
4288
+ const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
4289
+ const hash = modelCatalogHash(catalog, selection);
4290
+ const previous = await readFile(hashPath, "utf8").catch(() => "");
4291
+ if (previous.trim() === hash)
4292
+ return "unchanged";
4293
+ await input.api.execute("postAgentModelCatalog", {
4294
+ agentId: input.agentId,
4295
+ workspaceId: input.workspaceId,
4296
+ // `fetchAgentModelCatalog` already applied the category allow-list.
4297
+ options: catalog,
4298
+ ...selection ? { selection } : {}
4299
+ });
4300
+ await writeFile(hashPath, `${hash}
4301
+ `, { mode: 384 });
4302
+ log(`[body] model catalog posted: ${catalog.length} axis(es)` + (selection?.model ? `, model ${selection.model}` : "") + (selection?.effort ? `, effort ${selection.effort}` : ""));
4303
+ return "posted";
4304
+ } catch (error) {
4305
+ log(`[body] model catalog not posted this activation: ${error instanceof Error ? error.message : String(error)}`);
4306
+ return "failed";
4307
+ }
4308
+ }
4309
+
4310
+ // apps/body/dist/room-runtime.js
4311
+ import { execFile as execFile4 } from "node:child_process";
4312
+ import { createHash as createHash2 } from "node:crypto";
4242
4313
  import { existsSync as existsSync4, mkdirSync } from "node:fs";
4243
- import { mkdir as mkdir6, rm as rm3 } from "node:fs/promises";
4244
- import { dirname as dirname4, resolve as resolve10 } from "node:path";
4314
+ import { mkdir as mkdir8, rm as rm3 } from "node:fs/promises";
4315
+ import { dirname as dirname6, resolve as resolve15 } from "node:path";
4245
4316
  import { promisify as promisify3 } from "node:util";
4246
4317
 
4318
+ // apps/body/dist/grant-runner.js
4319
+ import { execFile } from "node:child_process";
4320
+ import { randomBytes } from "node:crypto";
4321
+ import { readFile as readFile3 } from "node:fs/promises";
4322
+ import { createServer } from "node:http";
4323
+ import { dirname as dirname3, resolve as resolve7 } from "node:path";
4324
+
4325
+ // packages/api-contract/dist/agent-grants.js
4326
+ var AGENT_GRANT_KINDS = ["path", "host", "secret", "device", "budget", "command"];
4327
+ var SHELL_METACHARACTERS = /[;&|<>$`\\'"(){}\n\r\t*?[\]~#!]/;
4328
+ var SECRET_NAME = /^[A-Z][A-Z0-9_]{0,63}$/;
4329
+ function parseCommandGrantTarget(target) {
4330
+ if (typeof target !== "string" || !target.trim()) {
4331
+ throw new Error("command target is required");
4332
+ }
4333
+ if (target !== target.trim() || /\s{2,}/.test(target)) {
4334
+ throw new Error("command target must be one line with single spaces");
4335
+ }
4336
+ if (SHELL_METACHARACTERS.test(target)) {
4337
+ throw new Error("command target must not contain shell metacharacters");
4338
+ }
4339
+ const words = target.split(" ");
4340
+ const argv = [];
4341
+ const secrets = [];
4342
+ for (let index = 0; index < words.length; index += 1) {
4343
+ const word = words[index];
4344
+ if (word === "--with") {
4345
+ const name = words[index + 1];
4346
+ if (!name || !SECRET_NAME.test(name)) {
4347
+ throw new Error("--with must name one UPPER_CASE secret");
4348
+ }
4349
+ if (!secrets.includes(name))
4350
+ secrets.push(name);
4351
+ index += 1;
4352
+ continue;
4353
+ }
4354
+ if (secrets.length)
4355
+ throw new Error("--with suffixes must come after the command words");
4356
+ argv.push(word);
4357
+ }
4358
+ if (!argv.length)
4359
+ throw new Error("command target must name a command");
4360
+ return { argv, secrets };
4361
+ }
4362
+ function commandGrantMatches(rule, requested) {
4363
+ if (requested.length < rule.argv.length)
4364
+ return false;
4365
+ return rule.argv.every((word, index) => requested[index] === word);
4366
+ }
4367
+ var DECISION_LINE = new RegExp(`^(.+?) (approved once|approved|declined) (${AGENT_GRANT_KINDS.join("|")}) (.+)$`, "s");
4368
+ function parseGrantDecisionLine(body) {
4369
+ const match = DECISION_LINE.exec(body);
4370
+ if (!match)
4371
+ return void 0;
4372
+ const decision2 = match[2] === "approved once" ? "once" : match[2] === "approved" ? "always" : "deny";
4373
+ return {
4374
+ deciderName: match[1],
4375
+ decision: decision2,
4376
+ kind: match[3],
4377
+ target: match[4]
4378
+ };
4379
+ }
4380
+
4381
+ // apps/body/dist/provider-key-store.js
4382
+ import { chmod, mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
4383
+ import { homedir as homedir2 } from "node:os";
4384
+ import { dirname as dirname2, resolve as resolve6 } from "node:path";
4385
+ var PROVIDER_KEY_ENV_VARS = {
4386
+ openrouter: "OPENROUTER_API_KEY",
4387
+ openai: "OPENAI_API_KEY",
4388
+ anthropic: "ANTHROPIC_API_KEY",
4389
+ google: "GOOGLE_API_KEY",
4390
+ xai: "XAI_API_KEY"
4391
+ };
4392
+ var GOOGLE_ENV_ALIAS = "GEMINI_API_KEY";
4393
+ function providerKeyStorePath(env = process.env) {
4394
+ const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve6(homedir2(), ".config");
4395
+ return resolve6(configRoot, "beeline", "providers.json");
4396
+ }
4397
+ async function readProviderKeyStore(env = process.env) {
4398
+ const path = providerKeyStorePath(env);
4399
+ const raw = await readFile2(path, "utf8").catch(() => void 0);
4400
+ if (!raw)
4401
+ return {};
4402
+ try {
4403
+ const parsed = JSON.parse(raw);
4404
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
4405
+ return {};
4406
+ const entries = Object.entries(parsed).filter((entry) => entry[0] in PROVIDER_KEY_ENV_VARS && typeof entry[1] === "string" && entry[1].length > 0);
4407
+ return Object.fromEntries(entries);
4408
+ } catch {
4409
+ return {};
4410
+ }
4411
+ }
4412
+ async function readSavedProviderKey(provider, env = process.env) {
4413
+ return (await readProviderKeyStore(env))[provider];
4414
+ }
4415
+ async function saveProviderKey(provider, key, env = process.env) {
4416
+ const path = providerKeyStorePath(env);
4417
+ const store = { ...await readProviderKeyStore(env), [provider]: key };
4418
+ await mkdir(dirname2(path), { recursive: true, mode: 448 });
4419
+ await writeFile2(path, `${JSON.stringify(store, null, 2)}
4420
+ `, { mode: 384 });
4421
+ await chmod(path, 384);
4422
+ }
4423
+ function providerKeyFromEnvironment(provider, env = process.env) {
4424
+ const primary = env[PROVIDER_KEY_ENV_VARS[provider]]?.trim();
4425
+ if (primary)
4426
+ return primary;
4427
+ if (provider === "google")
4428
+ return env[GOOGLE_ENV_ALIAS]?.trim() || void 0;
4429
+ return void 0;
4430
+ }
4431
+ function maskProviderKey(key) {
4432
+ const trimmed = key.trim();
4433
+ if (trimmed.length <= 9)
4434
+ return "\u2026";
4435
+ return `${trimmed.slice(0, 6)}\u2026${trimmed.slice(-3)}`;
4436
+ }
4437
+
4438
+ // apps/body/dist/grant-runner.js
4439
+ var GRANT_COMMAND_TIMEOUT_MS = 10 * 6e4;
4440
+ var GRANT_COMMAND_OUTPUT_CAP_BYTES = 64 * 1024;
4441
+ var ARGV_MAX_WORDS = 256;
4442
+ var ARGV_WORD_MAX_LENGTH = 4096;
4443
+ function operatorSecretResolver(env = process.env) {
4444
+ const providerByEnvVar = new Map(Object.entries(PROVIDER_KEY_ENV_VARS).map(([provider, envVar]) => [envVar, provider]));
4445
+ return async (name) => {
4446
+ const provider = providerByEnvVar.get(name);
4447
+ if (provider) {
4448
+ const saved = (await readProviderKeyStore(env))[provider];
4449
+ if (saved)
4450
+ return saved;
4451
+ }
4452
+ const raw = await readFile3(resolve7(dirname3(providerKeyStorePath(env)), "secrets.json"), "utf8").catch(() => void 0);
4453
+ if (!raw)
4454
+ return void 0;
4455
+ try {
4456
+ const parsed = JSON.parse(raw);
4457
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
4458
+ return void 0;
4459
+ const value = parsed[name];
4460
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4461
+ } catch {
4462
+ return void 0;
4463
+ }
4464
+ };
4465
+ }
4466
+ function validateGrantArgv(value) {
4467
+ if (!Array.isArray(value) || value.length === 0)
4468
+ throw new Error("argv must be a non-empty array");
4469
+ if (value.length > ARGV_MAX_WORDS)
4470
+ throw new Error(`argv exceeds ${ARGV_MAX_WORDS} words`);
4471
+ return value.map((word) => {
4472
+ if (typeof word !== "string" || !word)
4473
+ throw new Error("argv words must be non-empty strings");
4474
+ if (word.length > ARGV_WORD_MAX_LENGTH || /[\0\r\n]/.test(word)) {
4475
+ throw new Error("argv words must be single-line and bounded");
4476
+ }
4477
+ return word;
4478
+ });
4479
+ }
4480
+ function matchCommandGrant(grants, workspaceId, argv) {
4481
+ for (const grant of grants) {
4482
+ if (grant.kind !== "command" || grant.workspaceId !== workspaceId)
4483
+ continue;
4484
+ let rule;
4485
+ try {
4486
+ rule = parseCommandGrantTarget(grant.target);
4487
+ } catch {
4488
+ continue;
4489
+ }
4490
+ if (commandGrantMatches(rule, argv))
4491
+ return { grant, rule };
4492
+ }
4493
+ return void 0;
4494
+ }
4495
+ function capOutput(value, cap) {
4496
+ if (Buffer.byteLength(value) <= cap)
4497
+ return value;
4498
+ const half = Math.floor(cap / 2);
4499
+ const bytes = Buffer.from(value);
4500
+ return `${bytes.subarray(0, half).toString("utf8")}
4501
+ \u2026[${bytes.length - cap} bytes omitted]\u2026
4502
+ ${bytes.subarray(bytes.length - half).toString("utf8")}`;
4503
+ }
4504
+ function scrubSecrets(value, secrets) {
4505
+ let scrubbed = value;
4506
+ for (const [name, secret] of secrets) {
4507
+ if (secret.length >= 4)
4508
+ scrubbed = scrubbed.split(secret).join(`[${name}]`);
4509
+ }
4510
+ return scrubbed;
4511
+ }
4512
+ var GrantCommandRunner = class {
4513
+ options;
4514
+ rooms = /* @__PURE__ */ new Map();
4515
+ resolveSecret;
4516
+ constructor(options) {
4517
+ this.options = options;
4518
+ this.resolveSecret = options.resolveSecret ?? operatorSecretResolver(options.env ?? process.env);
4519
+ }
4520
+ register(roomId, room) {
4521
+ this.rooms.set(roomId, room);
4522
+ }
4523
+ unregister(roomId) {
4524
+ this.rooms.delete(roomId);
4525
+ }
4526
+ async run(input) {
4527
+ if (typeof input.roomId !== "string" || !input.roomId)
4528
+ throw new Error("roomId is required");
4529
+ const room = this.rooms.get(input.roomId);
4530
+ if (!room)
4531
+ throw new Error("this daemon is not serving that Room");
4532
+ const argv = validateGrantArgv(input.argv);
4533
+ const live = await this.options.api.execute("listAgentGrants", { agentId: this.options.agentId });
4534
+ const match = matchCommandGrant(live.grants, room.workspaceId, argv);
4535
+ if (!match) {
4536
+ throw new Error(`no approved command grant matches: ${argv.join(" ")}. Ask with request_grant kind=command first.`);
4537
+ }
4538
+ const { grant, rule } = match;
4539
+ const secrets = /* @__PURE__ */ new Map();
4540
+ for (const name of rule.secrets) {
4541
+ const value = await this.resolveSecret(name);
4542
+ if (!value)
4543
+ throw new Error(`secret ${name} is not in the operator key store`);
4544
+ secrets.set(name, value);
4545
+ }
4546
+ if (grant.status === "once") {
4547
+ await this.options.api.execute("consumeAgentGrant", { grantId: grant.grantId });
4548
+ }
4549
+ const source = this.options.env ?? process.env;
4550
+ const env = {
4551
+ ...source.PATH ? { PATH: source.PATH } : {},
4552
+ ...source.HOME ? { HOME: source.HOME } : {},
4553
+ ...Object.fromEntries(secrets)
4554
+ };
4555
+ const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
4556
+ const outcome = await new Promise((resolveRun) => {
4557
+ const child = execFile(argv[0], argv.slice(1), {
4558
+ cwd: room.cwd,
4559
+ env,
4560
+ timeout: this.options.timeoutMs ?? GRANT_COMMAND_TIMEOUT_MS,
4561
+ killSignal: "SIGKILL",
4562
+ maxBuffer: cap * 4,
4563
+ encoding: "utf8"
4564
+ }, (error, stdout6, stderr) => {
4565
+ const combined = [stdout6, stderr].filter(Boolean).join(stderr && stdout6 ? "\n" : "");
4566
+ const failure = error;
4567
+ const spawnFailure = Boolean(failure && typeof failure.code === "string");
4568
+ resolveRun({
4569
+ exitCode: spawnFailure ? null : child.exitCode ?? (typeof failure?.code === "number" ? failure.code : 0),
4570
+ ...failure?.signal ? { signal: failure.signal } : {},
4571
+ timedOut: Boolean(failure?.killed && failure?.signal === "SIGKILL"),
4572
+ output: spawnFailure ? `${combined}${combined ? "\n" : ""}${failure.message}` : combined
4573
+ });
4574
+ });
4575
+ });
4576
+ const output = capOutput(scrubSecrets(outcome.output, secrets), cap);
4577
+ const turn = room.turn();
4578
+ const requester = turn?.requester ?? {
4579
+ pubkey: grant.requestedBy,
4580
+ ...grant.requestedByName ? { name: grant.requestedByName } : {}
4581
+ };
4582
+ const status = outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
4583
+ await this.options.api.execute("postAgentActivity", {
4584
+ agentId: this.options.agentId,
4585
+ roomId: input.roomId,
4586
+ requestId: turn?.requestId ?? `grant:${grant.grantId}`,
4587
+ activity: [
4588
+ {
4589
+ kind: "tool",
4590
+ title: `ran ${argv.join(" ")} under grant ${grant.grantId} \xB7 asked by ${requester.name ?? requester.pubkey.slice(0, 12)}`,
4591
+ operation: "execute",
4592
+ status,
4593
+ command: argv.join(" "),
4594
+ ...output ? { output } : {},
4595
+ requestedBy: requester
4596
+ }
4597
+ ]
4598
+ });
4599
+ return {
4600
+ grantId: grant.grantId,
4601
+ exitCode: outcome.exitCode,
4602
+ ...outcome.signal ? { signal: outcome.signal } : {},
4603
+ timedOut: outcome.timedOut,
4604
+ output
4605
+ };
4606
+ }
4607
+ };
4608
+ var GrantRunnerServer = class {
4609
+ runner;
4610
+ server;
4611
+ endpoint;
4612
+ constructor(runner) {
4613
+ this.runner = runner;
4614
+ }
4615
+ async start() {
4616
+ if (this.endpoint)
4617
+ return this.endpoint;
4618
+ const token = randomBytes(32).toString("base64url");
4619
+ const server = createServer((request, response) => {
4620
+ void this.handle(request, response, token);
4621
+ });
4622
+ await new Promise((resolveListen, reject) => {
4623
+ server.once("error", reject);
4624
+ server.listen(0, "127.0.0.1", () => resolveListen());
4625
+ });
4626
+ server.unref?.();
4627
+ const address = server.address();
4628
+ this.server = server;
4629
+ this.endpoint = { url: `http://127.0.0.1:${address.port}`, token };
4630
+ return this.endpoint;
4631
+ }
4632
+ async close() {
4633
+ const server = this.server;
4634
+ this.server = void 0;
4635
+ this.endpoint = void 0;
4636
+ if (!server)
4637
+ return;
4638
+ await new Promise((resolveClose) => server.close(() => resolveClose()));
4639
+ }
4640
+ async handle(request, response, token) {
4641
+ const send = (status, body) => {
4642
+ response.writeHead(status, { "content-type": "application/json" });
4643
+ response.end(JSON.stringify(body));
4644
+ };
4645
+ if (request.headers.authorization !== `Bearer ${token}`) {
4646
+ send(401, { error: "grant runner token required" });
4647
+ return;
4648
+ }
4649
+ if (request.method !== "POST" || request.url !== "/run") {
4650
+ send(404, { error: "not found" });
4651
+ return;
4652
+ }
4653
+ const chunks = [];
4654
+ for await (const chunk of request)
4655
+ chunks.push(chunk);
4656
+ try {
4657
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
4658
+ send(200, await this.runner.run({ roomId: body.roomId, argv: body.argv }));
4659
+ } catch (error) {
4660
+ send(400, { error: error instanceof Error ? error.message : String(error) });
4661
+ }
4662
+ }
4663
+ };
4664
+
4247
4665
  // apps/body/dist/monolith-corner-turn.js
4248
- import { execFile as execFile2 } from "node:child_process";
4249
- import { mkdir as mkdir4 } from "node:fs/promises";
4250
- import { homedir as homedir5 } from "node:os";
4666
+ import { execFile as execFile3 } from "node:child_process";
4667
+ import { mkdir as mkdir6 } from "node:fs/promises";
4668
+ import { homedir as homedir6 } from "node:os";
4251
4669
  import { join as join4 } from "node:path";
4252
4670
  import { promisify as promisify2 } from "node:util";
4253
4671
 
4254
4672
  // apps/body/dist/agent-home.js
4255
4673
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
4256
4674
  import { randomUUID } from "node:crypto";
4257
- import { chmod, copyFile, lstat, mkdir, readdir, realpath, rename, rm as rm2, symlink, unlink, writeFile } from "node:fs/promises";
4258
- import { homedir as homedir2 } from "node:os";
4259
- import { basename as basename3, dirname as dirname2, relative, resolve as resolve6, sep } from "node:path";
4675
+ import { chmod as chmod2, copyFile, lstat, mkdir as mkdir3, readdir, realpath, rename, rm as rm2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
4676
+ import { homedir as homedir3 } from "node:os";
4677
+ import { basename as basename3, dirname as dirname4, relative, resolve as resolve10, sep } from "node:path";
4260
4678
 
4261
4679
  // apps/body/dist/beeline-skill.js
4262
4680
  import { readFileSync as readFileSync3 } from "node:fs";
4263
- import { resolve as resolve5 } from "node:path";
4681
+ import { resolve as resolve8 } from "node:path";
4264
4682
  var USING_BEELINE_SKILL_NAME = "using-beeline";
4265
4683
  var BEELINE_ROOM_CAPABILITIES = [
4266
4684
  "The repository filesystem is read-only in this Room session.",
@@ -4304,7 +4722,7 @@ function runningBeelineReleaseId(env = process.env, read = (path) => readFileSyn
4304
4722
  const lib = env.BEELINE_LIB_DIR;
4305
4723
  if (!lib)
4306
4724
  return "source";
4307
- const manifest = JSON.parse(read(resolve5(lib, "bundle.json")));
4725
+ const manifest = JSON.parse(read(resolve8(lib, "bundle.json")));
4308
4726
  return [manifest.version, manifest.commit].filter(Boolean).join("-") || "source";
4309
4727
  } catch {
4310
4728
  return "source";
@@ -4340,6 +4758,226 @@ var SQUIRE_GOVERNED_TOOLS = [
4340
4758
  ];
4341
4759
  var SQUIRE_GOVERNED_TOOL_SET = new Set(SQUIRE_GOVERNED_TOOLS);
4342
4760
 
4761
+ // apps/body/dist/openrouter-routing.js
4762
+ import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
4763
+ import { resolve as resolve9 } from "node:path";
4764
+ var OPENROUTER_ENDPOINTS_BASE_URL = "https://openrouter.ai/api/v1/models";
4765
+ var OPENROUTER_ROUTING_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
4766
+ var OPENROUTER_ROUTING_FETCH_TIMEOUT_MS = 1e4;
4767
+ var OPENROUTER_UPTIME_BAR = 98;
4768
+ var OPENROUTER_UPTIME_BAR_RELAXED = 95;
4769
+ var OPENROUTER_MIN_PROVIDERS = 2;
4770
+ var OPENROUTER_FALLBACK_PROVIDERS = ["deepinfra", "novita"];
4771
+ function openRouterModelId(model, env = {}) {
4772
+ const trimmed = model?.trim();
4773
+ if (!trimmed)
4774
+ return void 0;
4775
+ if (trimmed.startsWith("openrouter/")) {
4776
+ const id = trimmed.slice("openrouter/".length);
4777
+ return id.includes("/") ? id : void 0;
4778
+ }
4779
+ if (env.OPENROUTER_API_KEY?.trim() && /^[^/\s]+\/[^/\s][^\s]*$/.test(trimmed))
4780
+ return trimmed;
4781
+ return void 0;
4782
+ }
4783
+ function parseOpenRouterEndpoints(payload) {
4784
+ const data = payload?.data;
4785
+ const record2 = data && typeof data === "object" ? data : {};
4786
+ const raw = Array.isArray(record2.endpoints) ? record2.endpoints : [];
4787
+ const endpoints = [];
4788
+ for (const entry of raw) {
4789
+ if (!entry || typeof entry !== "object")
4790
+ continue;
4791
+ const endpoint2 = entry;
4792
+ const tag = typeof endpoint2.tag === "string" ? endpoint2.tag : "";
4793
+ const provider = tag.split("/")[0]?.trim() ?? "";
4794
+ if (!provider)
4795
+ continue;
4796
+ const uptime = typeof endpoint2.uptime_last_30m === "number" ? endpoint2.uptime_last_30m : 0;
4797
+ const contextLength = typeof endpoint2.context_length === "number" ? endpoint2.context_length : 0;
4798
+ const supported = Array.isArray(endpoint2.supported_parameters) ? endpoint2.supported_parameters : [];
4799
+ endpoints.push({ provider, uptime, contextLength, tools: supported.includes("tools") });
4800
+ }
4801
+ return {
4802
+ endpoints,
4803
+ contextLength: typeof record2.context_length === "number" ? record2.context_length : advertisedContextLength(endpoints)
4804
+ };
4805
+ }
4806
+ function advertisedContextLength(endpoints) {
4807
+ const counts = /* @__PURE__ */ new Map();
4808
+ for (const endpoint2 of endpoints) {
4809
+ if (!endpoint2.tools || endpoint2.contextLength <= 0)
4810
+ continue;
4811
+ counts.set(endpoint2.contextLength, (counts.get(endpoint2.contextLength) ?? 0) + 1);
4812
+ }
4813
+ let best;
4814
+ for (const [length, count] of counts) {
4815
+ if (best === void 0) {
4816
+ best = length;
4817
+ continue;
4818
+ }
4819
+ const bestCount = counts.get(best) ?? 0;
4820
+ if (count > bestCount || count === bestCount && length > best)
4821
+ best = length;
4822
+ }
4823
+ return best;
4824
+ }
4825
+ function selectReliableOpenRouterProviders(endpoints, contextLength) {
4826
+ const eligible = endpoints.filter((endpoint2) => endpoint2.tools && (contextLength === void 0 || endpoint2.contextLength >= contextLength));
4827
+ const atBar = (bar) => {
4828
+ const seen = /* @__PURE__ */ new Set();
4829
+ return eligible.filter((endpoint2) => endpoint2.uptime >= bar).sort((left, right) => right.uptime - left.uptime).map((endpoint2) => endpoint2.provider).filter((provider) => seen.has(provider) ? false : (seen.add(provider), true));
4830
+ };
4831
+ const strict = atBar(OPENROUTER_UPTIME_BAR);
4832
+ if (strict.length >= OPENROUTER_MIN_PROVIDERS) {
4833
+ return { providers: strict, bar: OPENROUTER_UPTIME_BAR, contextLength };
4834
+ }
4835
+ const relaxed = atBar(OPENROUTER_UPTIME_BAR_RELAXED);
4836
+ if (relaxed.length > 0) {
4837
+ return { providers: relaxed, bar: OPENROUTER_UPTIME_BAR_RELAXED, contextLength };
4838
+ }
4839
+ return { providers: [], bar: null, contextLength };
4840
+ }
4841
+ function openRouterRoutingFor(providers) {
4842
+ return {
4843
+ only: [...providers],
4844
+ order: [...providers],
4845
+ allow_fallbacks: true,
4846
+ require_parameters: true
4847
+ };
4848
+ }
4849
+ function openRouterRoutingCacheDir(runtimeDir) {
4850
+ return resolve9(runtimeDir, "openrouter-routing");
4851
+ }
4852
+ function cachePath(cacheDir, model) {
4853
+ return resolve9(cacheDir, `${model.replace(/[^A-Za-z0-9._-]+/g, "_")}.json`);
4854
+ }
4855
+ async function readCache(cacheDir, model) {
4856
+ try {
4857
+ const parsed = JSON.parse(await readFile4(cachePath(cacheDir, model), "utf8"));
4858
+ if (!parsed || typeof parsed !== "object")
4859
+ return void 0;
4860
+ const cached = parsed;
4861
+ if (cached.model !== model || typeof cached.fetchedAt !== "number" || !Array.isArray(cached.providers) || !cached.providers.every((provider) => typeof provider === "string" && provider.length > 0)) {
4862
+ return void 0;
4863
+ }
4864
+ return {
4865
+ model,
4866
+ fetchedAt: cached.fetchedAt,
4867
+ providers: cached.providers,
4868
+ bar: typeof cached.bar === "number" ? cached.bar : null
4869
+ };
4870
+ } catch {
4871
+ return void 0;
4872
+ }
4873
+ }
4874
+ async function writeCache(cacheDir, value) {
4875
+ await mkdir2(cacheDir, { recursive: true, mode: 448 });
4876
+ await writeFile3(cachePath(cacheDir, value.model), `${JSON.stringify(value, null, 2)}
4877
+ `, {
4878
+ mode: 384
4879
+ });
4880
+ }
4881
+ function decision(model, providers, bar, source, note) {
4882
+ const criteria = bar === null ? "fallback pair" : `uptime \u2265${bar}%, tools`;
4883
+ const suffix = [
4884
+ bar === OPENROUTER_UPTIME_BAR_RELAXED ? "bar lowered: fewer than 2 providers at 98%" : "",
4885
+ source === "cache" ? "cached" : "",
4886
+ source === "stale-cache" ? "stale cache" : "",
4887
+ note ?? ""
4888
+ ].filter(Boolean).join("; ");
4889
+ return {
4890
+ model,
4891
+ routing: openRouterRoutingFor(providers),
4892
+ providers: [...providers],
4893
+ bar,
4894
+ source,
4895
+ line: `[body] openrouter routing for ${model}: ${providers.join(", ")} (${criteria}` + (suffix ? `; ${suffix})` : ")")
4896
+ };
4897
+ }
4898
+ async function resolveOpenRouterRouting(input) {
4899
+ const now2 = input.now ?? Date.now;
4900
+ const cached = await readCache(input.cacheDir, input.model);
4901
+ if (cached && now2() - cached.fetchedAt < OPENROUTER_ROUTING_CACHE_TTL_MS) {
4902
+ return decision(input.model, cached.providers, cached.bar, "cache");
4903
+ }
4904
+ let failure;
4905
+ try {
4906
+ const doFetch = input.fetchImpl ?? fetch;
4907
+ const response = await doFetch(`${OPENROUTER_ENDPOINTS_BASE_URL}/${input.model}/endpoints`, {
4908
+ signal: AbortSignal.timeout(input.timeoutMs ?? OPENROUTER_ROUTING_FETCH_TIMEOUT_MS)
4909
+ });
4910
+ if (!response.ok)
4911
+ throw new Error(`HTTP ${response.status}`);
4912
+ const { endpoints, contextLength } = parseOpenRouterEndpoints(await response.json());
4913
+ if (endpoints.length === 0)
4914
+ throw new Error("no endpoints listed");
4915
+ const selected = selectReliableOpenRouterProviders(endpoints, contextLength);
4916
+ if (selected.providers.length === 0) {
4917
+ return decision(input.model, OPENROUTER_FALLBACK_PROVIDERS, null, "fallback", "no provider met the bar");
4918
+ }
4919
+ await writeCache(input.cacheDir, {
4920
+ model: input.model,
4921
+ fetchedAt: now2(),
4922
+ providers: selected.providers,
4923
+ bar: selected.bar
4924
+ }).catch(() => void 0);
4925
+ return decision(input.model, selected.providers, selected.bar, "live");
4926
+ } catch (error) {
4927
+ failure = error instanceof Error ? error.message : String(error);
4928
+ }
4929
+ if (cached) {
4930
+ return decision(input.model, cached.providers, cached.bar, "stale-cache", `api unreachable: ${failure}`);
4931
+ }
4932
+ return decision(input.model, OPENROUTER_FALLBACK_PROVIDERS, null, "fallback", `api unreachable: ${failure}`);
4933
+ }
4934
+ function withOpenRouterModelRouting(value, pin) {
4935
+ const root = value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
4936
+ const applyOverride = (provider) => {
4937
+ if (!pin)
4938
+ return provider;
4939
+ const overrides = provider.modelOverrides && typeof provider.modelOverrides === "object" && !Array.isArray(provider.modelOverrides) ? { ...provider.modelOverrides } : {};
4940
+ const existing = overrides[pin.model];
4941
+ const override = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {};
4942
+ const compat = override.compat && typeof override.compat === "object" && !Array.isArray(override.compat) ? { ...override.compat } : {};
4943
+ override.compat = { ...compat, openRouterRouting: pin.routing };
4944
+ overrides[pin.model] = override;
4945
+ return { ...provider, modelOverrides: overrides };
4946
+ };
4947
+ if (Array.isArray(root.providers)) {
4948
+ if (!pin)
4949
+ return root;
4950
+ const providers2 = root.providers.map((provider) => provider && typeof provider === "object" ? { ...provider } : provider);
4951
+ const index = providers2.findIndex((provider) => provider && typeof provider === "object" && provider.name === "openrouter");
4952
+ const current = applyOverride(index >= 0 ? providers2[index] : { name: "openrouter" });
4953
+ if (index >= 0)
4954
+ providers2[index] = current;
4955
+ else
4956
+ providers2.push(current);
4957
+ root.providers = providers2;
4958
+ return root;
4959
+ }
4960
+ const providers = root.providers && typeof root.providers === "object" ? { ...root.providers } : {};
4961
+ if (pin) {
4962
+ const current = providers.openrouter && typeof providers.openrouter === "object" && !Array.isArray(providers.openrouter) ? { ...providers.openrouter } : {};
4963
+ providers.openrouter = applyOverride(current);
4964
+ }
4965
+ root.providers = providers;
4966
+ return root;
4967
+ }
4968
+ function openRouterRoutingInput(config, selection, fetchImpl) {
4969
+ const model = openRouterModelId(selection?.model, config.agentEnv);
4970
+ if (!model || !config.openRouterRoutingCacheDir)
4971
+ return {};
4972
+ return {
4973
+ openRouterRouting: {
4974
+ model,
4975
+ cacheDir: config.openRouterRoutingCacheDir,
4976
+ ...fetchImpl ? { fetchImpl } : {}
4977
+ }
4978
+ };
4979
+ }
4980
+
4343
4981
  // apps/body/dist/toml-section.js
4344
4982
  function scanLine(line, openString) {
4345
4983
  if (openString.kind) {
@@ -4523,54 +5161,21 @@ var PI_CUSTOM_MODEL_CONFIG = {
4523
5161
  source: ".pi/agent/models.json",
4524
5162
  target: "models.json"
4525
5163
  };
4526
- var OPENROUTER_RELIABLE_PROVIDERS = ["deepinfra", "novita"];
4527
- function withReliableOpenRouterRouting(value) {
4528
- const root = value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
4529
- const routing = {
4530
- only: [...OPENROUTER_RELIABLE_PROVIDERS],
4531
- order: [...OPENROUTER_RELIABLE_PROVIDERS],
4532
- allow_fallbacks: false,
4533
- require_parameters: true
4534
- };
4535
- if (Array.isArray(root.providers)) {
4536
- const providers2 = root.providers.map((provider) => provider && typeof provider === "object" ? { ...provider } : provider);
4537
- const index = providers2.findIndex((provider) => provider && typeof provider === "object" && provider.name === "openrouter");
4538
- const current2 = index >= 0 ? providers2[index] : { name: "openrouter" };
4539
- const compat2 = current2.compat && typeof current2.compat === "object" && !Array.isArray(current2.compat) ? { ...current2.compat } : {};
4540
- current2.compat = { ...compat2, openRouterRouting: routing };
4541
- if (index >= 0)
4542
- providers2[index] = current2;
4543
- else
4544
- providers2.push(current2);
4545
- root.providers = providers2;
4546
- return root;
4547
- }
4548
- const providers = root.providers && typeof root.providers === "object" ? { ...root.providers } : {};
4549
- const current = providers.openrouter && typeof providers.openrouter === "object" && !Array.isArray(providers.openrouter) ? { ...providers.openrouter } : {};
4550
- const compat = current.compat && typeof current.compat === "object" && !Array.isArray(current.compat) ? { ...current.compat } : {};
4551
- current.compat = {
4552
- ...compat,
4553
- openRouterRouting: routing
4554
- };
4555
- providers.openrouter = current;
4556
- root.providers = providers;
4557
- return root;
4558
- }
4559
5164
  var CODEX_ROOM_AGENT_LOCKDOWN_TOML = "[agents]\nenabled = false\n";
4560
5165
  var CODEX_ROOM_WEB_SEARCH_TOML = "[features]\nstandalone_web_search = true\n";
4561
5166
  var HOME_SUBDIRS = ["user", "claude", "codex", "grok", "pi", "state", "cache", "tmp"];
4562
5167
  async function prepareRoomAgentHome(input) {
4563
- const root = resolve6(input.root);
4564
- const operatorHome = input.operatorHome ?? homedir2();
5168
+ const root = resolve10(input.root);
5169
+ const operatorHome = input.operatorHome ?? homedir3();
4565
5170
  try {
4566
- await mkdir(root, { recursive: true, mode: 448 });
5171
+ await mkdir3(root, { recursive: true, mode: 448 });
4567
5172
  const rootStats = await lstat(root);
4568
5173
  if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
4569
5174
  throw new AgentHomeSecurityError(`agent home root is not an ordinary directory: ${root}`);
4570
5175
  }
4571
5176
  for (const subdir of HOME_SUBDIRS) {
4572
- const path = resolve6(root, subdir);
4573
- await mkdir(path, { recursive: true, mode: 448 });
5177
+ const path = resolve10(root, subdir);
5178
+ await mkdir3(path, { recursive: true, mode: 448 });
4574
5179
  await assertRealContainedDirectory(path, root);
4575
5180
  }
4576
5181
  } catch (error) {
@@ -4580,14 +5185,14 @@ async function prepareRoomAgentHome(input) {
4580
5185
  return {};
4581
5186
  }
4582
5187
  for (const credential of SHARED_CREDENTIALS) {
4583
- const source = resolve6(operatorHome, credential.source);
4584
- const target = resolve6(root, credential.dir, credential.target);
5188
+ const source = resolve10(operatorHome, credential.source);
5189
+ const target = resolve10(root, credential.dir, credential.target);
4585
5190
  if (!existsSync3(source) || existsSync3(target))
4586
5191
  continue;
4587
5192
  await symlink(source, target).catch(() => void 0);
4588
5193
  }
4589
5194
  const prior = agentHomeProvisionQueues.get(root) ?? Promise.resolve();
4590
- const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? []));
5195
+ const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? [], input.openRouterRouting));
4591
5196
  agentHomeProvisionQueues.set(root, provision);
4592
5197
  try {
4593
5198
  await provision;
@@ -4597,19 +5202,19 @@ async function prepareRoomAgentHome(input) {
4597
5202
  }
4598
5203
  return roomAgentHomeEnv(root);
4599
5204
  }
4600
- async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills) {
5205
+ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, openRouterRouting) {
4601
5206
  const managedSkills = [
4602
5207
  { name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) }
4603
5208
  ];
4604
5209
  const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
4605
5210
  for (const dir of AGENT_SKILL_DIRS) {
4606
- const target = resolve6(root, dir, "skills");
5211
+ const target = resolve10(root, dir, "skills");
4607
5212
  await provisionManagedSkillsDir(target, managedSkills, shared, sharedSkills.length === 0);
4608
5213
  }
4609
5214
  for (const config of HARNESS_MCP_CONFIGS) {
4610
5215
  try {
4611
- const source = resolve6(operatorHome, config.toml);
4612
- const target = resolve6(root, config.dir, "config.toml");
5216
+ const source = resolve10(operatorHome, config.toml);
5217
+ const target = resolve10(root, config.dir, "config.toml");
4613
5218
  const mcpSection = existsSync3(source) ? filteredHarnessMcpToml(readFileSync4(source, "utf8")) : void 0;
4614
5219
  const section = config.dir === "codex" ? [CODEX_ROOM_AGENT_LOCKDOWN_TOML, CODEX_ROOM_WEB_SEARCH_TOML, mcpSection].filter(Boolean).join("\n") : mcpSection;
4615
5220
  if (!section) {
@@ -4624,8 +5229,8 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
4624
5229
  }
4625
5230
  }
4626
5231
  try {
4627
- const claudeJson = resolve6(operatorHome, ".claude.json");
4628
- const claudeTarget = resolve6(root, "claude", ".claude.json");
5232
+ const claudeJson = resolve10(operatorHome, ".claude.json");
5233
+ const claudeTarget = resolve10(root, "claude", ".claude.json");
4629
5234
  const mcpServers = existsSync3(claudeJson) ? readClaudeUserScopeMcpServers(claudeJson) : void 0;
4630
5235
  if (mcpServers && Object.keys(mcpServers).length > 0) {
4631
5236
  await writeIsolatedHarnessFile(claudeTarget, `${JSON.stringify({ mcpServers }, null, 2)}
@@ -4640,22 +5245,28 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
4640
5245
  }
4641
5246
  try {
4642
5247
  const settings2 = { permissions: { allow: ["WebSearch"] } };
4643
- await writeIsolatedHarnessFile(resolve6(root, "claude", "settings.json"), `${JSON.stringify(settings2, null, 2)}
5248
+ await writeIsolatedHarnessFile(resolve10(root, "claude", "settings.json"), `${JSON.stringify(settings2, null, 2)}
4644
5249
  `);
4645
5250
  } catch (error) {
4646
5251
  if (failClosed)
4647
5252
  throw error;
4648
5253
  console.warn("[body] claude web-search settings provisioning failed:", error);
4649
5254
  }
4650
- await provisionPiCustomModelConfig(root, operatorHome, failClosed);
5255
+ await provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting);
4651
5256
  }
4652
- async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
4653
- const source = resolve6(operatorHome, PI_CUSTOM_MODEL_CONFIG.source);
4654
- const target = resolve6(root, "pi", PI_CUSTOM_MODEL_CONFIG.target);
5257
+ async function provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting) {
5258
+ const source = resolve10(operatorHome, PI_CUSTOM_MODEL_CONFIG.source);
5259
+ const target = resolve10(root, "pi", PI_CUSTOM_MODEL_CONFIG.target);
5260
+ let decision2;
5261
+ if (openRouterRouting) {
5262
+ decision2 = await resolveOpenRouterRouting(openRouterRouting);
5263
+ console.log(decision2.line);
5264
+ }
5265
+ const pin = decision2 ? { model: decision2.model, routing: decision2.routing } : void 0;
4655
5266
  try {
4656
5267
  const sourceStats = await lstat(source).catch(() => void 0);
4657
5268
  if (!sourceStats) {
4658
- await writeIsolatedHarnessFile(target, `${JSON.stringify(withReliableOpenRouterRouting({}), null, 2)}
5269
+ await writeIsolatedHarnessFile(target, `${JSON.stringify(withOpenRouterModelRouting({}, pin), null, 2)}
4659
5270
  `);
4660
5271
  return;
4661
5272
  }
@@ -4667,7 +5278,7 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
4667
5278
  throw new AgentHomeSecurityError(`Pi custom model config resolves through a link: ${source}`);
4668
5279
  }
4669
5280
  const sourceValue = JSON.parse(readFileSync4(resolvedSource, "utf8"));
4670
- await writeIsolatedHarnessFile(target, `${JSON.stringify(withReliableOpenRouterRouting(sourceValue), null, 2)}
5281
+ await writeIsolatedHarnessFile(target, `${JSON.stringify(withOpenRouterModelRouting(sourceValue, pin), null, 2)}
4671
5282
  `);
4672
5283
  } catch (error) {
4673
5284
  await unlink(target).catch(() => void 0);
@@ -4702,16 +5313,16 @@ function filteredHarnessMcpToml(source) {
4702
5313
  return extractTomlSections(source, ["mcp_servers"], excluded);
4703
5314
  }
4704
5315
  async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, optionalShares) {
4705
- const parent = dirname2(target);
4706
- await assertRealContainedDirectory(parent, dirname2(parent));
4707
- const staged = resolve6(parent, `.skills.${process.pid}.${randomUUID()}.tmp`);
4708
- await mkdir(staged, { mode: 448 });
5316
+ const parent = dirname4(target);
5317
+ await assertRealContainedDirectory(parent, dirname4(parent));
5318
+ const staged = resolve10(parent, `.skills.${process.pid}.${randomUUID()}.tmp`);
5319
+ await mkdir3(staged, { mode: 448 });
4709
5320
  const names = new Set(managedSkills.map((skill) => skill.name));
4710
5321
  try {
4711
5322
  for (const skill of managedSkills) {
4712
- const skillDir = resolve6(staged, skill.name);
4713
- await mkdir(skillDir, { recursive: true });
4714
- await writeIsolatedHarnessFile(resolve6(skillDir, "SKILL.md"), skill.content);
5323
+ const skillDir = resolve10(staged, skill.name);
5324
+ await mkdir3(skillDir, { recursive: true });
5325
+ await writeIsolatedHarnessFile(resolve10(skillDir, "SKILL.md"), skill.content);
4715
5326
  }
4716
5327
  for (const shared of sharedSkills) {
4717
5328
  if (names.has(shared.name)) {
@@ -4719,7 +5330,7 @@ async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, op
4719
5330
  }
4720
5331
  names.add(shared.name);
4721
5332
  try {
4722
- await copySafeSkillTree(shared.source, resolve6(staged, shared.name), shared.source);
5333
+ await copySafeSkillTree(shared.source, resolve10(staged, shared.name), shared.source);
4723
5334
  } catch (error) {
4724
5335
  if (!optionalShares)
4725
5336
  throw error;
@@ -4740,20 +5351,20 @@ async function resolveSharedSkillSources(operatorHome, names) {
4740
5351
  const seen = /* @__PURE__ */ new Set();
4741
5352
  const resolved = [];
4742
5353
  for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
4743
- const sourceRoot = resolve6(operatorHome, relativeRoot);
5354
+ const sourceRoot = resolve10(operatorHome, relativeRoot);
4744
5355
  const rootStats = await lstat(sourceRoot).catch(() => void 0);
4745
5356
  if (!rootStats?.isDirectory() || rootStats.isSymbolicLink())
4746
5357
  continue;
4747
5358
  for (const entry of await readdir(sourceRoot)) {
4748
5359
  if (!isSharedSkillName(entry) || seen.has(entry))
4749
5360
  continue;
4750
- const candidate = resolve6(sourceRoot, entry);
5361
+ const candidate = resolve10(sourceRoot, entry);
4751
5362
  try {
4752
5363
  const candidateStats = await lstat(candidate);
4753
5364
  if (!candidateStats.isDirectory() || candidateStats.isSymbolicLink())
4754
5365
  continue;
4755
5366
  assertContained(sourceRoot, candidate);
4756
- const skillMd = resolve6(candidate, "SKILL.md");
5367
+ const skillMd = resolve10(candidate, "SKILL.md");
4757
5368
  const skillStats = await lstat(skillMd);
4758
5369
  if (!skillStats.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
4759
5370
  throw new Error(`shared skill requires an ordinary SKILL.md: ${entry}`);
@@ -4777,8 +5388,8 @@ async function resolveExplicitSkillSources(operatorHome, names) {
4777
5388
  for (const name of unique) {
4778
5389
  const matches = [];
4779
5390
  for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
4780
- const sourceRoot = resolve6(operatorHome, relativeRoot);
4781
- const candidate = resolve6(sourceRoot, name);
5391
+ const sourceRoot = resolve10(operatorHome, relativeRoot);
5392
+ const candidate = resolve10(sourceRoot, name);
4782
5393
  const rootStats = await lstat(sourceRoot).catch(() => void 0);
4783
5394
  const candidateStats = await lstat(candidate).catch(() => void 0);
4784
5395
  if (!candidateStats)
@@ -4795,7 +5406,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
4795
5406
  if (matches.length !== 1) {
4796
5407
  throw new Error(matches.length === 0 ? `shared skill is unavailable: ${name}` : `shared skill source is ambiguous: ${name}`);
4797
5408
  }
4798
- const skillMd = resolve6(matches[0], "SKILL.md");
5409
+ const skillMd = resolve10(matches[0], "SKILL.md");
4799
5410
  const skillStats = await lstat(skillMd).catch(() => void 0);
4800
5411
  if (!skillStats?.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
4801
5412
  throw new Error(`shared skill requires an ordinary SKILL.md: ${name}`);
@@ -4806,7 +5417,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
4806
5417
  }
4807
5418
  function assertContained(root, candidate) {
4808
5419
  const rel = relative(root, candidate);
4809
- if (rel === ".." || rel.startsWith(`..${sep}`) || resolve6(root, rel) !== resolve6(candidate)) {
5420
+ if (rel === ".." || rel.startsWith(`..${sep}`) || resolve10(root, rel) !== resolve10(candidate)) {
4810
5421
  throw new Error(`path escapes the agent skill boundary: ${candidate}`);
4811
5422
  }
4812
5423
  }
@@ -4821,7 +5432,7 @@ var BLOCKED_SHARED_FILENAMES = /^(?:\.env(?:\..*)?|auth\.json|\.credentials\.jso
4821
5432
  async function copySafeSkillTree(source, target, sourceRoot) {
4822
5433
  assertContained(sourceRoot, source);
4823
5434
  const resolvedSource = await realpath(source);
4824
- if (resolvedSource !== resolve6(source)) {
5435
+ if (resolvedSource !== resolve10(source)) {
4825
5436
  throw new Error(`shared skill path resolves through a link: ${source}`);
4826
5437
  }
4827
5438
  assertContained(sourceRoot, resolvedSource);
@@ -4832,11 +5443,11 @@ async function copySafeSkillTree(source, target, sourceRoot) {
4832
5443
  throw new Error(`shared skill contains credential or configuration material: ${source}`);
4833
5444
  }
4834
5445
  if (stats.isDirectory()) {
4835
- await mkdir(target, { mode: 448 });
5446
+ await mkdir3(target, { mode: 448 });
4836
5447
  for (const entry of await readdir(resolvedSource)) {
4837
5448
  if (entry === "." || entry === "..")
4838
5449
  throw new Error("invalid shared skill entry");
4839
- await copySafeSkillTree(resolve6(source, entry), resolve6(target, entry), sourceRoot);
5450
+ await copySafeSkillTree(resolve10(source, entry), resolve10(target, entry), sourceRoot);
4840
5451
  }
4841
5452
  return;
4842
5453
  }
@@ -4844,34 +5455,34 @@ async function copySafeSkillTree(source, target, sourceRoot) {
4844
5455
  throw new Error(`shared skill contains a nonordinary file: ${source}`);
4845
5456
  }
4846
5457
  await copyFile(resolvedSource, target);
4847
- await chmod(target, 384);
5458
+ await chmod2(target, 384);
4848
5459
  }
4849
5460
  async function writeIsolatedHarnessFile(path, content) {
4850
- const parent = dirname2(path);
5461
+ const parent = dirname4(path);
4851
5462
  const parentStats = await lstat(parent);
4852
5463
  if (!parentStats.isDirectory() || parentStats.isSymbolicLink()) {
4853
5464
  throw new Error(`isolated harness parent is not a real directory: ${parent}`);
4854
5465
  }
4855
- const temporary = resolve6(parent, `.${basename3(path)}.${process.pid}.${randomUUID()}.tmp`);
5466
+ const temporary = resolve10(parent, `.${basename3(path)}.${process.pid}.${randomUUID()}.tmp`);
4856
5467
  try {
4857
- await writeFile(temporary, content, { mode: 384, flag: "wx" });
4858
- await chmod(temporary, 384);
5468
+ await writeFile4(temporary, content, { mode: 384, flag: "wx" });
5469
+ await chmod2(temporary, 384);
4859
5470
  await rename(temporary, path);
4860
5471
  } finally {
4861
5472
  await unlink(temporary).catch(() => void 0);
4862
5473
  }
4863
5474
  }
4864
5475
  function roomAgentHomeEnv(root) {
4865
- const resolved = resolve6(root);
5476
+ const resolved = resolve10(root);
4866
5477
  return {
4867
- HOME: resolve6(resolved, "user"),
4868
- CLAUDE_CONFIG_DIR: resolve6(resolved, "claude"),
4869
- CODEX_HOME: resolve6(resolved, "codex"),
4870
- GROK_HOME: resolve6(resolved, "grok"),
4871
- PI_CODING_AGENT_DIR: resolve6(resolved, "pi"),
4872
- XDG_STATE_HOME: resolve6(resolved, "state"),
4873
- XDG_CACHE_HOME: resolve6(resolved, "cache"),
4874
- TMPDIR: resolve6(resolved, "tmp")
5478
+ HOME: resolve10(resolved, "user"),
5479
+ CLAUDE_CONFIG_DIR: resolve10(resolved, "claude"),
5480
+ CODEX_HOME: resolve10(resolved, "codex"),
5481
+ GROK_HOME: resolve10(resolved, "grok"),
5482
+ PI_CODING_AGENT_DIR: resolve10(resolved, "pi"),
5483
+ XDG_STATE_HOME: resolve10(resolved, "state"),
5484
+ XDG_CACHE_HOME: resolve10(resolved, "cache"),
5485
+ TMPDIR: resolve10(resolved, "tmp")
4875
5486
  };
4876
5487
  }
4877
5488
  var HARNESS_STATE_ENV_VARS = [
@@ -4888,14 +5499,14 @@ function harnessStateDirsFromEnv(env) {
4888
5499
  for (const name of HARNESS_STATE_ENV_VARS) {
4889
5500
  const value = env[name];
4890
5501
  if (value)
4891
- stateDirs.push(resolve6(value));
5502
+ stateDirs.push(resolve10(value));
4892
5503
  }
4893
5504
  const tmp = env.TMPDIR;
4894
- return { stateDirs, ...tmp ? { tmpDir: resolve6(tmp) } : {} };
5505
+ return { stateDirs, ...tmp ? { tmpDir: resolve10(tmp) } : {} };
4895
5506
  }
4896
5507
 
4897
5508
  // apps/body/dist/attachment-delivery.js
4898
- import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
5509
+ import { mkdir as mkdir4, writeFile as writeFile5 } from "node:fs/promises";
4899
5510
  import { basename as basename4, extname, join as join2 } from "node:path";
4900
5511
  var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
4901
5512
  var FETCH_TIMEOUT_MS = 3e4;
@@ -4913,7 +5524,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
4913
5524
  if (!attachments.length)
4914
5525
  return [];
4915
5526
  const taken = /* @__PURE__ */ new Set();
4916
- await mkdir2(dir, { recursive: true });
5527
+ await mkdir4(dir, { recursive: true });
4917
5528
  return Promise.all(attachments.map(async (attachment, index) => {
4918
5529
  const tooLarge = (bytes) => ({
4919
5530
  attachment,
@@ -4934,7 +5545,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
4934
5545
  if (bytes.length > MAX_ATTACHMENT_BYTES)
4935
5546
  return tooLarge(bytes.length);
4936
5547
  const path = join2(dir, safeFileName(attachment, index, taken));
4937
- await writeFile2(path, bytes);
5548
+ await writeFile5(path, bytes);
4938
5549
  const mimeType = attachment.mimeType ?? response.headers.get("content-type") ?? "";
4939
5550
  return {
4940
5551
  attachment,
@@ -5047,8 +5658,23 @@ function sanitizeAgentReply(message) {
5047
5658
  return lines.slice(1).join("\n").trim();
5048
5659
  }
5049
5660
 
5661
+ // apps/body/dist/turn-failure-reason.js
5662
+ var TURN_FAILURE_REASON_MAX = 200;
5663
+ function redactToolDetail(value) {
5664
+ return value.replace(/\b(["']?)(api[_-]?key|token|secret|password|passwd|authorization|credential|cookie|private[_-]?key)\1\s*[:=]\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,}\]]+)/gi, '"$2": "[REDACTED]"').replace(/\b(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, (assignment) => `${assignment.slice(0, assignment.indexOf("="))}=[REDACTED]`).replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/gi, "[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(Bearer\s+)[^\s,]+/gi, "$1[REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g, "[REDACTED]").replace(/(--?(?:api[_-]?key|token|secret|password|authorization|credential|cookie)\s+)(?:"[^"]*"|'[^']*'|\S+)/gi, "$1[REDACTED]");
5665
+ }
5666
+ function distillTurnFailureReason(error) {
5667
+ const raw = error instanceof Error ? error.message : typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(error.message) : error == null ? "" : String(error);
5668
+ const firstLine = raw.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^at\s/.test(line)) ?? "";
5669
+ const stripped = firstLine.replace(/^(?:[A-Za-z]*Error|Error):\s*/, "").replace(/\s+/g, " ");
5670
+ const clean4 = redactToolDetail(stripped).trim();
5671
+ if (!clean4)
5672
+ return "turn failed";
5673
+ return clean4.length > TURN_FAILURE_REASON_MAX ? `${clean4.slice(0, TURN_FAILURE_REASON_MAX - 1)}\u2026` : clean4;
5674
+ }
5675
+
5050
5676
  // apps/body/dist/room-session.js
5051
- import { resolve as resolve7 } from "node:path";
5677
+ import { resolve as resolve11 } from "node:path";
5052
5678
 
5053
5679
  // apps/body/dist/read-only-policy.js
5054
5680
  var READ_ONLY_MCP_SERVER_NAME = "beeline-readonly-mcp";
@@ -5180,7 +5806,11 @@ function beelineAgentMcpServer(config, api, context) {
5180
5806
  { name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
5181
5807
  { name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
5182
5808
  ...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
5183
- ...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : []
5809
+ ...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : [],
5810
+ ...context.grantRunner ? [
5811
+ { name: "BEELINE_GRANT_RUNNER_URL", value: context.grantRunner.url },
5812
+ { name: "BEELINE_GRANT_RUNNER_TOKEN", value: context.grantRunner.token }
5813
+ ] : []
5184
5814
  ]
5185
5815
  };
5186
5816
  }
@@ -5194,14 +5824,14 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
5194
5824
  command: config.readonlyMcpCommand,
5195
5825
  args: [...config.readonlyMcpArgs ?? []],
5196
5826
  env: [
5197
- { name: "BEELINE_READONLY_ROOT", value: resolve7(cwd) },
5827
+ { name: "BEELINE_READONLY_ROOT", value: resolve11(cwd) },
5198
5828
  ...config.agentHomeRoot ? [
5199
5829
  {
5200
5830
  name: "BEELINE_READONLY_AGENT_SKILLS_ROOT",
5201
- value: resolve7(config.agentHomeRoot, skillDir, "skills")
5831
+ value: resolve11(config.agentHomeRoot, skillDir, "skills")
5202
5832
  }
5203
5833
  ] : [],
5204
- ...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve7(agentMemoryDir) }] : []
5834
+ ...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve11(agentMemoryDir) }] : []
5205
5835
  ]
5206
5836
  };
5207
5837
  }
@@ -5209,8 +5839,8 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
5209
5839
  // apps/body/dist/bwrap-sandbox.js
5210
5840
  import { spawnSync } from "node:child_process";
5211
5841
  import { lstatSync as lstatSync2 } from "node:fs";
5212
- import { homedir as homedir3 } from "node:os";
5213
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve8 } from "node:path";
5842
+ import { homedir as homedir4 } from "node:os";
5843
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve12 } from "node:path";
5214
5844
  var DEFAULT_SANDBOX_POLICY = "bwrap";
5215
5845
  function isSandboxPolicy(value) {
5216
5846
  return value === "bwrap" || value === "off";
@@ -5242,12 +5872,12 @@ var HARNESS_HOME_STATE_DIRS = [
5242
5872
  dirs: [".grok"]
5243
5873
  }
5244
5874
  ];
5245
- function harnessHomeStateDirs(agentCommand, home = homedir3()) {
5875
+ function harnessHomeStateDirs(agentCommand, home = homedir4()) {
5246
5876
  if (!agentCommand)
5247
5877
  return [];
5248
5878
  for (const { match, dirs } of HARNESS_HOME_STATE_DIRS) {
5249
5879
  if (match.test(agentCommand))
5250
- return dirs.map((dir) => resolve8(home, dir));
5880
+ return dirs.map((dir) => resolve12(home, dir));
5251
5881
  }
5252
5882
  return [];
5253
5883
  }
@@ -5259,7 +5889,7 @@ var KNOWN_CREDENTIAL_MASK_PATHS = [
5259
5889
  ".git-credentials",
5260
5890
  ".secrets.env"
5261
5891
  ];
5262
- function credentialMaskPaths(extraPaths, home = homedir3(), stat3 = (path) => {
5892
+ function credentialMaskPaths(extraPaths, home = homedir4(), stat3 = (path) => {
5263
5893
  try {
5264
5894
  const info = lstatSync2(path);
5265
5895
  return { isDirectory: info.isDirectory() };
@@ -5267,10 +5897,10 @@ function credentialMaskPaths(extraPaths, home = homedir3(), stat3 = (path) => {
5267
5897
  return void 0;
5268
5898
  }
5269
5899
  }, requiredPaths = []) {
5270
- const required = new Set(requiredPaths.map((path) => resolve8(path)));
5900
+ const required = new Set(requiredPaths.map((path) => resolve12(path)));
5271
5901
  const candidates = [
5272
- ...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) => resolve8(home, entry)),
5273
- ...(extraPaths ?? []).map((entry) => resolve8(entry))
5902
+ ...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) => resolve12(home, entry)),
5903
+ ...(extraPaths ?? []).map((entry) => resolve12(entry))
5274
5904
  ];
5275
5905
  const seen = /* @__PURE__ */ new Set();
5276
5906
  const masks = [];
@@ -5297,7 +5927,7 @@ function normalize(paths) {
5297
5927
  for (const path of paths) {
5298
5928
  if (!path)
5299
5929
  continue;
5300
- seen.add(resolve8(path));
5930
+ seen.add(resolve12(path));
5301
5931
  }
5302
5932
  return Array.from(seen).sort();
5303
5933
  }
@@ -5335,7 +5965,7 @@ function sandboxMountPlan(spec) {
5335
5965
  writable,
5336
5966
  quotaTmpfs: spec.workbench ? [
5337
5967
  {
5338
- target: resolve8(spec.workbench.dir),
5968
+ target: resolve12(spec.workbench.dir),
5339
5969
  maxBytes: spec.workbench.maxBytes,
5340
5970
  maxInodes: spec.workbench.maxInodes,
5341
5971
  blockGit: true
@@ -5390,7 +6020,7 @@ function buildBwrapArgv(input) {
5390
6020
  for (const binding of quotaTmpfs) {
5391
6021
  args.push("--dir", binding.target, "--size", String(binding.maxBytes), "--tmpfs", binding.target);
5392
6022
  if (binding.blockGit)
5393
- args.push("--ro-bind", "/dev/null", resolve8(binding.target, ".git"));
6023
+ args.push("--ro-bind", "/dev/null", resolve12(binding.target, ".git"));
5394
6024
  }
5395
6025
  args.push("--chdir", input.cwd);
5396
6026
  args.push("--die-with-parent");
@@ -5454,13 +6084,156 @@ function detectBwrapSandbox(options = {}) {
5454
6084
  };
5455
6085
  }
5456
6086
 
6087
+ // apps/body/dist/pi-turn-record.js
6088
+ import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
6089
+ import { resolve as resolve13 } from "node:path";
6090
+ function summarizeProviderError(errorMessage2) {
6091
+ const trimmed = errorMessage2.trim();
6092
+ const statusMatch = /^(\d{3}):\s*([\s\S]*)$/.exec(trimmed);
6093
+ const status = statusMatch ? Number(statusMatch[1]) : void 0;
6094
+ let body = statusMatch ? statusMatch[2].trim() : trimmed;
6095
+ if (body.startsWith("{")) {
6096
+ try {
6097
+ const parsed = JSON.parse(body);
6098
+ const message = typeof parsed.message === "string" ? parsed.message : typeof parsed.error === "string" ? parsed.error : typeof parsed.error?.message === "string" ? parsed.error.message : void 0;
6099
+ if (message)
6100
+ body = message;
6101
+ } catch {
6102
+ }
6103
+ }
6104
+ const firstLine = body.split(/\r?\n/).find((line) => line.trim()) ?? "";
6105
+ const reason = `provider error${status === void 0 ? "" : ` ${status}`}${firstLine ? `: ${firstLine.trim()}` : ""}`;
6106
+ return status === void 0 ? { reason } : { reason, status };
6107
+ }
6108
+ async function sessionFileFromMap(home, sessionId) {
6109
+ try {
6110
+ const raw = await readFile5(resolve13(home, ".pi", "pi-acp", "session-map.json"), "utf8");
6111
+ const map = JSON.parse(raw);
6112
+ const file = map.sessions?.[sessionId]?.sessionFile;
6113
+ return typeof file === "string" && file ? file : void 0;
6114
+ } catch {
6115
+ return void 0;
6116
+ }
6117
+ }
6118
+ async function sessionFileFromLayout(piDir, sessionId) {
6119
+ const sessionsRoot = resolve13(piDir, "sessions");
6120
+ const suffix = `_${sessionId}.jsonl`;
6121
+ let projects;
6122
+ try {
6123
+ projects = await readdir2(sessionsRoot);
6124
+ } catch {
6125
+ return void 0;
6126
+ }
6127
+ for (const project of projects) {
6128
+ const dir = resolve13(sessionsRoot, project);
6129
+ let files;
6130
+ try {
6131
+ files = await readdir2(dir);
6132
+ } catch {
6133
+ continue;
6134
+ }
6135
+ const match = files.find((file) => file.endsWith(suffix));
6136
+ if (match)
6137
+ return resolve13(dir, match);
6138
+ }
6139
+ return void 0;
6140
+ }
6141
+ function messageText(content) {
6142
+ if (!Array.isArray(content))
6143
+ return "";
6144
+ return content.map((block2) => block2 && typeof block2 === "object" && block2.type === "text" ? String(block2.text ?? "") : "").join("").trim();
6145
+ }
6146
+ async function readPiTurnRecord(input) {
6147
+ const piDir = input.agentEnv.PI_CODING_AGENT_DIR;
6148
+ if (!piDir || !input.sessionId)
6149
+ return void 0;
6150
+ const file = (input.agentEnv.HOME ? await sessionFileFromMap(input.agentEnv.HOME, input.sessionId) : void 0) ?? await sessionFileFromLayout(piDir, input.sessionId);
6151
+ if (!file)
6152
+ return void 0;
6153
+ let raw;
6154
+ try {
6155
+ raw = await readFile5(file, "utf8");
6156
+ } catch {
6157
+ return void 0;
6158
+ }
6159
+ let sawUser = false;
6160
+ let last;
6161
+ for (const line of raw.split(/\r?\n/)) {
6162
+ if (!line.trim())
6163
+ continue;
6164
+ let entry;
6165
+ try {
6166
+ entry = JSON.parse(line);
6167
+ } catch {
6168
+ continue;
6169
+ }
6170
+ if (entry.type !== "message" || !entry.message)
6171
+ continue;
6172
+ if (entry.message.role === "user") {
6173
+ sawUser = true;
6174
+ last = void 0;
6175
+ } else if (entry.message.role === "assistant") {
6176
+ last = entry.message;
6177
+ }
6178
+ }
6179
+ if (!sawUser && !last)
6180
+ return void 0;
6181
+ if (!last)
6182
+ return { kind: "missing" };
6183
+ if (last.stopReason === "error") {
6184
+ return {
6185
+ kind: "error",
6186
+ ...summarizeProviderError(typeof last.errorMessage === "string" && last.errorMessage.trim() ? last.errorMessage : "unknown error")
6187
+ };
6188
+ }
6189
+ const text2 = messageText(last.content);
6190
+ if (text2)
6191
+ return { kind: "answer", text: text2 };
6192
+ return {
6193
+ kind: "empty",
6194
+ stopReason: typeof last.stopReason === "string" ? last.stopReason : "unknown"
6195
+ };
6196
+ }
6197
+
6198
+ // apps/body/dist/empty-turn.js
6199
+ async function explainEmptyAgentTurn(input) {
6200
+ const streamFact = describeEmptyTurn(input.result, input.agentLabel);
6201
+ if (!isPiAcpHarness(input.agentLabel))
6202
+ return { reason: streamFact };
6203
+ const record2 = await readPiTurnRecord({ agentEnv: input.agentEnv, sessionId: input.sessionId });
6204
+ if (!record2)
6205
+ return { reason: `pi left no readable turn record; ${streamFact}` };
6206
+ switch (record2.kind) {
6207
+ case "error":
6208
+ return { reason: record2.reason, record: record2 };
6209
+ case "empty":
6210
+ return {
6211
+ reason: `the model ended its turn with no text (stop reason ${record2.stopReason})`,
6212
+ record: record2
6213
+ };
6214
+ case "answer":
6215
+ return {
6216
+ recoveredText: record2.text,
6217
+ reason: "pi recorded the answer but the ACP stream delivered no text",
6218
+ record: record2
6219
+ };
6220
+ case "missing":
6221
+ return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record: record2 };
6222
+ }
6223
+ }
6224
+ function isAccountOrProviderRefusal(record2) {
6225
+ if (!record2 || record2.kind !== "error" || record2.status === void 0)
6226
+ return false;
6227
+ return [401, 402, 403, 407, 408, 429].includes(record2.status) || record2.status >= 500;
6228
+ }
6229
+
5457
6230
  // apps/body/dist/runtime.js
5458
- import { randomBytes as randomBytes5 } from "node:crypto";
5459
- import { execFile } from "node:child_process";
6231
+ import { randomBytes as randomBytes6 } from "node:crypto";
6232
+ import { execFile as execFile2 } from "node:child_process";
5460
6233
  import { closeSync, openSync } from "node:fs";
5461
- import { mkdir as mkdir3, readFile, readdir as readdir2, rename as rename2, stat, writeFile as writeFile3 } from "node:fs/promises";
5462
- import { homedir as homedir4 } from "node:os";
5463
- import { dirname as dirname3, resolve as resolve9 } from "node:path";
6234
+ import { mkdir as mkdir5, readFile as readFile6, readdir as readdir3, rename as rename2, stat, writeFile as writeFile6 } from "node:fs/promises";
6235
+ import { homedir as homedir5 } from "node:os";
6236
+ import { dirname as dirname5, resolve as resolve14 } from "node:path";
5464
6237
  import { spawn as spawn2 } from "node:child_process";
5465
6238
  import { promisify } from "node:util";
5466
6239
 
@@ -5608,7 +6381,7 @@ function createHasher(hashCons, info = {}) {
5608
6381
  Object.assign(hashC, info);
5609
6382
  return Object.freeze(hashC);
5610
6383
  }
5611
- function randomBytes(bytesLength = 32) {
6384
+ function randomBytes2(bytesLength = 32) {
5612
6385
  anumber(bytesLength, "bytesLength");
5613
6386
  const cr = typeof globalThis === "object" ? globalThis.crypto : null;
5614
6387
  if (typeof cr?.getRandomValues !== "function")
@@ -5926,7 +6699,7 @@ var bytesToHex2 = bytesToHex;
5926
6699
  var concatBytes2 = (...arrays) => concatBytes(...arrays);
5927
6700
  var hexToBytes2 = (hex) => hexToBytes(hex);
5928
6701
  var isBytes2 = isBytes;
5929
- var randomBytes2 = (bytesLength) => randomBytes(bytesLength);
6702
+ var randomBytes3 = (bytesLength) => randomBytes2(bytesLength);
5930
6703
  var _0n = /* @__PURE__ */ BigInt(0);
5931
6704
  var _1n = /* @__PURE__ */ BigInt(1);
5932
6705
  var atitle2 = (title) => title ? `"${title}" ` : "";
@@ -6518,18 +7291,18 @@ function validateTableBytes(numPoints, fpBytes) {
6518
7291
  if (bytes > TABLE_BYTES_MAX)
6519
7292
  throw new Error("invalid window size: table would need ~" + Math.ceil(bytes / 2 ** 20) + " MiB, max " + TABLE_BYTES_MAX / 2 ** 20 + " MiB");
6520
7293
  }
6521
- function probeRandomBytes(randomBytes6, length) {
6522
- if (randomBytes6 === void 0)
7294
+ function probeRandomBytes(randomBytes7, length) {
7295
+ if (randomBytes7 === void 0)
6523
7296
  return void 0;
6524
- afunction(randomBytes6, "randomBytes");
7297
+ afunction(randomBytes7, "randomBytes");
6525
7298
  try {
6526
- const probe = randomBytes6(length);
7299
+ const probe = randomBytes7(length);
6527
7300
  if (!isBytes2(probe) || probe.length !== length)
6528
7301
  return void 0;
6529
7302
  } catch {
6530
7303
  return void 0;
6531
7304
  }
6532
- return randomBytes6;
7305
+ return randomBytes7;
6533
7306
  }
6534
7307
  function validateMSMPoints(points, c2) {
6535
7308
  aarray(points, "points");
@@ -6622,9 +7395,9 @@ var ScalarMultiplier = class {
6622
7395
  baseCanBeBlinded;
6623
7396
  bits;
6624
7397
  // Parametrized with a given Point class (not individual point)
6625
- constructor(Point, randomBytes6) {
7398
+ constructor(Point, randomBytes7) {
6626
7399
  validatePointCons(Point);
6627
- this.randomBytes = probeRandomBytes(randomBytes6, BLIND_BYTES);
7400
+ this.randomBytes = probeRandomBytes(randomBytes7, BLIND_BYTES);
6628
7401
  this.Point = Point;
6629
7402
  this.BASE = Point.BASE;
6630
7403
  this.ZERO = Point.ZERO;
@@ -6912,7 +7685,7 @@ function weierstrass(params, extraOpts = {}) {
6912
7685
  randomBytes: "function"
6913
7686
  });
6914
7687
  const { endo, allowInfinityPoint } = extraOpts;
6915
- const randomBytes6 = extraOpts.randomBytes === void 0 ? randomBytes2 : extraOpts.randomBytes;
7688
+ const randomBytes7 = extraOpts.randomBytes === void 0 ? randomBytes3 : extraOpts.randomBytes;
6916
7689
  if (endo) {
6917
7690
  if (!Fp.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) {
6918
7691
  throw new Error('invalid endo: expected "beta": bigint and "basises": array');
@@ -7324,7 +8097,7 @@ function weierstrass(params, extraOpts = {}) {
7324
8097
  }
7325
8098
  }
7326
8099
  const normalize2 = (points) => normalizeZ(Point, points);
7327
- const wnaf = new ScalarMultiplier(Point, randomBytes6);
8100
+ const wnaf = new ScalarMultiplier(Point, randomBytes7);
7328
8101
  if (wnaf.bits >= 6)
7329
8102
  Point.BASE.precompute(6);
7330
8103
  Object.freeze(Point.prototype);
@@ -7433,7 +8206,7 @@ function challenge(...args) {
7433
8206
  function schnorrGetPublicKey(secretKey) {
7434
8207
  return schnorrGetExtPubKey(secretKey).bytes;
7435
8208
  }
7436
- function schnorrSign(message, secretKey, auxRand = randomBytes(32)) {
8209
+ function schnorrSign(message, secretKey, auxRand = randomBytes2(32)) {
7437
8210
  const { Fn, BASE } = Pointk1;
7438
8211
  const m2 = abytes2(message, void 0, "message");
7439
8212
  const { bytes: px, scalar: d } = schnorrGetExtPubKey(secretKey);
@@ -7483,7 +8256,7 @@ var schnorr = /* @__PURE__ */ (() => {
7483
8256
  const size = 32;
7484
8257
  const seedLength = 48;
7485
8258
  const randomSecretKey = (seed) => {
7486
- seed = seed === void 0 ? randomBytes(seedLength) : seed;
8259
+ seed = seed === void 0 ? randomBytes2(seedLength) : seed;
7487
8260
  return mapHashToField(abytes2(seed, seedLength, "seed"), secp256k1_CURVE.n);
7488
8261
  };
7489
8262
  return Object.freeze({
@@ -7630,7 +8403,7 @@ function createHasher2(hashCons, info = {}) {
7630
8403
  Object.assign(hashC, info);
7631
8404
  return Object.freeze(hashC);
7632
8405
  }
7633
- function randomBytes3(bytesLength = 32) {
8406
+ function randomBytes4(bytesLength = 32) {
7634
8407
  const cr = typeof globalThis === "object" ? globalThis.crypto : null;
7635
8408
  if (typeof cr?.getRandomValues !== "function")
7636
8409
  throw new Error("crypto.getRandomValues must be defined");
@@ -9326,7 +10099,7 @@ function getWLengths2(Fp, Fn) {
9326
10099
  }
9327
10100
  function ecdh(Point, ecdhOpts = {}) {
9328
10101
  const { Fn } = Point;
9329
- const randomBytes_ = ecdhOpts.randomBytes || randomBytes3;
10102
+ const randomBytes_ = ecdhOpts.randomBytes || randomBytes4;
9330
10103
  const lengths = Object.assign(getWLengths2(Point.Fp, Fn), { seed: getMinHashLength2(Fn.ORDER) });
9331
10104
  function isValidSecretKey(secretKey) {
9332
10105
  try {
@@ -9391,7 +10164,7 @@ function ecdsa2(Point, hash, ecdsaOpts = {}) {
9391
10164
  bits2int_modN: "function"
9392
10165
  });
9393
10166
  ecdsaOpts = Object.assign({}, ecdsaOpts);
9394
- const randomBytes6 = ecdsaOpts.randomBytes || randomBytes3;
10167
+ const randomBytes7 = ecdsaOpts.randomBytes || randomBytes4;
9395
10168
  const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
9396
10169
  const { Fp, Fn } = Point;
9397
10170
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
@@ -9533,7 +10306,7 @@ function ecdsa2(Point, hash, ecdsaOpts = {}) {
9533
10306
  throw new Error("invalid private key");
9534
10307
  const seedArgs = [int2octets(d), int2octets(h1int)];
9535
10308
  if (extraEntropy != null && extraEntropy !== false) {
9536
- const e = extraEntropy === true ? randomBytes6(lengths.secretKey) : extraEntropy;
10309
+ const e = extraEntropy === true ? randomBytes7(lengths.secretKey) : extraEntropy;
9537
10310
  seedArgs.push(abytes3(e, void 0, "extraEntropy"));
9538
10311
  }
9539
10312
  const seed = concatBytes3(...seedArgs);
@@ -9700,7 +10473,7 @@ function challenge2(...args) {
9700
10473
  function schnorrGetPublicKey2(secretKey) {
9701
10474
  return schnorrGetExtPubKey2(secretKey).bytes;
9702
10475
  }
9703
- function schnorrSign2(message, secretKey, auxRand = randomBytes3(32)) {
10476
+ function schnorrSign2(message, secretKey, auxRand = randomBytes4(32)) {
9704
10477
  const { Fn } = Pointk12;
9705
10478
  const m2 = abytes3(message, void 0, "message");
9706
10479
  const { bytes: px, scalar: d } = schnorrGetExtPubKey2(secretKey);
@@ -9742,7 +10515,7 @@ function schnorrVerify2(signature, message, publicKey) {
9742
10515
  var schnorr2 = /* @__PURE__ */ (() => {
9743
10516
  const size = 32;
9744
10517
  const seedLength = 48;
9745
- const randomSecretKey = (seed = randomBytes3(seedLength)) => {
10518
+ const randomSecretKey = (seed = randomBytes4(seedLength)) => {
9746
10519
  return mapHashToField2(seed, secp256k1_CURVE2.n);
9747
10520
  };
9748
10521
  return {
@@ -12106,7 +12879,7 @@ function encrypt2(secretKey, pubkey, text2) {
12106
12879
  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes3(secretKey);
12107
12880
  const key = secp256k1.getSharedSecret(privkey, hexToBytes3("02" + pubkey));
12108
12881
  const normalizedKey = getNormalizedX(key);
12109
- let iv = Uint8Array.from(randomBytes3(16));
12882
+ let iv = Uint8Array.from(randomBytes4(16));
12110
12883
  let plaintext = utf8Encoder.encode(text2);
12111
12884
  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
12112
12885
  let ctb64 = base64.encode(new Uint8Array(ciphertext));
@@ -12477,7 +13250,7 @@ function decodePayload(payload) {
12477
13250
  mac: data.subarray(-32)
12478
13251
  };
12479
13252
  }
12480
- function encrypt22(plaintext, conversationKey, nonce = randomBytes3(32)) {
13253
+ function encrypt22(plaintext, conversationKey, nonce = randomBytes4(32)) {
12481
13254
  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
12482
13255
  const padded = pad(plaintext);
12483
13256
  const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
@@ -13992,23 +14765,23 @@ function decodeNsec(nsec) {
13992
14765
  }
13993
14766
 
13994
14767
  // apps/body/dist/runtime.js
13995
- var execFileAsync = promisify(execFile);
14768
+ var execFileAsync = promisify(execFile2);
13996
14769
  var DEFAULT_AGENT_IDENTITY_NAME = "beeline-agent";
13997
14770
  var DEFAULT_BODY_IDENTITY_NAME = "beeline-body";
13998
14771
  var DEFAULT_DAEMON_MONOLITH_BASE_URL = "https://server.usebeeline.app";
13999
14772
  function defaultSupervisorRoot(env = process.env) {
14000
- return resolve9(env.XDG_STATE_HOME ?? resolve9(homedir4(), ".local", "state"));
14773
+ return resolve14(env.XDG_STATE_HOME ?? resolve14(homedir5(), ".local", "state"));
14001
14774
  }
14002
14775
  function runtimeDirectory(supervisorRoot, publicKey) {
14003
14776
  if (!/^[0-9a-f]{64}$/i.test(publicKey))
14004
14777
  throw new Error("invalid agent public key");
14005
- return resolve9(supervisorRoot, "beeline", "agents", publicKey.toLowerCase());
14778
+ return resolve14(supervisorRoot, "beeline", "agents", publicKey.toLowerCase());
14006
14779
  }
14007
14780
  function runtimeConfigPath(supervisorRoot, publicKey) {
14008
- return resolve9(runtimeDirectory(supervisorRoot, publicKey), "runtime.json");
14781
+ return resolve14(runtimeDirectory(supervisorRoot, publicKey), "runtime.json");
14009
14782
  }
14010
14783
  function identityFromKey(value, name) {
14011
- const secretKey = value ? value.startsWith("nsec1") ? decodeNsec(value) : Uint8Array.from(Buffer.from(value, "hex")) : randomBytes5(32);
14784
+ const secretKey = value ? value.startsWith("nsec1") ? decodeNsec(value) : Uint8Array.from(Buffer.from(value, "hex")) : randomBytes6(32);
14012
14785
  if (secretKey.length !== 32)
14013
14786
  throw new Error("identity secret key must be 32 bytes");
14014
14787
  return { name, secretKey, publicKey: getPublicKey2(secretKey) };
@@ -14035,15 +14808,15 @@ function runtimeAgentCommand(runtime) {
14035
14808
  }
14036
14809
  async function writeRuntimeRecord(runtime) {
14037
14810
  const path = runtimeConfigPath(runtime.supervisorRoot, runtime.agent.publicKey);
14038
- await mkdir3(dirname3(path), { recursive: true, mode: 448 });
14811
+ await mkdir5(dirname5(path), { recursive: true, mode: 448 });
14039
14812
  const staged = `${path}.${process.pid}.tmp`;
14040
- await writeFile3(staged, `${JSON.stringify(runtime, null, 2)}
14813
+ await writeFile6(staged, `${JSON.stringify(runtime, null, 2)}
14041
14814
  `, { mode: 384 });
14042
14815
  await rename2(staged, path);
14043
14816
  return path;
14044
14817
  }
14045
14818
  async function readRuntimeRecord(path) {
14046
- const parsed = JSON.parse(await readFile(path, "utf8"));
14819
+ const parsed = JSON.parse(await readFile6(path, "utf8"));
14047
14820
  if (parsed.version !== 2 || !parsed.agent || !parsed.body || !parsed.communityId) {
14048
14821
  throw new Error(`invalid agent runtime record: ${path}`);
14049
14822
  }
@@ -14061,7 +14834,7 @@ async function migrateRuntimeRecordAccessPolicy(path) {
14061
14834
  return { runtime, migrated: true };
14062
14835
  }
14063
14836
  async function stageMonolithAgentRuntime(input) {
14064
- const supervisorRoot = input.supervisorRoot ? resolve9(input.supervisorRoot) : defaultSupervisorRoot();
14837
+ const supervisorRoot = input.supervisorRoot ? resolve14(input.supervisorRoot) : defaultSupervisorRoot();
14065
14838
  const configPath = runtimeConfigPath(supervisorRoot, input.agentIdentity.publicKey);
14066
14839
  const configuredBaseUrl = input.monolithBaseUrl ?? DEFAULT_DAEMON_MONOLITH_BASE_URL;
14067
14840
  const baseUrl = new URL(configuredBaseUrl).origin;
@@ -14106,9 +14879,9 @@ async function stageMonolithAgentRuntime(input) {
14106
14879
  return { runtime, configPath };
14107
14880
  }
14108
14881
  async function runtimePaths(root) {
14109
- const agents = resolve9(root, "beeline", "agents");
14110
- const entries = await readdir2(agents, { withFileTypes: true }).catch(() => []);
14111
- return entries.filter((entry) => entry.isDirectory()).map((entry) => resolve9(agents, entry.name, "runtime.json"));
14882
+ const agents = resolve14(root, "beeline", "agents");
14883
+ const entries = await readdir3(agents, { withFileTypes: true }).catch(() => []);
14884
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => resolve14(agents, entry.name, "runtime.json"));
14112
14885
  }
14113
14886
  async function findAgentRuntimeConfigPaths(env = process.env, _cwd = process.cwd()) {
14114
14887
  return runtimePaths(defaultSupervisorRoot(env));
@@ -14117,13 +14890,13 @@ async function findRuntimeConfigPaths(cwd = process.cwd(), env = process.env) {
14117
14890
  return findAgentRuntimeConfigPaths(env, cwd);
14118
14891
  }
14119
14892
  async function resolveRuntimeConfigPath(path) {
14120
- return resolve9(path);
14893
+ return resolve14(path);
14121
14894
  }
14122
14895
  async function selectRuntimeConfigPaths(options) {
14123
14896
  const hostScope = true;
14124
14897
  const configs = await options.findHostRuntimes(options.cwd);
14125
14898
  const requestedPubkey = options.requestedPubkey;
14126
- const paths = requestedPubkey ? configs.filter((path) => dirname3(path).endsWith(requestedPubkey)) : [...new Set(configs)];
14899
+ const paths = requestedPubkey ? configs.filter((path) => dirname5(path).endsWith(requestedPubkey)) : [...new Set(configs)];
14127
14900
  if (!paths.length)
14128
14901
  throw new Error(options.noRuntimeMessage(hostScope));
14129
14902
  if (options.requestedPubkey && paths.length > 1)
@@ -14132,7 +14905,7 @@ async function selectRuntimeConfigPaths(options) {
14132
14905
  }
14133
14906
  async function runtimeDaemonPid(configPath) {
14134
14907
  try {
14135
- const pid = Number((await readFile(resolve9(dirname3(configPath), "daemon.pid"), "utf8")).trim());
14908
+ const pid = Number((await readFile6(resolve14(dirname5(configPath), "daemon.pid"), "utf8")).trim());
14136
14909
  if (!Number.isSafeInteger(pid) || pid <= 0)
14137
14910
  return null;
14138
14911
  process.kill(pid, 0);
@@ -14143,13 +14916,13 @@ async function runtimeDaemonPid(configPath) {
14143
14916
  }
14144
14917
  async function daemonIsThisRuntime(pid, configPath) {
14145
14918
  try {
14146
- const argv = (await readFile(`/proc/${pid}/cmdline`, "utf8")).split("\0").filter(Boolean);
14919
+ const argv = (await readFile6(`/proc/${pid}/cmdline`, "utf8")).split("\0").filter(Boolean);
14147
14920
  const flag = argv.lastIndexOf("--config");
14148
- return flag > 0 && argv[flag - 1] === "daemon" && resolve9(argv[flag + 1]) === resolve9(configPath);
14921
+ return flag > 0 && argv[flag - 1] === "daemon" && resolve14(argv[flag + 1]) === resolve14(configPath);
14149
14922
  } catch {
14150
14923
  try {
14151
14924
  const { stdout: stdout6 } = await execFileAsync("ps", ["-p", String(pid), "-o", "command="]);
14152
- return stdout6.includes(" daemon ") && stdout6.includes(resolve9(configPath));
14925
+ return stdout6.includes(" daemon ") && stdout6.includes(resolve14(configPath));
14153
14926
  } catch {
14154
14927
  return false;
14155
14928
  }
@@ -14175,14 +14948,14 @@ async function stopRuntimeDaemon(path, opts = {}) {
14175
14948
  throw new Error(`agent daemon ${pid} did not stop after ${timeout}ms`);
14176
14949
  }
14177
14950
  async function launchRuntimeDaemon(configPath, opts = {}) {
14178
- const directory = dirname3(configPath);
14179
- await mkdir3(directory, { recursive: true, mode: 448 });
14951
+ const directory = dirname5(configPath);
14952
+ await mkdir5(directory, { recursive: true, mode: 448 });
14180
14953
  const foreground = opts.foreground === true;
14181
- const output = foreground ? "inherit" : openSync(resolve9(directory, "daemon.log"), "a", 384);
14954
+ const output = foreground ? "inherit" : openSync(resolve14(directory, "daemon.log"), "a", 384);
14182
14955
  const entrypoint = opts.entrypoint ?? process.argv[1];
14183
14956
  if (!entrypoint)
14184
14957
  throw new Error("cannot resolve daemon CLI entrypoint");
14185
- const child = spawn2(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve9(configPath)], {
14958
+ const child = spawn2(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve14(configPath)], {
14186
14959
  cwd: directory,
14187
14960
  env: opts.env ?? process.env,
14188
14961
  detached: !foreground,
@@ -14199,9 +14972,9 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
14199
14972
  }
14200
14973
  async function removeAgentRuntime(runtime) {
14201
14974
  const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
14202
- const deletedRoot = resolve9(runtime.supervisorRoot, "beeline", "deleted-runtimes");
14203
- await mkdir3(deletedRoot, { recursive: true, mode: 448 });
14204
- const target = resolve9(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
14975
+ const deletedRoot = resolve14(runtime.supervisorRoot, "beeline", "deleted-runtimes");
14976
+ await mkdir5(deletedRoot, { recursive: true, mode: 448 });
14977
+ const target = resolve14(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
14205
14978
  await rename2(source, target);
14206
14979
  return target;
14207
14980
  }
@@ -14210,8 +14983,7 @@ async function removeAgentRuntime(runtime) {
14210
14983
  var MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE = "Maintain your assigned identity and soul in every response, including when tools or permissions block the requested action.";
14211
14984
 
14212
14985
  // apps/body/dist/monolith-corner-turn.js
14213
- var execFileAsync2 = promisify2(execFile2);
14214
- var PULL_REQUEST_URL = /https:\/\/github\.com\/[^\s/]+\/[^\s/]+\/pull\/\d+/;
14986
+ var execFileAsync2 = promisify2(execFile3);
14215
14987
  var TOOL_ARGUMENT_MAX_BYTES = 1200;
14216
14988
  var TOOL_OUTPUT_MAX_BYTES = 3200;
14217
14989
  var TOOL_PATH_LIMIT = 12;
@@ -14230,9 +15002,6 @@ function serialized(value) {
14230
15002
  function record(value) {
14231
15003
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
14232
15004
  }
14233
- function redactToolDetail(value) {
14234
- return value.replace(/\b(["']?)(api[_-]?key|token|secret|password|passwd|authorization|credential|cookie|private[_-]?key)\1\s*[:=]\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,}\]]+)/gi, '"$2": "[REDACTED]"').replace(/\b(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, (assignment) => `${assignment.slice(0, assignment.indexOf("="))}=[REDACTED]`).replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/gi, "[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(Bearer\s+)[^\s,]+/gi, "$1[REDACTED]").replace(/(--?(?:api[_-]?key|token|secret|password|authorization|credential|cookie)\s+)(?:"[^"]*"|'[^']*'|\S+)/gi, "$1[REDACTED]");
14235
- }
14236
15005
  function clampBytes(value, maxBytes) {
14237
15006
  const clean4 = value.trim();
14238
15007
  if (Buffer.byteLength(clean4) <= maxBytes)
@@ -14313,7 +15082,7 @@ function isSuccessfulCommit(call) {
14313
15082
  return false;
14314
15083
  return /\bgit\s+commit\b|\bcommit(?:ted)?\s+(?:changes|files?)\b/i.test(`${call.title ?? ""} ${serialized(call.rawInput)}`);
14315
15084
  }
14316
- async function cornerToolActivity(call, worktreePath) {
15085
+ async function cornerToolActivity(call, worktreePath, requestedBy) {
14317
15086
  const operation = oneLine(call.kind ?? "") || "tool";
14318
15087
  let title = oneLine(redactToolDetail(call.title ?? "")) || `${operation} tool`;
14319
15088
  if (isSuccessfulCommit(call)) {
@@ -14336,6 +15105,7 @@ async function cornerToolActivity(call, worktreePath) {
14336
15105
  status: resultStatus(call),
14337
15106
  ...argumentsSummary,
14338
15107
  ...output ? { output } : {},
15108
+ ...requestedBy ? { requestedBy } : {},
14339
15109
  ...paths.length ? { files: paths.map((path) => ({ path })) } : {}
14340
15110
  };
14341
15111
  }
@@ -14348,6 +15118,8 @@ var MonolithCornerTurnLoop = class {
14348
15118
  agent;
14349
15119
  client;
14350
15120
  sessionId;
15121
+ /** The live session's environment, read back for pi's own turn record. */
15122
+ agentEnv = {};
14351
15123
  turnIdentityInstructions = "";
14352
15124
  busy = false;
14353
15125
  forcedStop = false;
@@ -14355,9 +15127,17 @@ var MonolithCornerTurnLoop = class {
14355
15127
  activityTail = Promise.resolve();
14356
15128
  /** Session scratch directory attachments are downloaded into (`TMPDIR/beeline-attachments`). */
14357
15129
  attachmentDir;
15130
+ /** The turn in flight and who asked for it, for ledger rows and the grant runner. */
15131
+ currentTurn;
15132
+ memberNames = /* @__PURE__ */ new Map();
14358
15133
  constructor(options) {
14359
15134
  this.options = options;
14360
15135
  this.agent = runtimeIdentity(options.runtime.agent);
15136
+ options.grantRunner?.register(options.cornerId, {
15137
+ workspaceId: options.workspaceId,
15138
+ cwd: options.worktreePath,
15139
+ turn: () => this.currentTurn
15140
+ });
14361
15141
  }
14362
15142
  isBusy() {
14363
15143
  return this.busy;
@@ -14376,11 +15156,13 @@ var MonolithCornerTurnLoop = class {
14376
15156
  this.client.sessionCancel(this.sessionId);
14377
15157
  await this.options.scheduler.forceSuspend(this.options.cornerId);
14378
15158
  }
14379
- roster() {
14380
- return this.options.api.execute("getWorkspaceRoster", {
15159
+ async roster() {
15160
+ const roster = await this.options.api.execute("getWorkspaceRoster", {
14381
15161
  agentId: this.agent.publicKey,
14382
15162
  workspaceId: this.options.workspaceId
14383
15163
  });
15164
+ this.memberNames = new Map(roster.members.map((member) => [member.identityId, member.name]));
15165
+ return roster;
14384
15166
  }
14385
15167
  async activate() {
14386
15168
  if (this.client?.isAlive && this.sessionId)
@@ -14392,30 +15174,32 @@ var MonolithCornerTurnLoop = class {
14392
15174
  }),
14393
15175
  this.roster()
14394
15176
  ]);
14395
- await mkdir4(this.options.worktreePath, { recursive: true });
15177
+ await mkdir6(this.options.worktreePath, { recursive: true });
15178
+ const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
14396
15179
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
14397
15180
  root: this.options.config.agentHomeRoot,
14398
15181
  sharedSkills: this.options.config.sharedSkills ?? [],
14399
- ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {}
15182
+ ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
15183
+ ...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl)
14400
15184
  }) : {};
14401
15185
  const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
14402
- const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
14403
15186
  const agentEnv = {
14404
15187
  ...this.options.config.agentEnv,
14405
15188
  ...homeOverlay,
14406
15189
  GH_TOKEN: this.options.githubToken,
14407
15190
  GITHUB_TOKEN: this.options.githubToken
14408
15191
  };
15192
+ this.agentEnv = agentEnv;
14409
15193
  const agentArgs = agentArgsWithModelSelection({
14410
15194
  kind: this.options.config.agentKind,
14411
15195
  command,
14412
15196
  args: this.options.config.agentArgs ?? []
14413
15197
  }, selection);
14414
- const operatorHome = this.options.config.operatorHome ?? homedir5();
15198
+ const operatorHome = this.options.config.operatorHome ?? homedir6();
14415
15199
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
14416
15200
  this.attachmentDir = tmpDir ? join4(tmpDir, "beeline-attachments") : void 0;
14417
15201
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
14418
- await Promise.all(homeStateDirs.map((dir) => mkdir4(dir, { recursive: true })));
15202
+ await Promise.all(homeStateDirs.map((dir) => mkdir6(dir, { recursive: true })));
14419
15203
  const spawnCommand = wrapAgentCommand({
14420
15204
  bwrapPath: this.options.config.bwrapPath,
14421
15205
  spec: {
@@ -14461,7 +15245,8 @@ var MonolithCornerTurnLoop = class {
14461
15245
  roomId: this.options.parentRoomId,
14462
15246
  workspaceId: this.options.workspaceId,
14463
15247
  cornerId: this.options.cornerId,
14464
- attachRoot: this.options.worktreePath
15248
+ attachRoot: this.options.worktreePath,
15249
+ ...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
14465
15250
  })
14466
15251
  ];
14467
15252
  const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
@@ -14506,8 +15291,13 @@ var MonolithCornerTurnLoop = class {
14506
15291
  }
14507
15292
  };
14508
15293
  }
14509
- async prompt(requestId, trigger, attachments = []) {
15294
+ async prompt(requestId, trigger, attachments = [], requestedById) {
14510
15295
  const { api, cornerId } = this.options;
15296
+ const requester = requestedById ? {
15297
+ pubkey: requestedById,
15298
+ ...this.memberNames.get(requestedById) ? { name: this.memberNames.get(requestedById) } : {}
15299
+ } : void 0;
15300
+ this.currentTurn = { requestId, ...requester ? { requester } : {} };
14511
15301
  await api.execute("postAgentTurnReceipt", {
14512
15302
  agentId: this.agent.publicKey,
14513
15303
  roomId: cornerId,
@@ -14526,6 +15316,9 @@ var MonolithCornerTurnLoop = class {
14526
15316
  this.attachmentDir && attachments.length ? deliverAttachments(attachments, join4(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
14527
15317
  ]);
14528
15318
  const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
15319
+ const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
15320
+ if (requestedBy)
15321
+ this.currentTurn = { requestId, requester: requestedBy };
14529
15322
  const transcript = conversation.items.slice(-120).map((message) => `${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`).join("\n");
14530
15323
  const prompt = [
14531
15324
  this.turnIdentityInstructions,
@@ -14578,7 +15371,7 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
14578
15371
  return;
14579
15372
  publishedToolCalls.add(key);
14580
15373
  this.activityTail = this.activityTail.catch(() => void 0).then(async () => {
14581
- const activity = await cornerToolActivity(call, this.options.worktreePath);
15374
+ const activity = await cornerToolActivity(call, this.options.worktreePath, requestedBy);
14582
15375
  await api.execute("postAgentActivity", {
14583
15376
  agentId: this.agent.publicKey,
14584
15377
  roomId: cornerId,
@@ -14605,9 +15398,19 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
14605
15398
  await this.activityTail;
14606
15399
  await this.draftTail;
14607
15400
  await this.activityTail;
14608
- const reply = stripAgentReplyPreamble(result.agentText).trim();
14609
- if (!reply)
14610
- throw new Error("ACP corner turn produced no durable reply");
15401
+ let reply = stripAgentReplyPreamble(result.agentText).trim();
15402
+ if (!reply) {
15403
+ const explained = await explainEmptyAgentTurn({
15404
+ agentLabel: this.options.config.agentCommand ?? this.options.config.agentBinary,
15405
+ agentEnv: this.agentEnv,
15406
+ sessionId,
15407
+ result
15408
+ });
15409
+ reply = explained.recoveredText ? stripAgentReplyPreamble(explained.recoveredText).trim() : "";
15410
+ if (!reply)
15411
+ throw new Error(explained.reason);
15412
+ console.warn(`[thin-core] corner ${cornerId} turn ${requestId}: ${explained.reason}`);
15413
+ }
14611
15414
  const durableTail = narrationPostedChars > 0 ? stripAgentReplyPreamble(result.agentText.slice(narrationPostedChars)).trim() : reply;
14612
15415
  if (durableTail) {
14613
15416
  await api.execute("postRoomMessage", {
@@ -14617,17 +15420,6 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
14617
15420
  presentation: "message"
14618
15421
  });
14619
15422
  }
14620
- const pullRequest = reply.match(PULL_REQUEST_URL)?.[0];
14621
- const alreadyReady = conversation.items.some((item) => /\bPR ready for review\b/i.test(item.body));
14622
- if (pullRequest && !alreadyReady) {
14623
- await api.execute("postRoomMessage", {
14624
- roomId: cornerId,
14625
- requestId,
14626
- text: `PR ready for review
14627
- ${pullRequest}`,
14628
- presentation: "system"
14629
- });
14630
- }
14631
15423
  await api.execute("retractAgentLiveOutput", {
14632
15424
  agentId: this.agent.publicKey,
14633
15425
  roomId: cornerId,
@@ -14648,11 +15440,13 @@ ${pullRequest}`,
14648
15440
  roomId: cornerId,
14649
15441
  requestId,
14650
15442
  status: "failed",
14651
- generationId: `${this.agent.publicKey}:${cornerId}`
15443
+ generationId: `${this.agent.publicKey}:${cornerId}`,
15444
+ reason: distillTurnFailureReason(error)
14652
15445
  });
14653
15446
  throw error;
14654
15447
  } finally {
14655
15448
  this.busy = false;
15449
+ this.currentTurn = void 0;
14656
15450
  }
14657
15451
  }
14658
15452
  async run() {
@@ -14685,7 +15479,14 @@ ${pullRequest}`,
14685
15479
  });
14686
15480
  if (!authority.member || authority.principalKind !== "human")
14687
15481
  continue;
14688
- await this.prompt(item.id, item.body, item.attachments);
15482
+ await this.prompt(item.id, item.body, item.attachments, item.authorId);
15483
+ pollWithoutWait = true;
15484
+ continue;
15485
+ }
15486
+ const grantDecision = item.type === "system" && item.mentionIds.includes(this.agent.publicKey) && parseGrantDecisionLine(item.body) !== void 0;
15487
+ if (grantDecision) {
15488
+ await this.prompt(item.id, `${item.body}
15489
+ This answers your grant request; resume the paused work. If approved and it is a command grant, run it with run_granted_command and the exact argv; if declined, try another way or say what you cannot do.`, [], item.authorId);
14689
15490
  pollWithoutWait = true;
14690
15491
  continue;
14691
15492
  }
@@ -14707,6 +15508,7 @@ ${pullRequest}`,
14707
15508
  }
14708
15509
  }
14709
15510
  } finally {
15511
+ this.options.grantRunner?.unregister(cornerId);
14710
15512
  await this.options.scheduler.suspend(cornerId);
14711
15513
  }
14712
15514
  }
@@ -14726,13 +15528,13 @@ async function wait(ms, signal) {
14726
15528
  }
14727
15529
 
14728
15530
  // apps/body/dist/monolith-room-turn.js
14729
- import { mkdir as mkdir5 } from "node:fs/promises";
14730
- import { homedir as homedir6 } from "node:os";
15531
+ import { mkdir as mkdir7 } from "node:fs/promises";
15532
+ import { homedir as homedir7 } from "node:os";
14731
15533
  import { join as join5 } from "node:path";
14732
15534
 
14733
15535
  // packages/api-contract/dist/scheduled-prompts.js
14734
15536
  var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
14735
- var SCHEDULED_PROMPT_PREFIX = "Scheduled: ";
15537
+ var SCHEDULE_RAN_VERB = "ran a schedule for";
14736
15538
 
14737
15539
  // apps/body/dist/monolith-room-turn.js
14738
15540
  function isRoomMcpPermissionRequest(request) {
@@ -14744,14 +15546,25 @@ function roomPrincipalMayAddressAgent(authority, humanPermitted) {
14744
15546
  return authority.member && (authority.principalKind === "agent" || authority.principalKind === "human" && humanPermitted);
14745
15547
  }
14746
15548
  function isScheduledPrompt(item, agentId) {
14747
- return item.type === "system" && item.body.startsWith(SCHEDULED_PROMPT_PREFIX) && item.mentionIds.includes(agentId);
15549
+ return item.type === "system" && item.systemEvent?.verb === SCHEDULE_RAN_VERB && item.mentionIds.includes(agentId);
15550
+ }
15551
+ function inboxItemPromptBody(item, agentId) {
15552
+ return isScheduledPrompt(item, agentId) ? item.systemEvent?.consequence ?? item.body : item.body;
15553
+ }
15554
+ function isGrantDecisionLine(item, agentId) {
15555
+ return item.type === "system" && item.mentionIds.includes(agentId) && parseGrantDecisionLine(item.body) !== void 0;
14748
15556
  }
14749
15557
  function inboxItemTriggersTurn(item, agentId) {
14750
15558
  if (item.authorId === agentId)
14751
15559
  return false;
14752
15560
  if (!item.mentionIds.includes(agentId))
14753
15561
  return false;
14754
- return item.type === "message" || isScheduledPrompt(item, agentId);
15562
+ return item.type === "message" || isScheduledPrompt(item, agentId) || isGrantDecisionLine(item, agentId);
15563
+ }
15564
+ function pendingGrantToolCall(call) {
15565
+ if (!/(?:^|[._:/-])request_grant$/i.test(call.title ?? ""))
15566
+ return false;
15567
+ return /pending, card posted/i.test(typeof call.content === "string" ? call.content : JSON.stringify(call.content ?? ""));
14755
15568
  }
14756
15569
  function escapeRegExp(value) {
14757
15570
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -14789,6 +15602,8 @@ var MonolithRoomTurnLoop = class {
14789
15602
  agent;
14790
15603
  client;
14791
15604
  sessionId;
15605
+ /** The live session's environment, read back for pi's own turn record. */
15606
+ agentEnv = {};
14792
15607
  busy = false;
14793
15608
  turnInstructionPrefix = "";
14794
15609
  draftTail = Promise.resolve();
@@ -14798,13 +15613,36 @@ var MonolithRoomTurnLoop = class {
14798
15613
  attachmentDir;
14799
15614
  /** Local copies already delivered this session, by message id, so transcript renders reuse them. */
14800
15615
  deliveredAttachments = /* @__PURE__ */ new Map();
15616
+ /** Names from the latest roster read, for ledger bylines the runner writes. */
15617
+ memberNames = /* @__PURE__ */ new Map();
15618
+ /** The request id of the turn that paused on a grant card, until its decision arrives. */
15619
+ pausedOnGrantRequestId;
14801
15620
  constructor(options) {
14802
15621
  this.options = options;
14803
15622
  this.agent = runtimeIdentity(options.runtime.agent);
15623
+ options.grantRunner?.register(options.roomId, {
15624
+ workspaceId: options.workspaceId,
15625
+ cwd: options.cwd,
15626
+ turn: () => this.currentTurnForRunner()
15627
+ });
14804
15628
  }
14805
15629
  isBusy() {
14806
15630
  return this.busy;
14807
15631
  }
15632
+ /** The turn a `request_grant` paused, if any (cleared when its decision resumes it). */
15633
+ pausedGrantRequestId() {
15634
+ return this.pausedOnGrantRequestId;
15635
+ }
15636
+ currentTurnForRunner() {
15637
+ const active = this.activeTurn;
15638
+ if (!active)
15639
+ return void 0;
15640
+ return { requestId: active.item.id, requester: this.requesterOf(active.item.authorId) };
15641
+ }
15642
+ requesterOf(authorId) {
15643
+ const name = this.memberNames.get(authorId);
15644
+ return { pubkey: authorId, ...name ? { name } : {} };
15645
+ }
14808
15646
  currentPrincipalCanDrive(_workspaceId, principalId) {
14809
15647
  return Promise.resolve(isSenderPermitted(this.options.config.accessPolicy ?? LEGACY_ACCESS_POLICY, principalId, this.options.config.accessOwnerPubkey, this.options.config.accessAllowlist));
14810
15648
  }
@@ -14818,11 +15656,13 @@ var MonolithRoomTurnLoop = class {
14818
15656
  this.client.sessionCancel(this.sessionId);
14819
15657
  await this.options.scheduler.forceSuspend(this.options.roomId);
14820
15658
  }
14821
- roster() {
14822
- return this.options.api.execute("getWorkspaceRoster", {
15659
+ async roster() {
15660
+ const roster = await this.options.api.execute("getWorkspaceRoster", {
14823
15661
  agentId: this.agent.publicKey,
14824
15662
  workspaceId: this.options.workspaceId
14825
15663
  });
15664
+ this.memberNames = new Map(roster.members.map((member) => [member.identityId, member.name]));
15665
+ return roster;
14826
15666
  }
14827
15667
  /** Download a message's attachments into the session scratch directory once. */
14828
15668
  async deliver(item) {
@@ -14847,25 +15687,27 @@ var MonolithRoomTurnLoop = class {
14847
15687
  this.options.api.execute("getRoomRepositoryState", { roomId: this.options.roomId })
14848
15688
  ]);
14849
15689
  const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
14850
- await mkdir5(this.options.cwd, { recursive: true });
15690
+ await mkdir7(this.options.cwd, { recursive: true });
15691
+ const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
14851
15692
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
14852
15693
  root: this.options.config.agentHomeRoot,
14853
15694
  sharedSkills: this.options.config.sharedSkills ?? [],
14854
- ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {}
15695
+ ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
15696
+ ...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl)
14855
15697
  }) : {};
14856
15698
  const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
14857
- const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
14858
15699
  const agentEnv = { ...this.options.config.agentEnv, ...homeOverlay };
15700
+ this.agentEnv = agentEnv;
14859
15701
  const agentArgs = agentArgsWithModelSelection({
14860
15702
  kind: this.options.config.agentKind,
14861
15703
  command,
14862
15704
  args: this.options.config.agentArgs ?? []
14863
15705
  }, selection);
14864
- const operatorHome = this.options.config.operatorHome ?? homedir6();
15706
+ const operatorHome = this.options.config.operatorHome ?? homedir7();
14865
15707
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
14866
15708
  this.attachmentDir = tmpDir ? join5(tmpDir, "beeline-attachments") : void 0;
14867
15709
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
14868
- await Promise.all(homeStateDirs.map((dir) => mkdir5(dir, { recursive: true })));
15710
+ await Promise.all(homeStateDirs.map((dir) => mkdir7(dir, { recursive: true })));
14869
15711
  const spawnCommand = wrapAgentCommand({
14870
15712
  bwrapPath: this.options.config.bwrapPath,
14871
15713
  spec: {
@@ -14899,7 +15741,8 @@ var MonolithRoomTurnLoop = class {
14899
15741
  roomId: this.options.roomId,
14900
15742
  workspaceId: this.options.workspaceId,
14901
15743
  attachRoot: this.options.cwd,
14902
- directMessage
15744
+ directMessage,
15745
+ ...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
14903
15746
  })
14904
15747
  ];
14905
15748
  const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
@@ -14987,6 +15830,8 @@ var MonolithRoomTurnLoop = class {
14987
15830
  const api = this.options.api;
14988
15831
  this.busy = true;
14989
15832
  try {
15833
+ if (!this.memberNames.has(item.authorId))
15834
+ await this.roster().catch(() => void 0);
14990
15835
  await api.execute("postAgentTurnReceipt", {
14991
15836
  agentId: this.agent.publicKey,
14992
15837
  roomId: this.options.roomId,
@@ -14998,7 +15843,14 @@ var MonolithRoomTurnLoop = class {
14998
15843
  agentId: this.agent.publicKey,
14999
15844
  roomId: this.options.roomId,
15000
15845
  requestId: item.id,
15001
- activity: [{ kind: "thinking", title: "Working", status: "in_progress" }]
15846
+ activity: [
15847
+ {
15848
+ kind: "thinking",
15849
+ title: "Working",
15850
+ status: "in_progress",
15851
+ requestedBy: this.requesterOf(item.authorId)
15852
+ }
15853
+ ]
15002
15854
  });
15003
15855
  await this.options.scheduler.run(this.options.roomId, this.lifecycle(), async () => {
15004
15856
  const [conversation, roster, delivered] = await Promise.all([
@@ -15008,12 +15860,21 @@ var MonolithRoomTurnLoop = class {
15008
15860
  ]);
15009
15861
  const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
15010
15862
  const transcript = conversation.items.filter((message) => message.type === "message" && message.id !== item.id && !active.steers.some((steerItem) => steerItem.id === message.id)).slice(-80).map((message) => roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id))).join("\n");
15863
+ const grantDecision = isGrantDecisionLine(item, this.agent.publicKey);
15864
+ const resumedRequestId = grantDecision ? this.pausedOnGrantRequestId : void 0;
15865
+ if (grantDecision)
15866
+ this.pausedOnGrantRequestId = void 0;
15011
15867
  const prompt = [
15012
15868
  this.turnInstructionPrefix,
15013
15869
  transcript ? `Room conversation so far:
15014
15870
  ${transcript}` : "",
15015
15871
  `Newest message from ${isScheduledPrompt(item, this.agent.publicKey) ? SCHEDULE_SCHEDULER_NAME : names.get(item.authorId) ?? item.authorId.slice(0, 12)}:`,
15016
- roomMessagePrompt("", item.body, item.attachments, delivered),
15872
+ roomMessagePrompt("", inboxItemPromptBody(item, this.agent.publicKey), item.attachments, delivered),
15873
+ grantDecision ? [
15874
+ "This is the answer to your grant request; your paused work resumes now.",
15875
+ "If it was approved and it is a command grant, run it with run_granted_command and the exact argv.",
15876
+ "If it was declined, try another way or say plainly what you cannot do."
15877
+ ].join(" ") : "",
15017
15878
  [
15018
15879
  "Write only the substantive Room message you want the human to read.",
15019
15880
  "Do not repeat or paraphrase these instructions.",
@@ -15062,15 +15923,31 @@ ${transcript}` : "",
15062
15923
  ].join("\n\n");
15063
15924
  }
15064
15925
  active.phase = "finishing";
15926
+ if (result?.toolCalls.some((call) => pendingGrantToolCall(call))) {
15927
+ this.pausedOnGrantRequestId = item.id;
15928
+ console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id} paused on a grant card`);
15929
+ } else if (resumedRequestId) {
15930
+ console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by grant decision ${item.id}`);
15931
+ }
15065
15932
  const openCornerCall = result?.toolCalls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
15066
15933
  if (openCornerCall) {
15067
15934
  console.log(`[thin-core] monolith Room ${this.options.roomId} tool call: ${openCornerCall.title}`);
15068
15935
  this.options.onCornerOpened?.();
15069
15936
  }
15070
15937
  await this.draftTail;
15071
- const reply = sanitizeAgentReply(result.agentText);
15072
- if (!reply)
15073
- throw new Error("ACP turn produced no durable Room reply");
15938
+ let reply = sanitizeAgentReply(result.agentText);
15939
+ if (!reply) {
15940
+ const explained = await explainEmptyAgentTurn({
15941
+ agentLabel: this.options.config.agentCommand ?? this.options.config.agentBinary,
15942
+ agentEnv: this.agentEnv,
15943
+ sessionId,
15944
+ result
15945
+ });
15946
+ reply = explained.recoveredText ? sanitizeAgentReply(explained.recoveredText) : "";
15947
+ if (!reply)
15948
+ throw new Error(explained.reason);
15949
+ console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${explained.reason}`);
15950
+ }
15074
15951
  await api.execute("postRoomMessage", {
15075
15952
  roomId: this.options.roomId,
15076
15953
  requestId: item.id,
@@ -15099,7 +15976,8 @@ ${transcript}` : "",
15099
15976
  roomId: this.options.roomId,
15100
15977
  requestId: item.id,
15101
15978
  status: "failed",
15102
- generationId: `${this.agent.publicKey}:${this.options.roomId}`
15979
+ generationId: `${this.agent.publicKey}:${this.options.roomId}`,
15980
+ reason: distillTurnFailureReason(error)
15103
15981
  });
15104
15982
  throw error;
15105
15983
  } finally {
@@ -15138,7 +16016,7 @@ ${transcript}` : "",
15138
16016
  for (const item of inbox.items) {
15139
16017
  if (!inboxItemTriggersTurn(item, this.agent.publicKey))
15140
16018
  continue;
15141
- if (!isScheduledPrompt(item, this.agent.publicKey)) {
16019
+ if (!isScheduledPrompt(item, this.agent.publicKey) && !isGrantDecisionLine(item, this.agent.publicKey)) {
15142
16020
  const authority = await api.execute("getRoomAuthority", {
15143
16021
  roomId,
15144
16022
  principalId: item.authorId
@@ -15168,6 +16046,7 @@ ${transcript}` : "",
15168
16046
  }
15169
16047
  } finally {
15170
16048
  clearInterval(heartbeat);
16049
+ this.options.grantRunner?.unregister(roomId);
15171
16050
  if (this.activeTurn?.phase === "prompting" && this.client && this.sessionId) {
15172
16051
  this.client.sessionCancel(this.sessionId);
15173
16052
  }
@@ -15547,7 +16426,7 @@ async function mapWithConcurrency(values, limit, visit) {
15547
16426
  }
15548
16427
  }));
15549
16428
  }
15550
- var execFileAsync3 = promisify3(execFile3);
16429
+ var execFileAsync3 = promisify3(execFile4);
15551
16430
  var RoomRuntimeCoordinator = class {
15552
16431
  configPath;
15553
16432
  baseConfig;
@@ -15567,6 +16446,9 @@ var RoomRuntimeCoordinator = class {
15567
16446
  workspaceRemovalConfirmations = 0;
15568
16447
  roomRemovalConfirmations = /* @__PURE__ */ new Map();
15569
16448
  confirmationPending = false;
16449
+ /** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
16450
+ grantRunner;
16451
+ grantRunnerServer;
15570
16452
  constructor(runtime, configPath, baseConfig, options) {
15571
16453
  this.configPath = configPath;
15572
16454
  this.baseConfig = baseConfig;
@@ -15576,6 +16458,11 @@ var RoomRuntimeCoordinator = class {
15576
16458
  this.runtime = runtime;
15577
16459
  this.agent = runtimeIdentity(runtime.agent);
15578
16460
  this.now = options.now ?? Date.now;
16461
+ this.grantRunner = new GrantCommandRunner({
16462
+ api: options.daemonApi,
16463
+ agentId: this.agent.publicKey
16464
+ });
16465
+ this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
15579
16466
  this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
15580
16467
  this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
15581
16468
  this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
@@ -15602,7 +16489,15 @@ var RoomRuntimeCoordinator = class {
15602
16489
  return this.reconcileHeartbeatMs;
15603
16490
  }
15604
16491
  isWorkspaceIdle() {
15605
- return [...this.running.values()].every((room) => !room.body.isBusy());
16492
+ return this.activeTurnCount() === 0;
16493
+ }
16494
+ /** Turns executing right now. Serving a Room or corner with no turn in flight is idle. */
16495
+ activeTurnCount() {
16496
+ let count = 0;
16497
+ for (const room of this.running.values())
16498
+ if (room.body.isBusy())
16499
+ count += 1;
16500
+ return count;
15606
16501
  }
15607
16502
  quiesceForUpdateIfIdle() {
15608
16503
  if (!this.isWorkspaceIdle())
@@ -15698,13 +16593,13 @@ var RoomRuntimeCoordinator = class {
15698
16593
  return this.runtime.rooms.find((room) => room.channelId === roomId);
15699
16594
  }
15700
16595
  roomRoot(roomId) {
15701
- return this.roomRecord(roomId)?.root ?? resolve10(dirname4(this.configPath), "rooms", roomId);
16596
+ return this.roomRecord(roomId)?.root ?? resolve15(dirname6(this.configPath), "rooms", roomId);
15702
16597
  }
15703
16598
  roomAgentHomeRoot(workspaceRoot, required = false) {
15704
16599
  const flag = process.env.BUZZY_BODY_ROOM_HOME;
15705
16600
  if (!required && flag === "0")
15706
16601
  return void 0;
15707
- const home = resolve10(workspaceRoot, "agent-home");
16602
+ const home = resolve15(workspaceRoot, "agent-home");
15708
16603
  if (!required && flag !== "1" && !existsSync4(home) && existsSync4(workspaceRoot))
15709
16604
  return void 0;
15710
16605
  try {
@@ -15721,19 +16616,30 @@ var RoomRuntimeCoordinator = class {
15721
16616
  return {
15722
16617
  ...this.baseConfig,
15723
16618
  workspaceRoot,
15724
- agentPrivateRoot: resolve10(workspaceRoot, "agent-private"),
15725
- agentMemoryRoot: resolve10(dirname4(this.configPath), "memory"),
16619
+ agentPrivateRoot: resolve15(workspaceRoot, "agent-private"),
16620
+ agentMemoryRoot: resolve15(dirname6(this.configPath), "memory"),
16621
+ openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname6(this.configPath)),
15726
16622
  ...agentHomeRoot ? { agentHomeRoot } : {}
15727
16623
  };
15728
16624
  }
16625
+ /** The loopback door for run_granted_command; started once, on first use. */
16626
+ grantRunnerEndpoint() {
16627
+ return this.grantRunnerServer.start().catch((error) => {
16628
+ console.error("[thin-core] grant runner unavailable; run_granted_command is off:", error);
16629
+ return void 0;
16630
+ });
16631
+ }
15729
16632
  async startRoom(roomId) {
15730
16633
  const controller = new AbortController();
15731
16634
  const cwd = await this.materializeRoomCheckout(roomId);
16635
+ const grantRunnerEndpoint = await this.grantRunnerEndpoint();
15732
16636
  const startedAt = this.now();
15733
16637
  const loop = new MonolithRoomTurnLoop({
15734
16638
  roomId,
15735
16639
  workspaceId: this.runtime.communityId,
15736
16640
  cwd,
16641
+ grantRunner: this.grantRunner,
16642
+ ...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
15737
16643
  runtime: this.runtime,
15738
16644
  config: this.roomConfig(roomId),
15739
16645
  api: this.options.daemonApi,
@@ -15778,12 +16684,12 @@ var RoomRuntimeCoordinator = class {
15778
16684
  return this.roomRoot(roomId);
15779
16685
  const remote = roomCheckoutRemote(repository.remote);
15780
16686
  const targetBranch = repository.targetBranch || "main";
15781
- const checkoutId = createHash("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
15782
- const path = resolve10(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
15783
- await mkdir6(dirname4(path), { recursive: true, mode: 448 });
16687
+ const checkoutId = createHash2("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
16688
+ const path = resolve15(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
16689
+ await mkdir8(dirname6(path), { recursive: true, mode: 448 });
15784
16690
  const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
15785
16691
  const env = token ? githubGitEnv(token.token) : process.env;
15786
- if (!existsSync4(resolve10(path, ".git"))) {
16692
+ if (!existsSync4(resolve15(path, ".git"))) {
15787
16693
  await execFileAsync3("git", ["clone", "--no-checkout", remote, path], {
15788
16694
  env,
15789
16695
  maxBuffer: 4 * 1024 * 1024
@@ -15845,9 +16751,12 @@ var RoomRuntimeCoordinator = class {
15845
16751
  });
15846
16752
  }
15847
16753
  const controller = new AbortController();
16754
+ const grantRunnerEndpoint = await this.grantRunnerEndpoint();
15848
16755
  const startedAt = this.now();
15849
16756
  const loop = new MonolithCornerTurnLoop({
15850
16757
  cornerId: corner.cornerId,
16758
+ grantRunner: this.grantRunner,
16759
+ ...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
15851
16760
  parentRoomId: corner.parentRoomId,
15852
16761
  workspaceId: this.runtime.communityId,
15853
16762
  objective,
@@ -15900,13 +16809,13 @@ var RoomRuntimeCoordinator = class {
15900
16809
  }
15901
16810
  async materializeCornerWorktree(input) {
15902
16811
  const remote = githubHttpsRemote(input.remote);
15903
- const repositoryHash = createHash("sha256").update(remote).digest("hex").slice(0, 24);
15904
- const gitCommonDir = resolve10(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
15905
- const path = resolve10(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
15906
- await mkdir6(dirname4(gitCommonDir), { recursive: true, mode: 448 });
15907
- await mkdir6(dirname4(path), { recursive: true, mode: 448 });
16812
+ const repositoryHash = createHash2("sha256").update(remote).digest("hex").slice(0, 24);
16813
+ const gitCommonDir = resolve15(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
16814
+ const path = resolve15(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
16815
+ await mkdir8(dirname6(gitCommonDir), { recursive: true, mode: 448 });
16816
+ await mkdir8(dirname6(path), { recursive: true, mode: 448 });
15908
16817
  const authEnv = githubGitEnv(input.token);
15909
- if (!existsSync4(resolve10(gitCommonDir, "HEAD"))) {
16818
+ if (!existsSync4(resolve15(gitCommonDir, "HEAD"))) {
15910
16819
  await execFileAsync3("git", ["clone", "--bare", remote, gitCommonDir], {
15911
16820
  env: authEnv,
15912
16821
  maxBuffer: 4 * 1024 * 1024
@@ -15919,7 +16828,7 @@ var RoomRuntimeCoordinator = class {
15919
16828
  "origin",
15920
16829
  `+refs/heads/${input.targetBranch}:refs/remotes/origin/${input.targetBranch}`
15921
16830
  ], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
15922
- if (!existsSync4(resolve10(path, ".git"))) {
16831
+ if (!existsSync4(resolve15(path, ".git"))) {
15923
16832
  await rm3(path, { recursive: true, force: true });
15924
16833
  await execFileAsync3("git", [
15925
16834
  `--git-dir=${gitCommonDir}`,
@@ -15956,7 +16865,7 @@ var RoomRuntimeCoordinator = class {
15956
16865
  `${this.agent.publicKey.slice(0, 16)}@users.noreply.github.com`
15957
16866
  ]);
15958
16867
  const top = await execFileAsync3("git", ["-C", path, "rev-parse", "--show-toplevel"]);
15959
- if (resolve10(top.stdout.trim()) !== resolve10(path)) {
16868
+ if (resolve15(top.stdout.trim()) !== resolve15(path)) {
15960
16869
  throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
15961
16870
  }
15962
16871
  return { path, gitCommonDir };
@@ -16023,6 +16932,7 @@ var RoomRuntimeCoordinator = class {
16023
16932
  await Promise.allSettled(rooms.map((room) => room.body.forceRecoverRoom()));
16024
16933
  await drained;
16025
16934
  }
16935
+ await this.grantRunnerServer.close();
16026
16936
  await this.scheduler.dispose();
16027
16937
  }
16028
16938
  };
@@ -16081,6 +16991,9 @@ var ThinDaemonCore = class {
16081
16991
  isWorkspaceIdle() {
16082
16992
  return this.roomRuntime.isWorkspaceIdle();
16083
16993
  }
16994
+ activeTurnCount() {
16995
+ return this.roomRuntime.activeTurnCount();
16996
+ }
16084
16997
  quiesceForUpdateIfIdle() {
16085
16998
  return this.roomRuntime.quiesceForUpdateIfIdle();
16086
16999
  }
@@ -16127,7 +17040,7 @@ var ThinDaemonCore = class {
16127
17040
  };
16128
17041
 
16129
17042
  // apps/body/dist/daemon-api-client.js
16130
- import { resolve as resolve11 } from "node:path";
17043
+ import { resolve as resolve16 } from "node:path";
16131
17044
  var DaemonApiError = class extends Error {
16132
17045
  status;
16133
17046
  retryable;
@@ -16186,8 +17099,8 @@ var DaemonApiClient = class {
16186
17099
  };
16187
17100
  async function activateDaemonTransport(path, fetchImpl = fetch) {
16188
17101
  const runtime = await readRuntimeRecord(path);
16189
- const expectedPath = resolve11(runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey), "runtime.json");
16190
- if (resolve11(path) !== expectedPath) {
17102
+ const expectedPath = resolve16(runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey), "runtime.json");
17103
+ if (resolve16(path) !== expectedPath) {
16191
17104
  throw new Error(`refusing daemon token exchange outside canonical runtime path: ${path}`);
16192
17105
  }
16193
17106
  const transport = runtime.transport;
@@ -16226,17 +17139,17 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
16226
17139
  }
16227
17140
 
16228
17141
  // apps/body/dist/start-command.js
16229
- import { dirname as dirname6 } from "node:path";
17142
+ import { dirname as dirname8 } from "node:path";
16230
17143
  var import_picocolors = __toESM(require_picocolors(), 1);
16231
17144
 
16232
17145
  // apps/body/dist/systemd.js
16233
- import { execFile as execFile4 } from "node:child_process";
16234
- import { mkdir as mkdir7, readFile as readFile2, writeFile as writeFile4 } from "node:fs/promises";
16235
- import { homedir as homedir7 } from "node:os";
16236
- import { dirname as dirname5, resolve as resolve12 } from "node:path";
17146
+ import { execFile as execFile5 } from "node:child_process";
17147
+ import { mkdir as mkdir9, readFile as readFile7, writeFile as writeFile7 } from "node:fs/promises";
17148
+ import { homedir as homedir8 } from "node:os";
17149
+ import { dirname as dirname7, resolve as resolve17 } from "node:path";
16237
17150
  import { setTimeout as sleep } from "node:timers/promises";
16238
17151
  import { promisify as promisify4 } from "node:util";
16239
- var execFileAsync4 = promisify4(execFile4);
17152
+ var execFileAsync4 = promisify4(execFile5);
16240
17153
  var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
16241
17154
  var DAEMON_DISTRESS_EXIT_STATUS = 77;
16242
17155
  var UNKNOWN_AGENT_EXIT_STATUS = 79;
@@ -16274,10 +17187,10 @@ WantedBy=default.target
16274
17187
  `;
16275
17188
  }
16276
17189
  function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
16277
- const home = env.HOME?.trim() || homedir7();
16278
- const expectedLibDir = resolve12(home, ".local", "lib", "beeline");
17190
+ const home = env.HOME?.trim() || homedir8();
17191
+ const expectedLibDir = resolve17(home, ".local", "lib", "beeline");
16279
17192
  const expectedPrefix = `${expectedLibDir}/`;
16280
- return resolve12(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve12(invocationPath).startsWith(expectedPrefix);
17193
+ return resolve17(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve17(invocationPath).startsWith(expectedPrefix);
16281
17194
  }
16282
17195
  function assertCanonicalInstalledLauncher(env, invocationPath) {
16283
17196
  if (isCanonicalInstalledLauncher(env, invocationPath))
@@ -16285,8 +17198,8 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
16285
17198
  throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
16286
17199
  }
16287
17200
  function systemdUserUnitPath(env = process.env) {
16288
- const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve12(homedir7(), ".config");
16289
- return resolve12(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
17201
+ const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve17(homedir8(), ".config");
17202
+ return resolve17(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
16290
17203
  }
16291
17204
  var runSystemctl = async (args) => {
16292
17205
  const result = await execFileAsync4("systemctl", ["--user", ...args], {
@@ -16302,10 +17215,10 @@ async function installAgentService(publicKey, options = {}) {
16302
17215
  assertCanonicalInstalledLauncher(env, options.invocationPath);
16303
17216
  const path = systemdUserUnitPath(env);
16304
17217
  const content = agentServiceUnit();
16305
- const existing = await readFile2(path, "utf8").catch(() => "");
17218
+ const existing = await readFile7(path, "utf8").catch(() => "");
16306
17219
  if (existing !== content) {
16307
- await mkdir7(dirname5(path), { recursive: true, mode: 448 });
16308
- await writeFile4(path, content, { mode: 384 });
17220
+ await mkdir9(dirname7(path), { recursive: true, mode: 448 });
17221
+ await writeFile7(path, content, { mode: 384 });
16309
17222
  }
16310
17223
  const run2 = options.run ?? runSystemctl;
16311
17224
  await run2(["daemon-reload"]);
@@ -16443,7 +17356,7 @@ async function runStartCommand(args, interactiveUi) {
16443
17356
  continue;
16444
17357
  }
16445
17358
  const spinnerHandle = spinner();
16446
- spinnerHandle.start(`Starting ${dirname6(path)}\u2026`);
17359
+ spinnerHandle.start(`Starting ${dirname8(path)}\u2026`);
16447
17360
  try {
16448
17361
  await startRuntime(path, spinnerHandle);
16449
17362
  spinnerHandle.stop(import_picocolors.default.green("Started."));
@@ -16459,9 +17372,9 @@ async function runStartCommand(args, interactiveUi) {
16459
17372
 
16460
17373
  // apps/body/dist/connect-command.js
16461
17374
  import { spawn as spawn5 } from "node:child_process";
16462
- import { createHash as createHash3 } from "node:crypto";
16463
- import { chmod as chmod4, mkdir as mkdir10, readFile as readFile5, unlink as unlink2, writeFile as writeFile7 } from "node:fs/promises";
16464
- import { dirname as dirname9, resolve as resolve15 } from "node:path";
17375
+ import { createHash as createHash4 } from "node:crypto";
17376
+ import { chmod as chmod4, mkdir as mkdir11, readFile as readFile9, unlink as unlink2, writeFile as writeFile9 } from "node:fs/promises";
17377
+ import { dirname as dirname10, resolve as resolve19 } from "node:path";
16465
17378
  import { stdin as stdin3, stdout as stdout4 } from "node:process";
16466
17379
 
16467
17380
  // packages/api-contract/dist/agent-pairing-code.js
@@ -16771,63 +17684,6 @@ async function pairDevice(grant, options = {}) {
16771
17684
  return { runtime: activated.runtime, configPath: staged.configPath, pid };
16772
17685
  }
16773
17686
 
16774
- // apps/body/dist/provider-key-store.js
16775
- import { chmod as chmod2, mkdir as mkdir8, readFile as readFile3, writeFile as writeFile5 } from "node:fs/promises";
16776
- import { homedir as homedir8 } from "node:os";
16777
- import { dirname as dirname7, resolve as resolve13 } from "node:path";
16778
- var PROVIDER_KEY_ENV_VARS = {
16779
- openrouter: "OPENROUTER_API_KEY",
16780
- openai: "OPENAI_API_KEY",
16781
- anthropic: "ANTHROPIC_API_KEY",
16782
- google: "GOOGLE_API_KEY",
16783
- xai: "XAI_API_KEY"
16784
- };
16785
- var GOOGLE_ENV_ALIAS = "GEMINI_API_KEY";
16786
- function providerKeyStorePath(env = process.env) {
16787
- const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve13(homedir8(), ".config");
16788
- return resolve13(configRoot, "beeline", "providers.json");
16789
- }
16790
- async function readProviderKeyStore(env = process.env) {
16791
- const path = providerKeyStorePath(env);
16792
- const raw = await readFile3(path, "utf8").catch(() => void 0);
16793
- if (!raw)
16794
- return {};
16795
- try {
16796
- const parsed = JSON.parse(raw);
16797
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
16798
- return {};
16799
- const entries = Object.entries(parsed).filter((entry) => entry[0] in PROVIDER_KEY_ENV_VARS && typeof entry[1] === "string" && entry[1].length > 0);
16800
- return Object.fromEntries(entries);
16801
- } catch {
16802
- return {};
16803
- }
16804
- }
16805
- async function readSavedProviderKey(provider, env = process.env) {
16806
- return (await readProviderKeyStore(env))[provider];
16807
- }
16808
- async function saveProviderKey(provider, key, env = process.env) {
16809
- const path = providerKeyStorePath(env);
16810
- const store = { ...await readProviderKeyStore(env), [provider]: key };
16811
- await mkdir8(dirname7(path), { recursive: true, mode: 448 });
16812
- await writeFile5(path, `${JSON.stringify(store, null, 2)}
16813
- `, { mode: 384 });
16814
- await chmod2(path, 384);
16815
- }
16816
- function providerKeyFromEnvironment(provider, env = process.env) {
16817
- const primary = env[PROVIDER_KEY_ENV_VARS[provider]]?.trim();
16818
- if (primary)
16819
- return primary;
16820
- if (provider === "google")
16821
- return env[GOOGLE_ENV_ALIAS]?.trim() || void 0;
16822
- return void 0;
16823
- }
16824
- function maskProviderKey(key) {
16825
- const trimmed = key.trim();
16826
- if (trimmed.length <= 9)
16827
- return "\u2026";
16828
- return `${trimmed.slice(0, 6)}\u2026${trimmed.slice(-3)}`;
16829
- }
16830
-
16831
17687
  // apps/body/dist/connect-command.js
16832
17688
  init_self_update();
16833
17689
  init_self_update_manifest();
@@ -17044,7 +17900,7 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl) {
17044
17900
  const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
17045
17901
  if (!normalizedPairingCode)
17046
17902
  throw new Error("invalid pairing code");
17047
- const avatarSeed = createHash3("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
17903
+ const avatarSeed = createHash4("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
17048
17904
  return jsonRequest(`${baseUrl}/auth/agent/connect`, {
17049
17905
  pairing_code: normalizedPairingCode,
17050
17906
  harness: selection.harness,
@@ -17069,7 +17925,7 @@ async function installCurrentRelease(fetchImpl) {
17069
17925
  });
17070
17926
  await activateRelease(layout, releaseId);
17071
17927
  return {
17072
- binary: resolve15(layout.binDir, "beeline"),
17928
+ binary: resolve19(layout.binDir, "beeline"),
17073
17929
  version: published.version ?? releaseId
17074
17930
  };
17075
17931
  }
@@ -17092,8 +17948,8 @@ function providerEnvironment(selection) {
17092
17948
  };
17093
17949
  }
17094
17950
  async function writePrivateJson(path, value) {
17095
- await mkdir10(dirname9(path), { recursive: true, mode: 448 });
17096
- await writeFile7(path, `${JSON.stringify(value, null, 2)}
17951
+ await mkdir11(dirname10(path), { recursive: true, mode: 448 });
17952
+ await writeFile9(path, `${JSON.stringify(value, null, 2)}
17097
17953
  `, { mode: 384 });
17098
17954
  await chmod4(path, 384);
17099
17955
  }
@@ -17101,10 +17957,10 @@ async function writeProviderEnv(selection, agentPubkey) {
17101
17957
  const values = providerEnvironment(selection);
17102
17958
  if (Object.keys(values).length === 0)
17103
17959
  return void 0;
17104
- const path = resolve15(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
17105
- await mkdir10(dirname9(path), { recursive: true, mode: 448 });
17960
+ const path = resolve19(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
17961
+ await mkdir11(dirname10(path), { recursive: true, mode: 448 });
17106
17962
  const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
17107
- await writeFile7(path, `${contents}
17963
+ await writeFile9(path, `${contents}
17108
17964
  `, { mode: 384 });
17109
17965
  await chmod4(path, 384);
17110
17966
  return path;
@@ -17158,7 +18014,7 @@ async function runConnectCommand(code, options = {}) {
17158
18014
  const grant = await brassSpinner("Connecting to your Beeline Workspace\u2026", () => requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl), (connectedGrant) => `Connected to ${connectedGrant.workspace_name}`);
17159
18015
  const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
17160
18016
  const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
17161
- const grantPath = resolve15(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
18017
+ const grantPath = resolve19(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
17162
18018
  await writePrivateJson(grantPath, {
17163
18019
  agentSecretKey: grant.agent_secret_key,
17164
18020
  bodySecretKey: grant.body_secret_key,
@@ -17209,11 +18065,22 @@ async function runConnectFinishCommand(path) {
17209
18065
  throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
17210
18066
  }
17211
18067
  try {
17212
- const grant = JSON.parse(await readFile5(resolve15(path), "utf8"));
18068
+ const grant = JSON.parse(await readFile9(resolve19(path), "utf8"));
17213
18069
  if (!isDevicePairingGrant(grant))
17214
18070
  throw new Error("device connection grant is invalid");
17215
- await completeDevicePairing(grant);
17216
- await unlink2(resolve15(path));
18071
+ const connected = await completeDevicePairing(grant);
18072
+ await unlink2(resolve19(path));
18073
+ const providerEnv = grant.llmEnvFile ? await readFile9(grant.llmEnvFile, "utf8").catch(() => "") : "";
18074
+ const model = openRouterModelId(grant.model, {
18075
+ OPENROUTER_API_KEY: /^OPENROUTER_API_KEY=\S/m.test(providerEnv) ? "set" : ""
18076
+ });
18077
+ if (model) {
18078
+ const decision2 = await resolveOpenRouterRouting({
18079
+ model,
18080
+ cacheDir: openRouterRoutingCacheDir(dirname10(connected.configPath))
18081
+ });
18082
+ console.log(decision2.line);
18083
+ }
17217
18084
  } catch (error) {
17218
18085
  throw new ConnectFailureError(connectPlainFailure(error));
17219
18086
  }
@@ -17227,20 +18094,20 @@ init_self_update_manifest();
17227
18094
  // apps/body/dist/managed-update.js
17228
18095
  init_self_update();
17229
18096
  import { spawn as spawn6 } from "node:child_process";
17230
- import { mkdir as mkdir12, rm as rm5, stat as stat2, writeFile as writeFile9 } from "node:fs/promises";
17231
- import { dirname as dirname11, resolve as resolve17 } from "node:path";
18097
+ import { mkdir as mkdir13, rm as rm5, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
18098
+ import { dirname as dirname12, resolve as resolve21 } from "node:path";
17232
18099
 
17233
18100
  // apps/body/dist/update-rollback-alert.js
17234
- import { mkdir as mkdir11, readFile as readFile6, rename as rename4, writeFile as writeFile8 } from "node:fs/promises";
17235
- import { dirname as dirname10, resolve as resolve16 } from "node:path";
18101
+ import { mkdir as mkdir12, readFile as readFile10, rename as rename4, writeFile as writeFile10 } from "node:fs/promises";
18102
+ import { dirname as dirname11, resolve as resolve20 } from "node:path";
17236
18103
  function updateRollbackAlertPath(runtimeDir) {
17237
- return resolve16(runtimeDir, "update-rollback-alert.json");
18104
+ return resolve20(runtimeDir, "update-rollback-alert.json");
17238
18105
  }
17239
18106
  async function writeAlert(runtimeDir, alert) {
17240
18107
  const path = updateRollbackAlertPath(runtimeDir);
17241
18108
  const staged = `${path}.${process.pid}.tmp`;
17242
- await mkdir11(dirname10(path), { recursive: true });
17243
- await writeFile8(staged, `${JSON.stringify(alert, null, 2)}
18109
+ await mkdir12(dirname11(path), { recursive: true });
18110
+ await writeFile10(staged, `${JSON.stringify(alert, null, 2)}
17244
18111
  `, { mode: 384 });
17245
18112
  await rename4(staged, path);
17246
18113
  }
@@ -17252,7 +18119,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
17252
18119
  }
17253
18120
  async function readUpdateRollbackAlert(runtimeDir) {
17254
18121
  try {
17255
- const value = JSON.parse(await readFile6(updateRollbackAlertPath(runtimeDir), "utf8"));
18122
+ const value = JSON.parse(await readFile10(updateRollbackAlertPath(runtimeDir), "utf8"));
17256
18123
  if (value.version !== 1 || typeof value.releaseId !== "string")
17257
18124
  return void 0;
17258
18125
  return value;
@@ -17277,13 +18144,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
17277
18144
  var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
17278
18145
  async function withInstallLock(layout, work, options = {}) {
17279
18146
  const now2 = options.now ?? Date.now;
17280
- const lock = resolve17(layout.releasesRoot, ".state", "install.lock");
18147
+ const lock = resolve21(layout.releasesRoot, ".state", "install.lock");
17281
18148
  const deadline = now2() + (options.waitMs ?? 1e4);
17282
- await mkdir12(dirname11(lock), { recursive: true });
18149
+ await mkdir13(dirname12(lock), { recursive: true });
17283
18150
  for (; ; ) {
17284
18151
  try {
17285
- await mkdir12(lock);
17286
- await writeFile9(resolve17(lock, "owner"), `${process.pid}
18152
+ await mkdir13(lock);
18153
+ await writeFile11(resolve21(lock, "owner"), `${process.pid}
17287
18154
  ${now2()}
17288
18155
  `, "utf8");
17289
18156
  break;
@@ -17320,7 +18187,6 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
17320
18187
  #requested = false;
17321
18188
  #restartRequest;
17322
18189
  #stagedReleaseId;
17323
- #drainDeadlineLogged = false;
17324
18190
  constructor(options) {
17325
18191
  this.#layout = options.layout;
17326
18192
  this.#loadedRelease = options.loadedRelease;
@@ -17407,7 +18273,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
17407
18273
  if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
17408
18274
  const from = await readInstalledBundleIdentity({
17409
18275
  ...this.#layout,
17410
- libDir: resolve17(this.#layout.releasesRoot, this.#loadedRelease)
18276
+ libDir: resolve21(this.#layout.releasesRoot, this.#loadedRelease)
17411
18277
  }).catch(() => void 0) ?? {};
17412
18278
  const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
17413
18279
  const record2 = {
@@ -17444,11 +18310,13 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
17444
18310
  }
17445
18311
  /**
17446
18312
  * Resolve one handoff tick against the daemon's authoritative turn registry.
17447
- * A staged release stays inert while accepted work is running.
17448
- * `quiesceIfIdle` closes intake in the same synchronous transition that
17449
- * proves idle, so a new turn cannot race activation. Expiring the bounded
17450
- * drain window records delayed convergence; it never turns a routine update
17451
- * into an ACP cancellation or a failed Room receipt.
18313
+ * Busy means a turn is executing right now; serving Rooms and corners with
18314
+ * no turn in flight is idle. `quiesceIfIdle` closes intake in the same
18315
+ * synchronous transition that proves idle, so a new turn cannot race
18316
+ * activation. A staged release stays inert while a turn runs — until the
18317
+ * absolute drain deadline, when the restart is forced (`forced: true`) and
18318
+ * the caller cancels the running turn; the convergence contract outranks a
18319
+ * stuck turn. `ManagedUpdateDrain` enforces that deadline with a timer.
17452
18320
  */
17453
18321
  async restartRequest(quiesceIfIdle) {
17454
18322
  const activeDrift = await this.check();
@@ -17457,12 +18325,11 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
17457
18325
  const request = this.#restartRequest;
17458
18326
  if (!request)
17459
18327
  throw new Error("update drift was detected without an in-memory restart request");
18328
+ let forced = false;
17460
18329
  if (!quiesceIfIdle()) {
17461
- if (this.#now() >= request.drainDeadlineAt && !this.#drainDeadlineLogged) {
17462
- console.log(`[thin-core] update drain window elapsed with active work; keeping the staged release ${request.desiredRelease} inert instead of interrupting the Room turn`);
17463
- this.#drainDeadlineLogged = true;
17464
- }
17465
- return { kind: "waiting", request };
18330
+ if (this.#now() < request.drainDeadlineAt)
18331
+ return { kind: "waiting", request };
18332
+ forced = true;
17466
18333
  }
17467
18334
  if (this.#stagedReleaseId) {
17468
18335
  const stagedReleaseId = this.#stagedReleaseId;
@@ -17482,7 +18349,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
17482
18349
  this.#restartRequest = void 0;
17483
18350
  await this.#journalDrift(stagedReleaseId);
17484
18351
  }
17485
- return { kind: "restart", request: this.#restartRequest ?? request };
18352
+ return { kind: "restart", request: this.#restartRequest ?? request, forced };
17486
18353
  }
17487
18354
  };
17488
18355
  async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, waiting = async () => void 0) {
@@ -17493,9 +18360,88 @@ async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, wa
17493
18360
  await waiting(next.request);
17494
18361
  return "waiting-for-idle";
17495
18362
  }
17496
- await restart(next.request);
18363
+ await restart(next.request, next.forced ? "forced" : "drained");
17497
18364
  return "restarting";
17498
18365
  }
18366
+ var UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS = 6e4;
18367
+ var ManagedUpdateDrain = class {
18368
+ #options;
18369
+ #now;
18370
+ #log;
18371
+ #setTimer;
18372
+ #clearTimer;
18373
+ #deadlineTimer;
18374
+ #waitLogTimer;
18375
+ #resolving;
18376
+ #restarting = false;
18377
+ constructor(options) {
18378
+ this.#options = options;
18379
+ this.#now = options.now ?? Date.now;
18380
+ this.#log = options.log ?? ((line) => console.log(line));
18381
+ this.#setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
18382
+ this.#clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
18383
+ }
18384
+ /** Called from every completed core progress tick. */
18385
+ tick() {
18386
+ return this.#resolve();
18387
+ }
18388
+ #resolve() {
18389
+ if (this.#restarting)
18390
+ return Promise.resolve("restarting");
18391
+ if (this.#resolving)
18392
+ return this.#resolving;
18393
+ this.#resolving = this.#step().finally(() => {
18394
+ this.#resolving = void 0;
18395
+ });
18396
+ return this.#resolving;
18397
+ }
18398
+ async #step() {
18399
+ const { update, quiesceIfIdle, activeTurnCount } = this.#options;
18400
+ let armed;
18401
+ const progress = await coordinateManagedUpdateHandoff(update, quiesceIfIdle, async (request, mode) => {
18402
+ this.#disarm();
18403
+ if (mode === "forced") {
18404
+ this.#log(`[thin-core] update restart forced: drain deadline reached with ${activeTurnCount()} active turn(s); cancelling them and restarting onto ${request.desiredRelease}`);
18405
+ }
18406
+ await this.#options.restart(request, mode);
18407
+ this.#restarting = true;
18408
+ }, async (request) => {
18409
+ armed = request;
18410
+ await this.#options.waiting?.(request);
18411
+ });
18412
+ if (armed && !this.#deadlineTimer)
18413
+ this.#arm(armed);
18414
+ return progress;
18415
+ }
18416
+ #arm(request) {
18417
+ this.#logWaiting(request);
18418
+ this.#scheduleWaitLog(request);
18419
+ this.#deadlineTimer = this.#setTimer(() => void this.#resolve().catch((error) => {
18420
+ this.#log(`[thin-core] update restart at the drain deadline failed; retrying on the next tick: ${error instanceof Error ? error.message : String(error)}`);
18421
+ }), Math.max(0, request.drainDeadlineAt - this.#now()));
18422
+ }
18423
+ #scheduleWaitLog(request) {
18424
+ this.#waitLogTimer = this.#setTimer(() => {
18425
+ this.#waitLogTimer = void 0;
18426
+ if (this.#restarting)
18427
+ return;
18428
+ this.#logWaiting(request);
18429
+ this.#scheduleWaitLog(request);
18430
+ }, UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS);
18431
+ }
18432
+ #logWaiting(request) {
18433
+ const minutesLeft = Math.max(0, Math.ceil((request.drainDeadlineAt - this.#now()) / 6e4));
18434
+ this.#log(`[thin-core] update restart waiting: ${this.#options.activeTurnCount()} active turn(s); deadline in ${minutesLeft}m`);
18435
+ }
18436
+ #disarm() {
18437
+ if (this.#deadlineTimer !== void 0)
18438
+ this.#clearTimer(this.#deadlineTimer);
18439
+ if (this.#waitLogTimer !== void 0)
18440
+ this.#clearTimer(this.#waitLogTimer);
18441
+ this.#deadlineTimer = void 0;
18442
+ this.#waitLogTimer = void 0;
18443
+ }
18444
+ };
17499
18445
  function numberEnv(env, name, fallback) {
17500
18446
  const value = Number(env[name]);
17501
18447
  return Number.isFinite(value) && value >= 0 ? value : fallback;
@@ -17614,7 +18560,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
17614
18560
  });
17615
18561
  if (!accepted)
17616
18562
  return false;
17617
- await writeFile9(resolve17(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
18563
+ await writeFile11(resolve21(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
17618
18564
  readyAt: (options.now ?? Date.now)(),
17619
18565
  loadedRelease,
17620
18566
  functionalProof: options.functionalProof
@@ -17819,16 +18765,16 @@ async function runUpdateCommand(args) {
17819
18765
  init_self_update();
17820
18766
 
17821
18767
  // apps/body/dist/daemon-failure.js
17822
- import { mkdir as mkdir13, readFile as readFile7, rename as rename5, rm as rm6, writeFile as writeFile10 } from "node:fs/promises";
17823
- import { dirname as dirname12, resolve as resolve18 } from "node:path";
18768
+ import { mkdir as mkdir14, readFile as readFile11, rename as rename5, rm as rm6, writeFile as writeFile12 } from "node:fs/promises";
18769
+ import { dirname as dirname13, resolve as resolve22 } from "node:path";
17824
18770
  var DAEMON_FAILURE_LIMIT = 3;
17825
18771
  var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
17826
18772
  function daemonFailurePath(runtimeDir) {
17827
- return resolve18(runtimeDir, "daemon-distress.json");
18773
+ return resolve22(runtimeDir, "daemon-distress.json");
17828
18774
  }
17829
18775
  async function readFailureRecord(runtimeDir) {
17830
18776
  try {
17831
- const value = JSON.parse(await readFile7(daemonFailurePath(runtimeDir), "utf8"));
18777
+ const value = JSON.parse(await readFile11(daemonFailurePath(runtimeDir), "utf8"));
17832
18778
  if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure) => typeof failure !== "number") || typeof value.lastError !== "string") {
17833
18779
  return void 0;
17834
18780
  }
@@ -17840,8 +18786,8 @@ async function readFailureRecord(runtimeDir) {
17840
18786
  async function writeFailureRecord(runtimeDir, record2) {
17841
18787
  const path = daemonFailurePath(runtimeDir);
17842
18788
  const staged = `${path}.${process.pid}.tmp`;
17843
- await mkdir13(dirname12(path), { recursive: true, mode: 448 });
17844
- await writeFile10(staged, `${JSON.stringify(record2, null, 2)}
18789
+ await mkdir14(dirname13(path), { recursive: true, mode: 448 });
18790
+ await writeFile12(staged, `${JSON.stringify(record2, null, 2)}
17845
18791
  `, { mode: 384 });
17846
18792
  await rename5(staged, path);
17847
18793
  }
@@ -17863,9 +18809,9 @@ async function clearDaemonStartFailures(runtimeDir) {
17863
18809
  }
17864
18810
 
17865
18811
  // apps/body/dist/update-functional-probe.js
17866
- import { mkdir as mkdir14, rm as rm7 } from "node:fs/promises";
18812
+ import { mkdir as mkdir15, rm as rm7 } from "node:fs/promises";
17867
18813
  import { homedir as homedir10 } from "node:os";
17868
- import { resolve as resolve19 } from "node:path";
18814
+ import { resolve as resolve23 } from "node:path";
17869
18815
  var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
17870
18816
  var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
17871
18817
  var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
@@ -17887,11 +18833,11 @@ async function runUpdateFunctionalProbe(input) {
17887
18833
  if (input.sandboxRequired && !input.config.bwrapPath) {
17888
18834
  throw new UpdateFunctionalProbeError("sandbox-unavailable", "the configured bubblewrap boundary did not pass its startup self-test");
17889
18835
  }
17890
- const root = resolve19(input.runtimeDir, "update-functional-probe");
17891
- const cwd = resolve19(root, "checkout");
17892
- const homeRoot = resolve19(root, "agent-home");
18836
+ const root = resolve23(input.runtimeDir, "update-functional-probe");
18837
+ const cwd = resolve23(root, "checkout");
18838
+ const homeRoot = resolve23(root, "agent-home");
17893
18839
  await rm7(root, { recursive: true, force: true });
17894
- await mkdir14(cwd, { recursive: true, mode: 448 });
18840
+ await mkdir15(cwd, { recursive: true, mode: 448 });
17895
18841
  let client;
17896
18842
  try {
17897
18843
  const agentEnv = {
@@ -17901,7 +18847,8 @@ async function runUpdateFunctionalProbe(input) {
17901
18847
  operatorHome: input.config.operatorHome ?? homedir10(),
17902
18848
  sharedSkills: input.config.sharedSkills ?? [],
17903
18849
  skillReleaseId: input.releaseId,
17904
- failClosed: true
18850
+ failClosed: true,
18851
+ ...openRouterRoutingInput({ ...input.config, openRouterRoutingCacheDir: openRouterRoutingCacheDir(input.runtimeDir) }, input.config.modelSelection)
17905
18852
  })
17906
18853
  };
17907
18854
  const selectedAgent = {
@@ -17913,11 +18860,12 @@ async function runUpdateFunctionalProbe(input) {
17913
18860
  command,
17914
18861
  args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
17915
18862
  };
18863
+ let modelAnswer = {};
17916
18864
  if (input.config.bwrapPath) {
17917
18865
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
17918
18866
  const operatorHome = input.config.operatorHome ?? homedir10();
17919
18867
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
17920
- await Promise.all(homeStateDirs.map((dir) => mkdir14(dir, { recursive: true })));
18868
+ await Promise.all(homeStateDirs.map((dir) => mkdir15(dir, { recursive: true })));
17921
18869
  spawnCommand = wrapAgentCommand({
17922
18870
  bwrapPath: input.config.bwrapPath,
17923
18871
  spec: {
@@ -17956,8 +18904,21 @@ async function runUpdateFunctionalProbe(input) {
17956
18904
  }
17957
18905
  try {
17958
18906
  const served = await client.sessionPrompt(opened.sessionId, "Reply READY.", input.turnTimeoutMs ?? UPDATE_PROBE_TURN_TIMEOUT_MS);
17959
- if (!served.agentText.trim()) {
17960
- throw new UpdateFunctionalProbeError("turn-failed", "the harness completed a session/prompt without an agent answer");
18907
+ if (served.agentText.trim()) {
18908
+ modelAnswer = { modelAnswer: "served" };
18909
+ } else {
18910
+ const explained = await explainEmptyAgentTurn({
18911
+ agentLabel: command,
18912
+ agentEnv,
18913
+ sessionId: opened.sessionId,
18914
+ result: served
18915
+ });
18916
+ const modelSide = isAccountOrProviderRefusal(explained.record) || explained.record?.kind === "empty";
18917
+ if (!modelSide) {
18918
+ throw new UpdateFunctionalProbeError("turn-failed", `the harness completed a session/prompt without an agent answer: ${explained.reason}`);
18919
+ }
18920
+ console.warn(`[body] update probe: the bundle reached the model boundary but the model answered nothing (${explained.reason}); that is the model's own answer on any release, so the probe passes without one`);
18921
+ modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: explained.reason };
17961
18922
  }
17962
18923
  } catch (error) {
17963
18924
  throw new UpdateFunctionalProbeError("turn-failed", error instanceof Error ? error.message : String(error), { cause: error });
@@ -17972,7 +18933,8 @@ async function runUpdateFunctionalProbe(input) {
17972
18933
  sandboxed: Boolean(input.config.bwrapPath),
17973
18934
  sessionStarted: true,
17974
18935
  turnCompleted: true,
17975
- nativeTools: []
18936
+ nativeTools: [],
18937
+ ...modelAnswer
17976
18938
  };
17977
18939
  } finally {
17978
18940
  await client?.stop().catch(() => void 0);
@@ -17981,8 +18943,8 @@ async function runUpdateFunctionalProbe(input) {
17981
18943
  }
17982
18944
 
17983
18945
  // apps/body/dist/release-status.js
17984
- import { readFile as readFile8, readdir as readdir3, rename as rename6, writeFile as writeFile11 } from "node:fs/promises";
17985
- import { resolve as resolve20 } from "node:path";
18946
+ import { readFile as readFile12, readdir as readdir4, rename as rename6, writeFile as writeFile13 } from "node:fs/promises";
18947
+ import { resolve as resolve24 } from "node:path";
17986
18948
  var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
17987
18949
  var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
17988
18950
  var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
@@ -17999,9 +18961,9 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
17999
18961
  pid: options.pid ?? process.pid,
18000
18962
  readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
18001
18963
  };
18002
- const target = resolve20(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
18964
+ const target = resolve24(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
18003
18965
  const temporary = `${target}.${status.pid}.tmp`;
18004
- await writeFile11(temporary, `${JSON.stringify(status, null, 2)}
18966
+ await writeFile13(temporary, `${JSON.stringify(status, null, 2)}
18005
18967
  `, { mode: 384 });
18006
18968
  await rename6(temporary, target);
18007
18969
  return status;
@@ -18042,7 +19004,7 @@ var DaemonExitError = class extends Error {
18042
19004
  };
18043
19005
  async function runStoredDaemon(pathOrPointer) {
18044
19006
  const configPath = await resolveRuntimeConfigPath(pathOrPointer);
18045
- daemonFailureRuntimeDir = dirname13(configPath);
19007
+ daemonFailureRuntimeDir = dirname14(configPath);
18046
19008
  const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
18047
19009
  let runtime = accessMigration.runtime;
18048
19010
  if (!runtime.transport) {
@@ -18054,7 +19016,7 @@ async function runStoredDaemon(pathOrPointer) {
18054
19016
  runtime = activated.runtime;
18055
19017
  const daemonApi = activated.client;
18056
19018
  const agent = runtimeAgentCommand(runtime);
18057
- await writeFile12(resolve21(dirname13(configPath), "daemon.pid"), `${process.pid}
19019
+ await writeFile14(resolve25(dirname14(configPath), "daemon.pid"), `${process.pid}
18058
19020
  `, { mode: 384 });
18059
19021
  const env = {
18060
19022
  ...process.env,
@@ -18062,7 +19024,7 @@ async function runStoredDaemon(pathOrPointer) {
18062
19024
  BUZZ_DEV_MCP_BIN: runtime.mcpBinary
18063
19025
  };
18064
19026
  const config = loadBodyConfig({
18065
- workspaceRoot: resolve21(dirname13(configPath), "workspace"),
19027
+ workspaceRoot: resolve25(dirname14(configPath), "workspace"),
18066
19028
  llmEnvFile: runtime.llmEnvFile,
18067
19029
  env,
18068
19030
  agent
@@ -18097,7 +19059,7 @@ async function runStoredDaemon(pathOrPointer) {
18097
19059
  const stop = () => controller.abort();
18098
19060
  process.once("SIGINT", stop);
18099
19061
  process.once("SIGTERM", stop);
18100
- const runtimeDir = dirname13(configPath);
19062
+ const runtimeDir = dirname14(configPath);
18101
19063
  const layout = beelineInstallLayout(process.env);
18102
19064
  const notifier = new SystemdNotifier();
18103
19065
  let rollbackAlertDrain;
@@ -18143,6 +19105,22 @@ async function runStoredDaemon(pathOrPointer) {
18143
19105
  let stoppingStatus = "daemon stopped";
18144
19106
  try {
18145
19107
  const core = new ThinDaemonCore(runtime, configPath, config, { daemonApi });
19108
+ const updateDrain = update ? new ManagedUpdateDrain({
19109
+ update,
19110
+ quiesceIfIdle: () => core.quiesceForUpdateIfIdle(),
19111
+ activeTurnCount: () => core.activeTurnCount(),
19112
+ restart: async ({ desiredRelease, drainDeadlineAt }, mode) => {
19113
+ if (mode === "forced")
19114
+ await core.prepareForForcedUpdateRestart();
19115
+ core.setDrainDeadlineAt(drainDeadlineAt);
19116
+ stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; ${mode === "forced" ? "active work cancelled at the drain deadline" : "active work drained"}; intake quiesced; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`;
19117
+ await notifier.stopping(stoppingStatus);
19118
+ controller.abort();
19119
+ },
19120
+ waiting: async ({ desiredRelease, drainDeadlineAt }) => {
19121
+ await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`);
19122
+ }
19123
+ }) : void 0;
18146
19124
  const result = await core.run({
18147
19125
  signal: controller.signal,
18148
19126
  onEstablished: async () => {
@@ -18166,28 +19144,26 @@ async function runStoredDaemon(pathOrPointer) {
18166
19144
  }
18167
19145
  functionalProof = gate.proof;
18168
19146
  pendingSuccessor = false;
18169
- console.log(`[thin-core] successor functional probe passed on exact release ${loadedRelease}: ${functionalProof?.harness ?? "unknown"} session/new + turn`);
19147
+ console.log(`[thin-core] successor functional probe passed on exact release ${loadedRelease}: ${functionalProof?.harness ?? "unknown"} session/new + turn` + (functionalProof?.modelAnswer === "unavailable" ? ` (model answer unavailable: ${functionalProof.modelAnswerReason})` : ""));
18170
19148
  }
18171
19149
  await clearDaemonStartFailures(runtimeDir);
18172
19150
  await writeDaemonReleaseStatus(runtimeDir, runtime.agent.publicKey, loadedReleaseIdentity);
18173
19151
  await notifier.ready(`ready; loaded_release=${loadedRelease ?? "development"}`);
18174
19152
  ready = true;
19153
+ void syncAgentModelCatalog({
19154
+ api: daemonApi,
19155
+ agent,
19156
+ agentEnv: config.agentEnv,
19157
+ agentId: runtime.agent.publicKey,
19158
+ workspaceId: runtime.communityId,
19159
+ runtimeDir,
19160
+ ...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {}
19161
+ });
18175
19162
  },
18176
19163
  onProgress: async (status) => {
18177
19164
  void drainRollbackAlert(core.activeRoomIds()[0] ?? runtime.rooms[0]?.channelId);
18178
19165
  await notifier.progress(`loaded_release=${loadedRelease ?? "development"}; ${status}`);
18179
- if (!update)
18180
- return;
18181
- await coordinateManagedUpdateHandoff(update, () => core.quiesceForUpdateIfIdle(), async ({ desiredRelease, drainDeadlineAt }) => {
18182
- if (Date.now() >= drainDeadlineAt)
18183
- await core.prepareForForcedUpdateRestart();
18184
- core.setDrainDeadlineAt(drainDeadlineAt);
18185
- stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; active work drained; intake quiesced; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`;
18186
- await notifier.stopping(stoppingStatus);
18187
- controller.abort();
18188
- }, async ({ desiredRelease, drainDeadlineAt }) => {
18189
- await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`);
18190
- });
19166
+ await updateDrain?.tick();
18191
19167
  }
18192
19168
  });
18193
19169
  if (result === "agent-removed") {
@@ -18209,8 +19185,8 @@ async function runStoredDaemon(pathOrPointer) {
18209
19185
  throw error;
18210
19186
  } finally {
18211
19187
  await notifier.stopping(stoppingStatus).catch(() => void 0);
18212
- const pidPath = resolve21(dirname13(configPath), "daemon.pid");
18213
- const recorded = Number((await readFile9(pidPath, "utf8").catch(() => "")).trim());
19188
+ const pidPath = resolve25(dirname14(configPath), "daemon.pid");
19189
+ const recorded = Number((await readFile13(pidPath, "utf8").catch(() => "")).trim());
18214
19190
  if (recorded === process.pid) {
18215
19191
  await unlink3(pidPath).catch(() => void 0);
18216
19192
  }
@@ -18266,14 +19242,14 @@ async function main() {
18266
19242
  const agentPubkey = agentFlag >= 0 ? args[agentFlag + 1] : void 0;
18267
19243
  if (!configPath && agentPubkey) {
18268
19244
  const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
18269
- configPath = configs.find((candidate) => dirname13(candidate).endsWith(agentPubkey));
19245
+ configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
18270
19246
  }
18271
19247
  if (!configPath && agentPubkey) {
18272
19248
  throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
18273
19249
  }
18274
19250
  if (!configPath)
18275
19251
  throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
18276
- await runStoredDaemon(resolve21(configPath));
19252
+ await runStoredDaemon(resolve25(configPath));
18277
19253
  return;
18278
19254
  }
18279
19255
  if (command === "update") {
@@ -18290,7 +19266,7 @@ async function main() {
18290
19266
  if (!agentPubkey)
18291
19267
  throw new Error("stop requires --agent <pubkey>");
18292
19268
  const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
18293
- const configPath = configs.find((candidate) => dirname13(candidate).endsWith(agentPubkey));
19269
+ const configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
18294
19270
  if (!configPath)
18295
19271
  throw new Error(`no stored runtime found for agent ${agentPubkey}`);
18296
19272
  const runtime = await readRuntimeRecord(configPath);