usebeeline 0.0.35 → 0.0.37

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 +282 -119
  2. package/package.json +1 -1
@@ -309,10 +309,10 @@ __export(self_update_exports, {
309
309
  });
310
310
  import { createHash as createHash2 } from "node:crypto";
311
311
  import { constants as fsConstants } from "node:fs";
312
- import { access, chmod as chmod3, lstat as lstat2, mkdir as mkdir8, open, readFile as readFile4, rename as rename3, rm as rm4, symlink as symlink2, writeFile as writeFile5 } from "node:fs/promises";
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";
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 join3, resolve as resolve14 } from "node:path";
315
+ import { dirname as dirname8, join as join6, resolve as resolve14 } from "node:path";
316
316
  function anchorLayout(rawLibDir) {
317
317
  const libDir = resolve14(rawLibDir);
318
318
  const segments = libDir.split(/[/\\]/);
@@ -353,7 +353,7 @@ function hostPlatformKey() {
353
353
  return `${os}-${arch}`;
354
354
  }
355
355
  function bundleJsonCandidates(bundleDir) {
356
- return [join3(bundleDir, "lib", "beeline", "bundle.json"), join3(bundleDir, "bundle.json")];
356
+ return [join6(bundleDir, "lib", "beeline", "bundle.json"), join6(bundleDir, "bundle.json")];
357
357
  }
358
358
  async function readBundleJson(bundleDir) {
359
359
  let raw;
@@ -377,7 +377,7 @@ async function readBundleJson(bundleDir) {
377
377
  }
378
378
  }
379
379
  function updateStatePath(layout) {
380
- return join3(layout.releasesRoot, ".state", "update-state.json");
380
+ return join6(layout.releasesRoot, ".state", "update-state.json");
381
381
  }
382
382
  async function readUpdateState(layout) {
383
383
  try {
@@ -387,8 +387,8 @@ async function readUpdateState(layout) {
387
387
  }
388
388
  }
389
389
  async function writeUpdateState(layout, state) {
390
- await mkdir8(join3(layout.releasesRoot, ".state"), { recursive: true });
391
- await writeFile5(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
390
+ await mkdir9(join6(layout.releasesRoot, ".state"), { recursive: true });
391
+ await writeFile6(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
392
392
  `, "utf8");
393
393
  }
394
394
  async function readInstalledBundleIdentity(layout, _state = {}) {
@@ -469,7 +469,7 @@ function run(command, args, timeoutMs) {
469
469
  });
470
470
  }
471
471
  function entrypointCandidates(bundleDir) {
472
- return [join3(bundleDir, BUNDLE_ENTRYPOINT), join3(bundleDir, "beeline-cli.mjs")];
472
+ return [join6(bundleDir, BUNDLE_ENTRYPOINT), join6(bundleDir, "beeline-cli.mjs")];
473
473
  }
474
474
  async function resolveBundleEntrypoint(bundleDir) {
475
475
  for (const candidate of entrypointCandidates(bundleDir)) {
@@ -491,8 +491,8 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
491
491
  const fetchImpl = opts.fetchImpl ?? fetch;
492
492
  const log = opts.logger ?? ((line) => console.log(`[body] self-update: ${line}`));
493
493
  const releaseId = sanitizeReleaseId(published.commit ?? published.version ?? `release-${Date.now()}`);
494
- const releaseDir = join3(layout.releasesRoot, releaseId);
495
- const okMarker = join3(releaseDir, ".stage-ok");
494
+ const releaseDir = join6(layout.releasesRoot, releaseId);
495
+ const okMarker = join6(releaseDir, ".stage-ok");
496
496
  let previouslyVerified = false;
497
497
  try {
498
498
  const recorded = await readFile4(okMarker, "utf8");
@@ -501,8 +501,8 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
501
501
  previouslyVerified = true;
502
502
  } catch {
503
503
  }
504
- await mkdir8(releaseDir, { recursive: true });
505
- const tempArchive = join3(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
504
+ await mkdir9(releaseDir, { recursive: true });
505
+ const tempArchive = join6(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
506
506
  try {
507
507
  log(`downloading ${published.file}`);
508
508
  const response = await fetchImpl(archiveUrlFor(manifestUrl, published.file), {
@@ -522,7 +522,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
522
522
  if (actual !== published.sha256.toLowerCase()) {
523
523
  throw new Error(`checksum mismatch for ${published.file}: expected ${published.sha256}, got ${actual} \u2014 aborting without touching the installed bundle`);
524
524
  }
525
- await writeFile5(tempArchive, Buffer.concat(chunks), { mode: 384 });
525
+ await writeFile6(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 = "";
@@ -542,18 +542,18 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
542
542
  throw new Error(`extracting bundle failed: ${extract2.stderr}`);
543
543
  for (const relative3 of requiredBundlePaths()) {
544
544
  try {
545
- await access(join3(releaseDir, relative3), fsConstants.F_OK);
545
+ await access(join6(releaseDir, relative3), fsConstants.F_OK);
546
546
  } catch {
547
547
  throw new Error(`staged bundle is missing ${relative3}`);
548
548
  }
549
549
  }
550
550
  if (opts.smokeTestCli !== false) {
551
- const probe = await run(process.execPath, [join3(releaseDir, BUNDLE_ENTRYPOINT), "--version"], 6e4);
551
+ const probe = await run(process.execPath, [join6(releaseDir, BUNDLE_ENTRYPOINT), "--version"], 6e4);
552
552
  if (probe.status !== 0) {
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 writeFile5(okMarker, `${published.sha256}
556
+ await writeFile6(okMarker, `${published.sha256}
557
557
  `, "utf8");
558
558
  log(`staged release ${releaseId} (sha256 verified)`);
559
559
  return releaseId;
@@ -581,26 +581,26 @@ function forwarderScript(tool) {
581
581
  }
582
582
  async function replaceFile(path, contents, mode) {
583
583
  const temp = `${path}.new-${process.pid}`;
584
- await writeFile5(temp, contents, { mode });
584
+ await writeFile6(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
- const releaseDir = join3(layout.releasesRoot, releaseId);
590
- await access(join3(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
591
- await mkdir8(layout.releasesRoot, { recursive: true });
592
- await mkdir8(layout.binDir, { recursive: true });
589
+ const releaseDir = join6(layout.releasesRoot, releaseId);
590
+ await access(join6(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
591
+ await mkdir9(layout.releasesRoot, { recursive: true });
592
+ await mkdir9(layout.binDir, { recursive: true });
593
593
  let previousReleaseId = await activeReleaseId(layout);
594
594
  const kind = await pathKind(layout.libDir);
595
595
  if (kind === "directory") {
596
596
  const legacyIdentity = await readBundleJson(layout.libDir);
597
597
  const legacyId = sanitizeReleaseId(legacyIdentity?.commit ?? legacyIdentity?.version ?? `legacy-${Date.now()}`);
598
- const legacyDir = join3(layout.releasesRoot, legacyId);
598
+ const legacyDir = join6(layout.releasesRoot, legacyId);
599
599
  try {
600
600
  await access(legacyDir, fsConstants.F_OK);
601
601
  previousReleaseId = `${legacyId}-${Date.now()}`;
602
- await rename3(layout.libDir, join3(layout.releasesRoot, previousReleaseId));
603
- await normalizeLegacyBundleShape(join3(layout.releasesRoot, previousReleaseId));
602
+ await rename3(layout.libDir, join6(layout.releasesRoot, previousReleaseId));
603
+ await normalizeLegacyBundleShape(join6(layout.releasesRoot, previousReleaseId));
604
604
  } catch {
605
605
  await rename3(layout.libDir, legacyDir);
606
606
  await normalizeLegacyBundleShape(legacyDir);
@@ -609,18 +609,18 @@ async function activateRelease(layout, releaseId) {
609
609
  }
610
610
  const tempLink = `${layout.libDir}.new-${process.pid}`;
611
611
  await rm4(tempLink, { force: true });
612
- await symlink2(join3("beeline-releases", releaseId), tempLink);
612
+ await symlink2(join6("beeline-releases", releaseId), tempLink);
613
613
  await rename3(tempLink, layout.libDir);
614
614
  await fsyncDir(dirname8(layout.libDir));
615
615
  await writeBinForwarders(layout, releaseDir);
616
616
  return { previousReleaseId };
617
617
  }
618
618
  async function normalizeLegacyBundleShape(bundleDir) {
619
- const innerLib = join3(bundleDir, "lib", "beeline");
619
+ const innerLib = join6(bundleDir, "lib", "beeline");
620
620
  let anyFlat = false;
621
621
  for (const name of LEGACY_FLAT_BUNDLE_FILES) {
622
622
  try {
623
- await access(join3(bundleDir, name), fsConstants.F_OK);
623
+ await access(join6(bundleDir, name), fsConstants.F_OK);
624
624
  anyFlat = true;
625
625
  break;
626
626
  } catch {
@@ -628,31 +628,31 @@ async function normalizeLegacyBundleShape(bundleDir) {
628
628
  }
629
629
  if (!anyFlat)
630
630
  return;
631
- await mkdir8(innerLib, { recursive: true });
631
+ await mkdir9(innerLib, { recursive: true });
632
632
  for (const name of LEGACY_FLAT_BUNDLE_FILES) {
633
633
  try {
634
- await access(join3(innerLib, name), fsConstants.F_OK);
634
+ await access(join6(innerLib, name), fsConstants.F_OK);
635
635
  continue;
636
636
  } catch {
637
637
  }
638
- await rename3(join3(bundleDir, name), join3(innerLib, name)).catch(() => void 0);
638
+ await rename3(join6(bundleDir, name), join6(innerLib, name)).catch(() => void 0);
639
639
  }
640
640
  }
641
641
  async function writeBinForwarders(layout, activeBundleRoot) {
642
642
  for (const tool of FORWARDER_TOOLS) {
643
- const target = join3(activeBundleRoot, "bin", tool);
643
+ const target = join6(activeBundleRoot, "bin", tool);
644
644
  try {
645
645
  await access(target, fsConstants.X_OK);
646
646
  } catch {
647
647
  continue;
648
648
  }
649
- await replaceFile(join3(layout.binDir, tool), forwarderScript(tool), 493);
649
+ await replaceFile(join6(layout.binDir, tool), forwarderScript(tool), 493);
650
650
  }
651
651
  }
652
652
  async function repairInstallForwarders(layout, opts = {}) {
653
653
  if (await pathKind(layout.libDir) !== "symlink")
654
654
  return false;
655
- const forwarderPath = join3(layout.binDir, "beeline");
655
+ const forwarderPath = join6(layout.binDir, "beeline");
656
656
  let current;
657
657
  try {
658
658
  current = await readFile4(forwarderPath, "utf8");
@@ -661,25 +661,25 @@ async function repairInstallForwarders(layout, opts = {}) {
661
661
  }
662
662
  if (current === forwarderScript("beeline"))
663
663
  return false;
664
- await mkdir8(layout.binDir, { recursive: true });
664
+ await mkdir9(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;
668
668
  }
669
669
  async function rollbackToPreviousRelease(layout, previousReleaseId) {
670
- const releaseDir = join3(layout.releasesRoot, previousReleaseId);
670
+ const releaseDir = join6(layout.releasesRoot, previousReleaseId);
671
671
  const entrypoint = await resolveBundleEntrypoint(releaseDir);
672
672
  if (!entrypoint) {
673
673
  throw new Error(`release ${previousReleaseId} has no runnable CLI entrypoint`);
674
674
  }
675
675
  const tempLink = `${layout.libDir}.rollback-${process.pid}`;
676
676
  await rm4(tempLink, { force: true });
677
- await symlink2(join3("beeline-releases", previousReleaseId), tempLink);
677
+ await symlink2(join6("beeline-releases", previousReleaseId), tempLink);
678
678
  await rename3(tempLink, layout.libDir);
679
679
  await fsyncDir(dirname8(layout.libDir));
680
680
  }
681
681
  function updateAttemptPath(layout) {
682
- return join3(layout.releasesRoot, ".state", "update-attempt.json");
682
+ return join6(layout.releasesRoot, ".state", "update-attempt.json");
683
683
  }
684
684
  async function readUpdateAttempt(layout) {
685
685
  try {
@@ -693,10 +693,10 @@ async function readUpdateAttempt(layout) {
693
693
  }
694
694
  }
695
695
  async function writeUpdateAttempt(layout, record2) {
696
- await mkdir8(join3(layout.releasesRoot, ".state"), { recursive: true });
696
+ await mkdir9(join6(layout.releasesRoot, ".state"), { recursive: true });
697
697
  const path = updateAttemptPath(layout);
698
698
  const staged = `${path}.${process.pid}.tmp`;
699
- await writeFile5(staged, `${JSON.stringify(record2, null, 2)}
699
+ await writeFile6(staged, `${JSON.stringify(record2, null, 2)}
700
700
  `, { mode: 384 });
701
701
  await rename3(staged, path);
702
702
  }
@@ -956,7 +956,7 @@ var init_self_update = __esm({
956
956
 
957
957
  // apps/body/dist/cli.js
958
958
  import { dirname as dirname13, resolve as resolve21 } from "node:path";
959
- import { readFile as readFile9, unlink as unlink3, writeFile as writeFile11 } from "node:fs/promises";
959
+ import { readFile as readFile9, unlink as unlink3, writeFile as writeFile12 } 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
@@ -2954,6 +2954,12 @@ function cornerAutonomyModeCandidates(agentCommand) {
2954
2954
  return [];
2955
2955
  return ["agent", "edit", "code"];
2956
2956
  }
2957
+ function roomModeCandidates(agentCommand, options = {}) {
2958
+ if (options.osSandbox && agentCommand && /(^|[/\\])codex-acp(\.[a-z]+)?$/i.test(agentCommand)) {
2959
+ return ["agent-full-access"];
2960
+ }
2961
+ return ["read-only", "readonly"];
2962
+ }
2957
2963
 
2958
2964
  // apps/body/dist/harness-tool-scope.js
2959
2965
  var CLAUDE_TOOL_SCOPE_SETTINGS = { disableClaudeAiConnectors: true };
@@ -3268,6 +3274,7 @@ var AcpClient = class extends EventEmitter {
3268
3274
  sessionCommands = /* @__PURE__ */ new Map();
3269
3275
  supportsStandardSteering = false;
3270
3276
  supportsSessionLoading = false;
3277
+ supportsImagePrompts = false;
3271
3278
  alive = false;
3272
3279
  /** Bounded tail of recent stderr, so a spawn/exit failure's rejection text
3273
3280
  * carries the real reason (e.g. a harness's own "missing API key" notice)
@@ -3280,6 +3287,7 @@ var AcpClient = class extends EventEmitter {
3280
3287
  agentCwd;
3281
3288
  inheritProcessEnv;
3282
3289
  autoApprove;
3290
+ osSandbox;
3283
3291
  permissionHandler;
3284
3292
  permissionAllowlist;
3285
3293
  constructor(opts) {
@@ -3295,6 +3303,7 @@ var AcpClient = class extends EventEmitter {
3295
3303
  this.agentCwd = opts.agentCwd;
3296
3304
  this.inheritProcessEnv = opts.inheritProcessEnv ?? process.env.BUZZY_BODY_AGENT_ENV_INHERIT === "1";
3297
3305
  this.autoApprove = opts.autoApprovePermissions ?? true;
3306
+ this.osSandbox = opts.osSandbox ?? false;
3298
3307
  this.permissionHandler = opts.permissionHandler;
3299
3308
  this.permissionAllowlist = opts.permissionAllowlist;
3300
3309
  }
@@ -3354,6 +3363,7 @@ var AcpClient = class extends EventEmitter {
3354
3363
  this.supportsStandardSteering = steering?.supported === true;
3355
3364
  const agentCapabilities = initResult.agentCapabilities;
3356
3365
  this.supportsSessionLoading = agentCapabilities?.loadSession === true;
3366
+ this.supportsImagePrompts = agentCapabilities?.promptCapabilities?.image === true;
3357
3367
  this.emit("initialized", initResult);
3358
3368
  this.notify("notifications/initialized", {});
3359
3369
  }
@@ -3400,6 +3410,10 @@ var AcpClient = class extends EventEmitter {
3400
3410
  canLoadSession() {
3401
3411
  return this.supportsSessionLoading;
3402
3412
  }
3413
+ /** Whether this live ACP process accepts `image` prompt blocks (`promptCapabilities.image`). */
3414
+ canPromptWithImages() {
3415
+ return this.supportsImagePrompts;
3416
+ }
3403
3417
  async sessionNew(opts) {
3404
3418
  const params = {
3405
3419
  cwd: opts.cwd,
@@ -3459,7 +3473,7 @@ var AcpClient = class extends EventEmitter {
3459
3473
  if (!mode)
3460
3474
  return;
3461
3475
  const modes = raw.modes;
3462
- const candidates = mode === "readonly" ? ["read-only", "readonly"] : cornerAutonomyModeCandidates(this.agentLabel);
3476
+ const candidates = mode === "readonly" ? roomModeCandidates(this.agentLabel, { osSandbox: this.osSandbox }) : cornerAutonomyModeCandidates(this.agentLabel);
3463
3477
  const target = modes?.availableModes?.find((candidate) => candidate.id && candidates.includes(candidate.id))?.id;
3464
3478
  if (!target || modes?.currentModeId === target)
3465
3479
  return;
@@ -3506,7 +3520,7 @@ var AcpClient = class extends EventEmitter {
3506
3520
  try {
3507
3521
  const result = await this.request("session/prompt", {
3508
3522
  sessionId,
3509
- prompt: [{ type: "text", text: text2 }]
3523
+ prompt: typeof text2 === "string" ? [{ type: "text", text: text2 }] : [...text2]
3510
3524
  }, timeoutMs, (id) => {
3511
3525
  requestId = id;
3512
3526
  });
@@ -3874,6 +3888,18 @@ function advertisedChoiceId(choice) {
3874
3888
  return choice.value;
3875
3889
  return void 0;
3876
3890
  }
3891
+ function sortModelChoicesNewestFirst(choices) {
3892
+ return choices.map((choice, index) => ({ choice, index })).sort((left, right) => {
3893
+ const leftParts = left.choice.id.match(/\d+/g)?.map(Number) ?? [];
3894
+ const rightParts = right.choice.id.match(/\d+/g)?.map(Number) ?? [];
3895
+ for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
3896
+ const delta = (rightParts[index] ?? -1) - (leftParts[index] ?? -1);
3897
+ if (delta !== 0)
3898
+ return delta;
3899
+ }
3900
+ return left.index - right.index;
3901
+ }).map(({ choice }) => choice);
3902
+ }
3877
3903
  function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort = false) {
3878
3904
  const configOptions = raw?.configOptions;
3879
3905
  const result = [];
@@ -3903,7 +3929,7 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
3903
3929
  id,
3904
3930
  category,
3905
3931
  ...typeof record2.currentValue === "string" ? { currentValue: record2.currentValue } : {},
3906
- options: choices
3932
+ options: category === "model" ? sortModelChoicesNewestFirst(choices) : choices
3907
3933
  });
3908
3934
  }
3909
3935
  const models = raw?.models;
@@ -3929,7 +3955,7 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
3929
3955
  id: GROK_SESSION_MODEL_AXIS_ID,
3930
3956
  category: "model",
3931
3957
  ...currentModelId ? { currentValue: currentModelId } : {},
3932
- options: modelChoices
3958
+ options: sortModelChoicesNewestFirst(modelChoices)
3933
3959
  });
3934
3960
  }
3935
3961
  const effortModelId = preferredModelId ?? currentModelId;
@@ -4214,14 +4240,15 @@ async function applyRuntimeModelPreflight(config, agent, selection, validate = v
4214
4240
  import { execFile as execFile3 } from "node:child_process";
4215
4241
  import { createHash } from "node:crypto";
4216
4242
  import { existsSync as existsSync4, mkdirSync } from "node:fs";
4217
- import { mkdir as mkdir5, rm as rm3 } from "node:fs/promises";
4243
+ import { mkdir as mkdir6, rm as rm3 } from "node:fs/promises";
4218
4244
  import { dirname as dirname4, resolve as resolve10 } from "node:path";
4219
4245
  import { promisify as promisify3 } from "node:util";
4220
4246
 
4221
4247
  // apps/body/dist/monolith-corner-turn.js
4222
4248
  import { execFile as execFile2 } from "node:child_process";
4223
- import { mkdir as mkdir3 } from "node:fs/promises";
4249
+ import { mkdir as mkdir4 } from "node:fs/promises";
4224
4250
  import { homedir as homedir5 } from "node:os";
4251
+ import { join as join4 } from "node:path";
4225
4252
  import { promisify as promisify2 } from "node:util";
4226
4253
 
4227
4254
  // apps/body/dist/agent-home.js
@@ -4241,9 +4268,10 @@ var BEELINE_ROOM_CAPABILITIES = [
4241
4268
  "Tag another agent only when you need something from them: a question, a handoff, a task. Never tag to acknowledge, agree, or say you are ready. If nothing is actionable, do not reply.",
4242
4269
  "Tag the user only when you need a decision or input, or when the task they asked for is finished. Never tag for progress, acknowledgement, or questions the transcript already answers.",
4243
4270
  "Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
4271
+ "Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
4244
4272
  "To send a file, call beeline-agent attach_file with a path inside your checkout; it is attached to your reply.",
4245
4273
  "To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
4246
- "When repository work is needed, you MUST call beeline-agent open_corner with a one-paragraph summary of the complete objective. The host-governed call is the only way to start write work.",
4274
+ "When repository work is needed, you MUST call beeline-agent open_corner with a complete objective of no more than 24 words. The host-governed call is the only way to start write work.",
4247
4275
  "Never claim an action or reply happened unless the prompt or a tool result proves it."
4248
4276
  ].join(" ");
4249
4277
  var BEELINE_DM_CAPABILITIES = [
@@ -4251,6 +4279,7 @@ var BEELINE_DM_CAPABILITIES = [
4251
4279
  "This Room is strictly conversational: there is no repository binding and no corner can be opened from here.",
4252
4280
  "The repository filesystem is read-only in this session.",
4253
4281
  "Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
4282
+ "Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
4254
4283
  "To send a file, call beeline-agent attach_file with a path inside your checkout; it is attached to your reply.",
4255
4284
  "Tag the person only when you need a decision or input, or when the task they asked for is finished.",
4256
4285
  "Never claim an action or reply happened unless the prompt or a tool result proves it."
@@ -4494,6 +4523,39 @@ var PI_CUSTOM_MODEL_CONFIG = {
4494
4523
  source: ".pi/agent/models.json",
4495
4524
  target: "models.json"
4496
4525
  };
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
+ }
4497
4559
  var CODEX_ROOM_AGENT_LOCKDOWN_TOML = "[agents]\nenabled = false\n";
4498
4560
  var CODEX_ROOM_WEB_SEARCH_TOML = "[features]\nstandalone_web_search = true\n";
4499
4561
  var HOME_SUBDIRS = ["user", "claude", "codex", "grok", "pi", "state", "cache", "tmp"];
@@ -4593,7 +4655,8 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
4593
4655
  try {
4594
4656
  const sourceStats = await lstat(source).catch(() => void 0);
4595
4657
  if (!sourceStats) {
4596
- await unlink(target).catch(() => void 0);
4658
+ await writeIsolatedHarnessFile(target, `${JSON.stringify(withReliableOpenRouterRouting({}), null, 2)}
4659
+ `);
4597
4660
  return;
4598
4661
  }
4599
4662
  if (!sourceStats.isFile() || sourceStats.isSymbolicLink() || sourceStats.nlink !== 1) {
@@ -4603,7 +4666,9 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
4603
4666
  if (resolvedSource !== source) {
4604
4667
  throw new AgentHomeSecurityError(`Pi custom model config resolves through a link: ${source}`);
4605
4668
  }
4606
- await writeIsolatedHarnessFile(target, readFileSync4(resolvedSource, "utf8"));
4669
+ const sourceValue = JSON.parse(readFileSync4(resolvedSource, "utf8"));
4670
+ await writeIsolatedHarnessFile(target, `${JSON.stringify(withReliableOpenRouterRouting(sourceValue), null, 2)}
4671
+ `);
4607
4672
  } catch (error) {
4608
4673
  await unlink(target).catch(() => void 0);
4609
4674
  if (failClosed)
@@ -4829,6 +4894,88 @@ function harnessStateDirsFromEnv(env) {
4829
4894
  return { stateDirs, ...tmp ? { tmpDir: resolve6(tmp) } : {} };
4830
4895
  }
4831
4896
 
4897
+ // apps/body/dist/attachment-delivery.js
4898
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
4899
+ import { basename as basename4, extname, join as join2 } from "node:path";
4900
+ var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
4901
+ var FETCH_TIMEOUT_MS = 3e4;
4902
+ function safeFileName(attachment, index, taken) {
4903
+ const raw = basename4(attachment.name ?? "").replace(/[^\w.-]+/g, "_").replace(/^\.+/, "");
4904
+ let name = raw || `attachment-${index + 1}`;
4905
+ if (taken.has(name)) {
4906
+ const ext = extname(name);
4907
+ name = `${name.slice(0, name.length - ext.length)}-${index + 1}${ext}`;
4908
+ }
4909
+ taken.add(name);
4910
+ return name;
4911
+ }
4912
+ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
4913
+ if (!attachments.length)
4914
+ return [];
4915
+ const taken = /* @__PURE__ */ new Set();
4916
+ await mkdir2(dir, { recursive: true });
4917
+ return Promise.all(attachments.map(async (attachment, index) => {
4918
+ const tooLarge = (bytes) => ({
4919
+ attachment,
4920
+ reason: `skipped: ${bytes} bytes exceeds the ${MAX_ATTACHMENT_BYTES}-byte limit`
4921
+ });
4922
+ if (attachment.size && attachment.size > MAX_ATTACHMENT_BYTES)
4923
+ return tooLarge(attachment.size);
4924
+ try {
4925
+ const response = await fetchImpl(attachment.url, {
4926
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
4927
+ });
4928
+ if (!response.ok)
4929
+ throw new Error(`HTTP ${response.status}`);
4930
+ const declared = Number(response.headers.get("content-length") ?? 0);
4931
+ if (declared > MAX_ATTACHMENT_BYTES)
4932
+ return tooLarge(declared);
4933
+ const bytes = Buffer.from(await response.arrayBuffer());
4934
+ if (bytes.length > MAX_ATTACHMENT_BYTES)
4935
+ return tooLarge(bytes.length);
4936
+ const path = join2(dir, safeFileName(attachment, index, taken));
4937
+ await writeFile2(path, bytes);
4938
+ const mimeType = attachment.mimeType ?? response.headers.get("content-type") ?? "";
4939
+ return {
4940
+ attachment,
4941
+ path,
4942
+ ...mimeType.startsWith("image/") ? { image: { data: bytes.toString("base64"), mimeType } } : {}
4943
+ };
4944
+ } catch (error) {
4945
+ return {
4946
+ attachment,
4947
+ reason: `download failed: ${error instanceof Error ? error.message : String(error)}`
4948
+ };
4949
+ }
4950
+ }));
4951
+ }
4952
+ function withoutImageData(delivered) {
4953
+ return delivered.map(({ image: _image, ...rest }) => rest);
4954
+ }
4955
+ function attachmentPromptLines(attachments, delivered) {
4956
+ if (!attachments.length)
4957
+ return [];
4958
+ const byUrl = new Map(delivered?.map((entry) => [entry.attachment.url, entry]) ?? []);
4959
+ return [
4960
+ "Attachments shared with this message (read the local file; the trailing URL is only a reference, do not download it):",
4961
+ ...attachments.map((attachment) => {
4962
+ const kind = attachment.mimeType?.startsWith("image/") ? "image" : "file";
4963
+ const metadata = [attachment.mimeType, attachment.size ? `${attachment.size} bytes` : ""].filter(Boolean).join(", ");
4964
+ const entry = byUrl.get(attachment.url);
4965
+ const location = entry?.path ? `local file ${entry.path}` : entry?.reason ?? "no local copy in this session";
4966
+ return `- ${kind}: ${attachment.name ?? "attachment"}${metadata ? ` (${metadata})` : ""}: ${location} (source ${attachment.url})`;
4967
+ })
4968
+ ];
4969
+ }
4970
+ function attachmentImageBlocks(delivered, harnessAcceptsImages) {
4971
+ if (!harnessAcceptsImages)
4972
+ return [];
4973
+ return delivered.flatMap((entry) => entry.image ? [{ type: "image", data: entry.image.data, mimeType: entry.image.mimeType }] : []);
4974
+ }
4975
+ function promptWithImages(text2, images) {
4976
+ return images.length ? [{ type: "text", text: text2 }, ...images] : text2;
4977
+ }
4978
+
4832
4979
  // apps/body/dist/reply-sanitizer.js
4833
4980
  var CODEX_NOTICE = /^(?:⚠(?:️)?\s*)?(?:warning|notice):\s*(?:skill|tool|plugin) descriptions?\b.*\b(?:context budget|budget limit)\b/i;
4834
4981
  var CODEX_CONTINUATION = /^(?:codex can still (?:see|access|read)|(?:use|open|read)\s+\S*skill\.md\b)/i;
@@ -5311,7 +5458,7 @@ function detectBwrapSandbox(options = {}) {
5311
5458
  import { randomBytes as randomBytes5 } from "node:crypto";
5312
5459
  import { execFile } from "node:child_process";
5313
5460
  import { closeSync, openSync } from "node:fs";
5314
- import { mkdir as mkdir2, readFile, readdir as readdir2, rename as rename2, stat, writeFile as writeFile2 } from "node:fs/promises";
5461
+ import { mkdir as mkdir3, readFile, readdir as readdir2, rename as rename2, stat, writeFile as writeFile3 } from "node:fs/promises";
5315
5462
  import { homedir as homedir4 } from "node:os";
5316
5463
  import { dirname as dirname3, resolve as resolve9 } from "node:path";
5317
5464
  import { spawn as spawn2 } from "node:child_process";
@@ -9701,7 +9848,7 @@ function alphabet(letters) {
9701
9848
  };
9702
9849
  }
9703
9850
  // @__NO_SIDE_EFFECTS__
9704
- function join2(separator = "") {
9851
+ function join3(separator = "") {
9705
9852
  astr("join", separator);
9706
9853
  return {
9707
9854
  encode: (from) => {
@@ -9831,8 +9978,8 @@ var base64 = hasBase64Builtin ? {
9831
9978
  decode(s) {
9832
9979
  return decodeBase64Builtin(s, false);
9833
9980
  }
9834
- } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join2(""));
9835
- var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join2(""));
9981
+ } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join3(""));
9982
+ var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join3(""));
9836
9983
  var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
9837
9984
  function bech32Polymod(pre) {
9838
9985
  const b = pre >> 25;
@@ -13888,9 +14035,9 @@ function runtimeAgentCommand(runtime) {
13888
14035
  }
13889
14036
  async function writeRuntimeRecord(runtime) {
13890
14037
  const path = runtimeConfigPath(runtime.supervisorRoot, runtime.agent.publicKey);
13891
- await mkdir2(dirname3(path), { recursive: true, mode: 448 });
14038
+ await mkdir3(dirname3(path), { recursive: true, mode: 448 });
13892
14039
  const staged = `${path}.${process.pid}.tmp`;
13893
- await writeFile2(staged, `${JSON.stringify(runtime, null, 2)}
14040
+ await writeFile3(staged, `${JSON.stringify(runtime, null, 2)}
13894
14041
  `, { mode: 384 });
13895
14042
  await rename2(staged, path);
13896
14043
  return path;
@@ -14029,7 +14176,7 @@ async function stopRuntimeDaemon(path, opts = {}) {
14029
14176
  }
14030
14177
  async function launchRuntimeDaemon(configPath, opts = {}) {
14031
14178
  const directory = dirname3(configPath);
14032
- await mkdir2(directory, { recursive: true, mode: 448 });
14179
+ await mkdir3(directory, { recursive: true, mode: 448 });
14033
14180
  const foreground = opts.foreground === true;
14034
14181
  const output = foreground ? "inherit" : openSync(resolve9(directory, "daemon.log"), "a", 384);
14035
14182
  const entrypoint = opts.entrypoint ?? process.argv[1];
@@ -14053,7 +14200,7 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
14053
14200
  async function removeAgentRuntime(runtime) {
14054
14201
  const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
14055
14202
  const deletedRoot = resolve9(runtime.supervisorRoot, "beeline", "deleted-runtimes");
14056
- await mkdir2(deletedRoot, { recursive: true, mode: 448 });
14203
+ await mkdir3(deletedRoot, { recursive: true, mode: 448 });
14057
14204
  const target = resolve9(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
14058
14205
  await rename2(source, target);
14059
14206
  return target;
@@ -14206,6 +14353,8 @@ var MonolithCornerTurnLoop = class {
14206
14353
  forcedStop = false;
14207
14354
  draftTail = Promise.resolve();
14208
14355
  activityTail = Promise.resolve();
14356
+ /** Session scratch directory attachments are downloaded into (`TMPDIR/beeline-attachments`). */
14357
+ attachmentDir;
14209
14358
  constructor(options) {
14210
14359
  this.options = options;
14211
14360
  this.agent = runtimeIdentity(options.runtime.agent);
@@ -14243,7 +14392,7 @@ var MonolithCornerTurnLoop = class {
14243
14392
  }),
14244
14393
  this.roster()
14245
14394
  ]);
14246
- await mkdir3(this.options.worktreePath, { recursive: true });
14395
+ await mkdir4(this.options.worktreePath, { recursive: true });
14247
14396
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
14248
14397
  root: this.options.config.agentHomeRoot,
14249
14398
  sharedSkills: this.options.config.sharedSkills ?? [],
@@ -14264,8 +14413,9 @@ var MonolithCornerTurnLoop = class {
14264
14413
  }, selection);
14265
14414
  const operatorHome = this.options.config.operatorHome ?? homedir5();
14266
14415
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
14416
+ this.attachmentDir = tmpDir ? join4(tmpDir, "beeline-attachments") : void 0;
14267
14417
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
14268
- await Promise.all(homeStateDirs.map((dir) => mkdir3(dir, { recursive: true })));
14418
+ await Promise.all(homeStateDirs.map((dir) => mkdir4(dir, { recursive: true })));
14269
14419
  const spawnCommand = wrapAgentCommand({
14270
14420
  bwrapPath: this.options.config.bwrapPath,
14271
14421
  spec: {
@@ -14330,6 +14480,7 @@ var MonolithCornerTurnLoop = class {
14330
14480
  "Work normally with the full coding tools. Commit and push only this feature branch. Use gh to open its pull request.",
14331
14481
  "PR-opening turn rule: as soon as a pull request exists, print its full GitHub URL as your final response and end the turn immediately. Do not call pr_checks_status in that same turn and do not wait for checks inside it. Then stay idle until a later corner fact or human message starts another turn.",
14332
14482
  'Never merge because local tests pass or because gh reports passing checks. On a later turn triggered by a server-posted checks-passed note, call beeline-agent pr_checks_status. Merge only when it returns checks="passed", held=false, and approvalPending=false.',
14483
+ "Merge the PR yourself only after the checks-passed event shows every check green; if any check failed or is still running, say exactly which and stop - never merge red.",
14333
14484
  "If any human in this corner says hold or do not merge, do not merge until a later human explicitly resumes it.",
14334
14485
  "Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
14335
14486
  "A human approval in the app asks the server to merge. When approval is pending, wait for the server close request instead of racing it with gh. If checks passed, no hold exists, and no approval is pending, merge the pull request yourself with gh.",
@@ -14355,7 +14506,7 @@ var MonolithCornerTurnLoop = class {
14355
14506
  }
14356
14507
  };
14357
14508
  }
14358
- async prompt(requestId, trigger) {
14509
+ async prompt(requestId, trigger, attachments = []) {
14359
14510
  const { api, cornerId } = this.options;
14360
14511
  await api.execute("postAgentTurnReceipt", {
14361
14512
  agentId: this.agent.publicKey,
@@ -14369,9 +14520,10 @@ var MonolithCornerTurnLoop = class {
14369
14520
  if (this.forcedStop)
14370
14521
  throw new Error("corner turn stopped for daemon handoff");
14371
14522
  this.busy = true;
14372
- const [conversation, roster] = await Promise.all([
14523
+ const [conversation, roster, delivered] = await Promise.all([
14373
14524
  api.execute("getRoomConversation", { roomId: cornerId, limit: 200 }),
14374
- this.roster()
14525
+ this.roster(),
14526
+ this.attachmentDir && attachments.length ? deliverAttachments(attachments, join4(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
14375
14527
  ]);
14376
14528
  const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
14377
14529
  const transcript = conversation.items.slice(-120).map((message) => `${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`).join("\n");
@@ -14381,8 +14533,8 @@ var MonolithCornerTurnLoop = class {
14381
14533
  ${this.options.objective}`,
14382
14534
  transcript ? `Corner transcript:
14383
14535
  ${transcript}` : "",
14384
- `Newest trigger:
14385
- ${trigger}`,
14536
+ [`Newest trigger:
14537
+ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
14386
14538
  "Continue the objective. Obey the PR checks and human hold rules in your session instructions.",
14387
14539
  MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE
14388
14540
  ].filter(Boolean).join("\n\n");
@@ -14439,7 +14591,7 @@ ${trigger}`,
14439
14591
  });
14440
14592
  });
14441
14593
  };
14442
- const result = await this.client.sessionPrompt(sessionId, prompt, 12e4, (_delta, full) => {
14594
+ const result = await this.client.sessionPrompt(sessionId, promptWithImages(prompt, attachmentImageBlocks(delivered, this.client.canPromptWithImages())), 12e4, (_delta, full) => {
14443
14595
  postNarrationSegments(full);
14444
14596
  this.draftTail = this.draftTail.catch(() => void 0).then(() => api.execute("postAgentDraft", {
14445
14597
  agentId: this.agent.publicKey,
@@ -14533,7 +14685,7 @@ ${pullRequest}`,
14533
14685
  });
14534
14686
  if (!authority.member || authority.principalKind !== "human")
14535
14687
  continue;
14536
- await this.prompt(item.id, item.body);
14688
+ await this.prompt(item.id, item.body, item.attachments);
14537
14689
  pollWithoutWait = true;
14538
14690
  continue;
14539
14691
  }
@@ -14574,8 +14726,9 @@ async function wait(ms, signal) {
14574
14726
  }
14575
14727
 
14576
14728
  // apps/body/dist/monolith-room-turn.js
14577
- import { mkdir as mkdir4 } from "node:fs/promises";
14729
+ import { mkdir as mkdir5 } from "node:fs/promises";
14578
14730
  import { homedir as homedir6 } from "node:os";
14731
+ import { join as join5 } from "node:path";
14579
14732
 
14580
14733
  // packages/api-contract/dist/scheduled-prompts.js
14581
14734
  var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
@@ -14641,6 +14794,10 @@ var MonolithRoomTurnLoop = class {
14641
14794
  draftTail = Promise.resolve();
14642
14795
  activeTurn;
14643
14796
  queuedTurns = [];
14797
+ /** Session scratch directory attachments are downloaded into (`TMPDIR/beeline-attachments`). */
14798
+ attachmentDir;
14799
+ /** Local copies already delivered this session, by message id, so transcript renders reuse them. */
14800
+ deliveredAttachments = /* @__PURE__ */ new Map();
14644
14801
  constructor(options) {
14645
14802
  this.options = options;
14646
14803
  this.agent = runtimeIdentity(options.runtime.agent);
@@ -14667,6 +14824,17 @@ var MonolithRoomTurnLoop = class {
14667
14824
  workspaceId: this.options.workspaceId
14668
14825
  });
14669
14826
  }
14827
+ /** Download a message's attachments into the session scratch directory once. */
14828
+ async deliver(item) {
14829
+ if (!item.attachments.length || !this.attachmentDir)
14830
+ return [];
14831
+ const cached = this.deliveredAttachments.get(item.id);
14832
+ if (cached)
14833
+ return cached;
14834
+ const delivered = await deliverAttachments(item.attachments, join5(this.attachmentDir, item.id.replace(/[^\w-]/g, "_")), this.options.fetchImpl);
14835
+ this.deliveredAttachments.set(item.id, withoutImageData(delivered));
14836
+ return delivered;
14837
+ }
14670
14838
  async activate() {
14671
14839
  if (this.client?.isAlive && this.sessionId)
14672
14840
  return this.sessionId;
@@ -14679,7 +14847,7 @@ var MonolithRoomTurnLoop = class {
14679
14847
  this.options.api.execute("getRoomRepositoryState", { roomId: this.options.roomId })
14680
14848
  ]);
14681
14849
  const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
14682
- await mkdir4(this.options.cwd, { recursive: true });
14850
+ await mkdir5(this.options.cwd, { recursive: true });
14683
14851
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
14684
14852
  root: this.options.config.agentHomeRoot,
14685
14853
  sharedSkills: this.options.config.sharedSkills ?? [],
@@ -14695,8 +14863,9 @@ var MonolithRoomTurnLoop = class {
14695
14863
  }, selection);
14696
14864
  const operatorHome = this.options.config.operatorHome ?? homedir6();
14697
14865
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
14866
+ this.attachmentDir = tmpDir ? join5(tmpDir, "beeline-attachments") : void 0;
14698
14867
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
14699
- await Promise.all(homeStateDirs.map((dir) => mkdir4(dir, { recursive: true })));
14868
+ await Promise.all(homeStateDirs.map((dir) => mkdir5(dir, { recursive: true })));
14700
14869
  const spawnCommand = wrapAgentCommand({
14701
14870
  bwrapPath: this.options.config.bwrapPath,
14702
14871
  spec: {
@@ -14716,6 +14885,9 @@ var MonolithRoomTurnLoop = class {
14716
14885
  agentEnv,
14717
14886
  agentCwd: this.options.cwd,
14718
14887
  agentLabel: command,
14888
+ // `bwrapPath` is set only when `detectBwrapSandbox` passed its self-test
14889
+ // (`config.ts`), which is exactly when `wrapAgentCommand` above wraps.
14890
+ osSandbox: Boolean(this.options.config.bwrapPath),
14719
14891
  autoApprovePermissions: false,
14720
14892
  permissionAllowlist: isRoomMcpPermissionRequest
14721
14893
  };
@@ -14796,11 +14968,11 @@ var MonolithRoomTurnLoop = class {
14796
14968
  active.steers.push(item);
14797
14969
  active.steerTail = active.steerTail.catch(() => void 0).then(async () => {
14798
14970
  try {
14799
- const roster = await this.roster();
14971
+ const [roster, delivered] = await Promise.all([this.roster(), this.deliver(item)]);
14800
14972
  const author = roster.members.find((member) => member.identityId === item.authorId)?.name ?? item.authorId.slice(0, 12);
14801
14973
  await this.client.sessionSteer(this.sessionId, [
14802
14974
  `Human steer received while the current turn is running from ${author}:`,
14803
- roomMessagePrompt("", item.body, item.attachments),
14975
+ roomMessagePrompt("", item.body, item.attachments, delivered),
14804
14976
  "Adjust the current work now. Keep the original request and earlier messages as context."
14805
14977
  ].join("\n\n"));
14806
14978
  } catch (error) {
@@ -14829,18 +15001,19 @@ var MonolithRoomTurnLoop = class {
14829
15001
  activity: [{ kind: "thinking", title: "Working", status: "in_progress" }]
14830
15002
  });
14831
15003
  await this.options.scheduler.run(this.options.roomId, this.lifecycle(), async () => {
14832
- const [conversation, roster] = await Promise.all([
15004
+ const [conversation, roster, delivered] = await Promise.all([
14833
15005
  api.execute("getRoomConversation", { roomId: this.options.roomId, limit: 200 }),
14834
- this.roster()
15006
+ this.roster(),
15007
+ this.deliver(item)
14835
15008
  ]);
14836
15009
  const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
14837
- 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)).join("\n");
15010
+ 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");
14838
15011
  const prompt = [
14839
15012
  this.turnInstructionPrefix,
14840
15013
  transcript ? `Room conversation so far:
14841
15014
  ${transcript}` : "",
14842
15015
  `Newest message from ${isScheduledPrompt(item, this.agent.publicKey) ? SCHEDULE_SCHEDULER_NAME : names.get(item.authorId) ?? item.authorId.slice(0, 12)}:`,
14843
- roomMessagePrompt("", item.body, item.attachments),
15016
+ roomMessagePrompt("", item.body, item.attachments, delivered),
14844
15017
  [
14845
15018
  "Write only the substantive Room message you want the human to read.",
14846
15019
  "Do not repeat or paraphrase these instructions.",
@@ -14850,7 +15023,7 @@ ${transcript}` : "",
14850
15023
  ].filter(Boolean).join("\n\n");
14851
15024
  const sessionId = this.sessionId;
14852
15025
  const turnId = item.id;
14853
- let nextPrompt = prompt;
15026
+ let nextPrompt = promptWithImages(prompt, attachmentImageBlocks(delivered, this.client.canPromptWithImages()));
14854
15027
  let result;
14855
15028
  for (; ; ) {
14856
15029
  let promptError;
@@ -14884,7 +15057,7 @@ ${transcript}` : "",
14884
15057
  "The previous run was cancelled because its harness could not accept every live steer.",
14885
15058
  "Resume the same turn. Keep the original request and everything that happened before it was cancelled.",
14886
15059
  "Human messages that arrived after the original request, in transcript order:",
14887
- ...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments)),
15060
+ ...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments, this.deliveredAttachments.get(steerItem.id))),
14888
15061
  "Continue now and answer the updated request without erasing the earlier context."
14889
15062
  ].join("\n\n");
14890
15063
  }
@@ -15004,20 +15177,10 @@ ${transcript}` : "",
15004
15177
  }
15005
15178
  }
15006
15179
  };
15007
- function roomMessagePrompt(author, body, attachments) {
15180
+ function roomMessagePrompt(author, body, attachments, delivered) {
15008
15181
  const message = body.trim() || "(shared attachments)";
15009
15182
  const rendered = author ? `${author}: ${message}` : message;
15010
- if (!attachments.length)
15011
- return rendered;
15012
- return [
15013
- rendered,
15014
- "Attachments available to this turn (use the capability URL when the task requires the file):",
15015
- ...attachments.map((attachment) => {
15016
- const kind = attachment.mimeType?.startsWith("image/") ? "image" : "file";
15017
- const metadata = [attachment.mimeType, attachment.size ? `${attachment.size} bytes` : ""].filter(Boolean).join(", ");
15018
- return `- ${kind}: ${attachment.name ?? "attachment"}${metadata ? ` (${metadata})` : ""}: ${attachment.url}`;
15019
- })
15020
- ].join("\n");
15183
+ return [rendered, ...attachmentPromptLines(attachments, delivered)].join("\n");
15021
15184
  }
15022
15185
  async function wait2(ms, signal) {
15023
15186
  if (signal?.aborted)
@@ -15617,7 +15780,7 @@ var RoomRuntimeCoordinator = class {
15617
15780
  const targetBranch = repository.targetBranch || "main";
15618
15781
  const checkoutId = createHash("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
15619
15782
  const path = resolve10(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
15620
- await mkdir5(dirname4(path), { recursive: true, mode: 448 });
15783
+ await mkdir6(dirname4(path), { recursive: true, mode: 448 });
15621
15784
  const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
15622
15785
  const env = token ? githubGitEnv(token.token) : process.env;
15623
15786
  if (!existsSync4(resolve10(path, ".git"))) {
@@ -15740,8 +15903,8 @@ var RoomRuntimeCoordinator = class {
15740
15903
  const repositoryHash = createHash("sha256").update(remote).digest("hex").slice(0, 24);
15741
15904
  const gitCommonDir = resolve10(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
15742
15905
  const path = resolve10(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
15743
- await mkdir5(dirname4(gitCommonDir), { recursive: true, mode: 448 });
15744
- await mkdir5(dirname4(path), { recursive: true, mode: 448 });
15906
+ await mkdir6(dirname4(gitCommonDir), { recursive: true, mode: 448 });
15907
+ await mkdir6(dirname4(path), { recursive: true, mode: 448 });
15745
15908
  const authEnv = githubGitEnv(input.token);
15746
15909
  if (!existsSync4(resolve10(gitCommonDir, "HEAD"))) {
15747
15910
  await execFileAsync3("git", ["clone", "--bare", remote, gitCommonDir], {
@@ -16068,7 +16231,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
16068
16231
 
16069
16232
  // apps/body/dist/systemd.js
16070
16233
  import { execFile as execFile4 } from "node:child_process";
16071
- import { mkdir as mkdir6, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
16234
+ import { mkdir as mkdir7, readFile as readFile2, writeFile as writeFile4 } from "node:fs/promises";
16072
16235
  import { homedir as homedir7 } from "node:os";
16073
16236
  import { dirname as dirname5, resolve as resolve12 } from "node:path";
16074
16237
  import { setTimeout as sleep } from "node:timers/promises";
@@ -16141,8 +16304,8 @@ async function installAgentService(publicKey, options = {}) {
16141
16304
  const content = agentServiceUnit();
16142
16305
  const existing = await readFile2(path, "utf8").catch(() => "");
16143
16306
  if (existing !== content) {
16144
- await mkdir6(dirname5(path), { recursive: true, mode: 448 });
16145
- await writeFile3(path, content, { mode: 384 });
16307
+ await mkdir7(dirname5(path), { recursive: true, mode: 448 });
16308
+ await writeFile4(path, content, { mode: 384 });
16146
16309
  }
16147
16310
  const run2 = options.run ?? runSystemctl;
16148
16311
  await run2(["daemon-reload"]);
@@ -16297,7 +16460,7 @@ async function runStartCommand(args, interactiveUi) {
16297
16460
  // apps/body/dist/connect-command.js
16298
16461
  import { spawn as spawn5 } from "node:child_process";
16299
16462
  import { createHash as createHash3 } from "node:crypto";
16300
- import { chmod as chmod4, mkdir as mkdir9, readFile as readFile5, unlink as unlink2, writeFile as writeFile6 } from "node:fs/promises";
16463
+ import { chmod as chmod4, mkdir as mkdir10, readFile as readFile5, unlink as unlink2, writeFile as writeFile7 } from "node:fs/promises";
16301
16464
  import { dirname as dirname9, resolve as resolve15 } from "node:path";
16302
16465
  import { stdin as stdin3, stdout as stdout4 } from "node:process";
16303
16466
 
@@ -16609,7 +16772,7 @@ async function pairDevice(grant, options = {}) {
16609
16772
  }
16610
16773
 
16611
16774
  // apps/body/dist/provider-key-store.js
16612
- import { chmod as chmod2, mkdir as mkdir7, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
16775
+ import { chmod as chmod2, mkdir as mkdir8, readFile as readFile3, writeFile as writeFile5 } from "node:fs/promises";
16613
16776
  import { homedir as homedir8 } from "node:os";
16614
16777
  import { dirname as dirname7, resolve as resolve13 } from "node:path";
16615
16778
  var PROVIDER_KEY_ENV_VARS = {
@@ -16645,8 +16808,8 @@ async function readSavedProviderKey(provider, env = process.env) {
16645
16808
  async function saveProviderKey(provider, key, env = process.env) {
16646
16809
  const path = providerKeyStorePath(env);
16647
16810
  const store = { ...await readProviderKeyStore(env), [provider]: key };
16648
- await mkdir7(dirname7(path), { recursive: true, mode: 448 });
16649
- await writeFile4(path, `${JSON.stringify(store, null, 2)}
16811
+ await mkdir8(dirname7(path), { recursive: true, mode: 448 });
16812
+ await writeFile5(path, `${JSON.stringify(store, null, 2)}
16650
16813
  `, { mode: 384 });
16651
16814
  await chmod2(path, 384);
16652
16815
  }
@@ -16929,8 +17092,8 @@ function providerEnvironment(selection) {
16929
17092
  };
16930
17093
  }
16931
17094
  async function writePrivateJson(path, value) {
16932
- await mkdir9(dirname9(path), { recursive: true, mode: 448 });
16933
- await writeFile6(path, `${JSON.stringify(value, null, 2)}
17095
+ await mkdir10(dirname9(path), { recursive: true, mode: 448 });
17096
+ await writeFile7(path, `${JSON.stringify(value, null, 2)}
16934
17097
  `, { mode: 384 });
16935
17098
  await chmod4(path, 384);
16936
17099
  }
@@ -16939,9 +17102,9 @@ async function writeProviderEnv(selection, agentPubkey) {
16939
17102
  if (Object.keys(values).length === 0)
16940
17103
  return void 0;
16941
17104
  const path = resolve15(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
16942
- await mkdir9(dirname9(path), { recursive: true, mode: 448 });
17105
+ await mkdir10(dirname9(path), { recursive: true, mode: 448 });
16943
17106
  const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
16944
- await writeFile6(path, `${contents}
17107
+ await writeFile7(path, `${contents}
16945
17108
  `, { mode: 384 });
16946
17109
  await chmod4(path, 384);
16947
17110
  return path;
@@ -17064,11 +17227,11 @@ init_self_update_manifest();
17064
17227
  // apps/body/dist/managed-update.js
17065
17228
  init_self_update();
17066
17229
  import { spawn as spawn6 } from "node:child_process";
17067
- import { mkdir as mkdir11, rm as rm5, stat as stat2, writeFile as writeFile8 } from "node:fs/promises";
17230
+ import { mkdir as mkdir12, rm as rm5, stat as stat2, writeFile as writeFile9 } from "node:fs/promises";
17068
17231
  import { dirname as dirname11, resolve as resolve17 } from "node:path";
17069
17232
 
17070
17233
  // apps/body/dist/update-rollback-alert.js
17071
- import { mkdir as mkdir10, readFile as readFile6, rename as rename4, writeFile as writeFile7 } from "node:fs/promises";
17234
+ import { mkdir as mkdir11, readFile as readFile6, rename as rename4, writeFile as writeFile8 } from "node:fs/promises";
17072
17235
  import { dirname as dirname10, resolve as resolve16 } from "node:path";
17073
17236
  function updateRollbackAlertPath(runtimeDir) {
17074
17237
  return resolve16(runtimeDir, "update-rollback-alert.json");
@@ -17076,8 +17239,8 @@ function updateRollbackAlertPath(runtimeDir) {
17076
17239
  async function writeAlert(runtimeDir, alert) {
17077
17240
  const path = updateRollbackAlertPath(runtimeDir);
17078
17241
  const staged = `${path}.${process.pid}.tmp`;
17079
- await mkdir10(dirname10(path), { recursive: true });
17080
- await writeFile7(staged, `${JSON.stringify(alert, null, 2)}
17242
+ await mkdir11(dirname10(path), { recursive: true });
17243
+ await writeFile8(staged, `${JSON.stringify(alert, null, 2)}
17081
17244
  `, { mode: 384 });
17082
17245
  await rename4(staged, path);
17083
17246
  }
@@ -17116,11 +17279,11 @@ async function withInstallLock(layout, work, options = {}) {
17116
17279
  const now2 = options.now ?? Date.now;
17117
17280
  const lock = resolve17(layout.releasesRoot, ".state", "install.lock");
17118
17281
  const deadline = now2() + (options.waitMs ?? 1e4);
17119
- await mkdir11(dirname11(lock), { recursive: true });
17282
+ await mkdir12(dirname11(lock), { recursive: true });
17120
17283
  for (; ; ) {
17121
17284
  try {
17122
- await mkdir11(lock);
17123
- await writeFile8(resolve17(lock, "owner"), `${process.pid}
17285
+ await mkdir12(lock);
17286
+ await writeFile9(resolve17(lock, "owner"), `${process.pid}
17124
17287
  ${now2()}
17125
17288
  `, "utf8");
17126
17289
  break;
@@ -17451,7 +17614,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
17451
17614
  });
17452
17615
  if (!accepted)
17453
17616
  return false;
17454
- await writeFile8(resolve17(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
17617
+ await writeFile9(resolve17(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
17455
17618
  readyAt: (options.now ?? Date.now)(),
17456
17619
  loadedRelease,
17457
17620
  functionalProof: options.functionalProof
@@ -17656,7 +17819,7 @@ async function runUpdateCommand(args) {
17656
17819
  init_self_update();
17657
17820
 
17658
17821
  // apps/body/dist/daemon-failure.js
17659
- import { mkdir as mkdir12, readFile as readFile7, rename as rename5, rm as rm6, writeFile as writeFile9 } from "node:fs/promises";
17822
+ import { mkdir as mkdir13, readFile as readFile7, rename as rename5, rm as rm6, writeFile as writeFile10 } from "node:fs/promises";
17660
17823
  import { dirname as dirname12, resolve as resolve18 } from "node:path";
17661
17824
  var DAEMON_FAILURE_LIMIT = 3;
17662
17825
  var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
@@ -17677,8 +17840,8 @@ async function readFailureRecord(runtimeDir) {
17677
17840
  async function writeFailureRecord(runtimeDir, record2) {
17678
17841
  const path = daemonFailurePath(runtimeDir);
17679
17842
  const staged = `${path}.${process.pid}.tmp`;
17680
- await mkdir12(dirname12(path), { recursive: true, mode: 448 });
17681
- await writeFile9(staged, `${JSON.stringify(record2, null, 2)}
17843
+ await mkdir13(dirname12(path), { recursive: true, mode: 448 });
17844
+ await writeFile10(staged, `${JSON.stringify(record2, null, 2)}
17682
17845
  `, { mode: 384 });
17683
17846
  await rename5(staged, path);
17684
17847
  }
@@ -17700,7 +17863,7 @@ async function clearDaemonStartFailures(runtimeDir) {
17700
17863
  }
17701
17864
 
17702
17865
  // apps/body/dist/update-functional-probe.js
17703
- import { mkdir as mkdir13, rm as rm7 } from "node:fs/promises";
17866
+ import { mkdir as mkdir14, rm as rm7 } from "node:fs/promises";
17704
17867
  import { homedir as homedir10 } from "node:os";
17705
17868
  import { resolve as resolve19 } from "node:path";
17706
17869
  var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
@@ -17728,7 +17891,7 @@ async function runUpdateFunctionalProbe(input) {
17728
17891
  const cwd = resolve19(root, "checkout");
17729
17892
  const homeRoot = resolve19(root, "agent-home");
17730
17893
  await rm7(root, { recursive: true, force: true });
17731
- await mkdir13(cwd, { recursive: true, mode: 448 });
17894
+ await mkdir14(cwd, { recursive: true, mode: 448 });
17732
17895
  let client;
17733
17896
  try {
17734
17897
  const agentEnv = {
@@ -17754,7 +17917,7 @@ async function runUpdateFunctionalProbe(input) {
17754
17917
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
17755
17918
  const operatorHome = input.config.operatorHome ?? homedir10();
17756
17919
  const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
17757
- await Promise.all(homeStateDirs.map((dir) => mkdir13(dir, { recursive: true })));
17920
+ await Promise.all(homeStateDirs.map((dir) => mkdir14(dir, { recursive: true })));
17758
17921
  spawnCommand = wrapAgentCommand({
17759
17922
  bwrapPath: input.config.bwrapPath,
17760
17923
  spec: {
@@ -17818,7 +17981,7 @@ async function runUpdateFunctionalProbe(input) {
17818
17981
  }
17819
17982
 
17820
17983
  // apps/body/dist/release-status.js
17821
- import { readFile as readFile8, readdir as readdir3, rename as rename6, writeFile as writeFile10 } from "node:fs/promises";
17984
+ import { readFile as readFile8, readdir as readdir3, rename as rename6, writeFile as writeFile11 } from "node:fs/promises";
17822
17985
  import { resolve as resolve20 } from "node:path";
17823
17986
  var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
17824
17987
  var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
@@ -17838,7 +18001,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
17838
18001
  };
17839
18002
  const target = resolve20(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
17840
18003
  const temporary = `${target}.${status.pid}.tmp`;
17841
- await writeFile10(temporary, `${JSON.stringify(status, null, 2)}
18004
+ await writeFile11(temporary, `${JSON.stringify(status, null, 2)}
17842
18005
  `, { mode: 384 });
17843
18006
  await rename6(temporary, target);
17844
18007
  return status;
@@ -17891,7 +18054,7 @@ async function runStoredDaemon(pathOrPointer) {
17891
18054
  runtime = activated.runtime;
17892
18055
  const daemonApi = activated.client;
17893
18056
  const agent = runtimeAgentCommand(runtime);
17894
- await writeFile11(resolve21(dirname13(configPath), "daemon.pid"), `${process.pid}
18057
+ await writeFile12(resolve21(dirname13(configPath), "daemon.pid"), `${process.pid}
17895
18058
  `, { mode: 384 });
17896
18059
  const env = {
17897
18060
  ...process.env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "repository": {
6
6
  "type": "git",