usebeeline 0.0.40 → 0.0.44

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 +473 -69
  2. package/package.json +1 -1
@@ -489,7 +489,7 @@ function requiredBundlePaths() {
489
489
  }
490
490
  async function stageRelease(layout, manifestUrl, published, opts = {}) {
491
491
  const fetchImpl = opts.fetchImpl ?? fetch;
492
- const log = opts.logger ?? ((line) => console.log(`[body] self-update: ${line}`));
492
+ const log2 = opts.logger ?? ((line) => console.log(`[body] self-update: ${line}`));
493
493
  const releaseId = sanitizeReleaseId(published.commit ?? published.version ?? `release-${Date.now()}`);
494
494
  const releaseDir = join6(layout.releasesRoot, releaseId);
495
495
  const okMarker = join6(releaseDir, ".stage-ok");
@@ -504,7 +504,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
504
504
  await mkdir10(releaseDir, { recursive: true });
505
505
  const tempArchive = join6(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
506
506
  try {
507
- log(`downloading ${published.file}`);
507
+ log2(`downloading ${published.file}`);
508
508
  const response = await fetchImpl(archiveUrlFor(manifestUrl, published.file), {
509
509
  signal: AbortSignal.timeout(10 * 6e4)
510
510
  });
@@ -555,7 +555,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
555
555
  }
556
556
  await writeFile8(okMarker, `${published.sha256}
557
557
  `, "utf8");
558
- log(`staged release ${releaseId} (sha256 verified)`);
558
+ log2(`staged release ${releaseId} (sha256 verified)`);
559
559
  return releaseId;
560
560
  } catch (error) {
561
561
  if (!previouslyVerified)
@@ -955,7 +955,7 @@ var init_self_update = __esm({
955
955
  });
956
956
 
957
957
  // apps/body/dist/cli.js
958
- import { dirname as dirname14, resolve as resolve25 } from "node:path";
958
+ import { dirname as dirname15, resolve as resolve25 } from "node:path";
959
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
 
@@ -2057,6 +2057,49 @@ var MULTISELECT_INSTRUCTIONS = [
2057
2057
  `${styleText2("dim", "Space:")} select`,
2058
2058
  `${styleText2("dim", "Enter:")} confirm`
2059
2059
  ];
2060
+ var log = {
2061
+ message: (s = [], {
2062
+ symbol: e = styleText2("gray", S_BAR),
2063
+ secondarySymbol: r2 = styleText2("gray", S_BAR),
2064
+ output: m2 = process.stdout,
2065
+ spacing: l2 = 1,
2066
+ withGuide: c2
2067
+ } = {}) => {
2068
+ const t2 = [], o = c2 ?? settings.withGuide, f = o ? r2 : "", O = o ? `${e} ` : "", u4 = o ? `${r2} ` : "";
2069
+ for (let i3 = 0; i3 < l2; i3++)
2070
+ t2.push(f);
2071
+ const g2 = Array.isArray(s) ? s : s.split(`
2072
+ `);
2073
+ if (g2.length > 0) {
2074
+ const [i3, ...y] = g2;
2075
+ i3.length > 0 ? t2.push(`${O}${i3}`) : t2.push(o ? e : "");
2076
+ for (const p of y)
2077
+ p.length > 0 ? t2.push(`${u4}${p}`) : t2.push(o ? r2 : "");
2078
+ }
2079
+ m2.write(`${t2.join(`
2080
+ `)}
2081
+ `);
2082
+ },
2083
+ info: (s, e) => {
2084
+ log.message(s, { ...e, symbol: styleText2("blue", S_INFO) });
2085
+ },
2086
+ success: (s, e) => {
2087
+ log.message(s, { ...e, symbol: styleText2("green", S_SUCCESS) });
2088
+ },
2089
+ step: (s, e) => {
2090
+ log.message(s, { ...e, symbol: styleText2("green", S_STEP_SUBMIT) });
2091
+ },
2092
+ warn: (s, e) => {
2093
+ log.message(s, { ...e, symbol: styleText2("yellow", S_WARN) });
2094
+ },
2095
+ /** alias for `log.warn()`. */
2096
+ warning: (s, e) => {
2097
+ log.warn(s, e);
2098
+ },
2099
+ error: (s, e) => {
2100
+ log.message(s, { ...e, symbol: styleText2("red", S_ERROR) });
2101
+ }
2102
+ };
2060
2103
  var cancel = (o = "", t2) => {
2061
2104
  const i3 = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_END)} ` : "";
2062
2105
  i3.write(`${e}${styleText2("red", o)}
@@ -4173,12 +4216,25 @@ async function applyAgentModelSelection(client, sessionId, advertisedOptions, se
4173
4216
  }
4174
4217
 
4175
4218
  // apps/body/dist/model-catalog.js
4219
+ function modelCatalogProbeEnvironment(agent, agentEnv, scratchCwd) {
4220
+ if (agent.kind !== "goose")
4221
+ return agentEnv;
4222
+ return {
4223
+ ...agentEnv,
4224
+ GOOSE_PATH_ROOT: resolve4(scratchCwd, "goose")
4225
+ };
4226
+ }
4227
+ function filterAgentModelCatalog(agent, raw, agentEnv) {
4228
+ const allowed = filterAllowedModelConfigOptions(raw);
4229
+ return agent.kind === "goose" ? allowed : filterModelOptionsByCredentials(allowed, agentEnv);
4230
+ }
4176
4231
  async function withAgentModelCatalog(agent, agentEnv, selection, inspect) {
4177
4232
  const scratchCwd = await mkdtemp(resolve4(tmpdir(), "beeline-pair-model-check-"));
4233
+ const probeEnv = modelCatalogProbeEnvironment(agent, agentEnv, scratchCwd);
4178
4234
  const client = new AcpClient({
4179
4235
  agentCommand: agent.command,
4180
4236
  agentArgs: agentArgsWithModelSelection(agent, selection),
4181
- agentEnv,
4237
+ agentEnv: probeEnv,
4182
4238
  agentCwd: scratchCwd,
4183
4239
  // The wizard probe runs on the human's own machine for a few seconds —
4184
4240
  // inherit the caller's environment so harness launchers resolve (`pi`,
@@ -4191,7 +4247,7 @@ async function withAgentModelCatalog(agent, agentEnv, selection, inspect) {
4191
4247
  await client.start();
4192
4248
  const { sessionId, raw: sessionRaw } = await client.sessionNew({ cwd: scratchCwd });
4193
4249
  const raw = parseAdvertisedConfigOptions(sessionRaw, selection?.model, isGrokAgentCommand(agent));
4194
- const catalog = filterModelOptionsByCredentials(filterAllowedModelConfigOptions(raw), agentEnv);
4250
+ const catalog = filterAgentModelCatalog(agent, raw, probeEnv);
4195
4251
  return await inspect({ client, sessionId, raw, catalog });
4196
4252
  } finally {
4197
4253
  await client.stop().catch(() => void 0);
@@ -4274,7 +4330,7 @@ async function withTimeout(work, timeoutMs, what) {
4274
4330
  }
4275
4331
  }
4276
4332
  async function syncAgentModelCatalog(input) {
4277
- const log = input.log ?? ((line) => console.log(line));
4333
+ const log2 = input.log ?? ((line) => console.log(line));
4278
4334
  const fetchCatalog = input.fetchCatalog ?? fetchAgentModelCatalog;
4279
4335
  const hashPath = resolve5(input.runtimeDir, MODEL_CATALOG_HASH_FILE);
4280
4336
  try {
@@ -4299,10 +4355,10 @@ async function syncAgentModelCatalog(input) {
4299
4355
  });
4300
4356
  await writeFile(hashPath, `${hash}
4301
4357
  `, { mode: 384 });
4302
- log(`[body] model catalog posted: ${catalog.length} axis(es)` + (selection?.model ? `, model ${selection.model}` : "") + (selection?.effort ? `, effort ${selection.effort}` : ""));
4358
+ log2(`[body] model catalog posted: ${catalog.length} axis(es)` + (selection?.model ? `, model ${selection.model}` : "") + (selection?.effort ? `, effort ${selection.effort}` : ""));
4303
4359
  return "posted";
4304
4360
  } catch (error) {
4305
- log(`[body] model catalog not posted this activation: ${error instanceof Error ? error.message : String(error)}`);
4361
+ log2(`[body] model catalog not posted this activation: ${error instanceof Error ? error.message : String(error)}`);
4306
4362
  return "failed";
4307
4363
  }
4308
4364
  }
@@ -4690,6 +4746,7 @@ var BEELINE_ROOM_CAPABILITIES = [
4690
4746
  "To send a file, call beeline-agent attach_file with a path inside your checkout; it is attached to your reply.",
4691
4747
  "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.",
4692
4748
  "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.",
4749
+ "When open_corner succeeds, the server posts the corner card: do not announce or restate the opening. End the turn with nothing more unless the person asked something else.",
4693
4750
  "Never claim an action or reply happened unless the prompt or a tool result proves it."
4694
4751
  ].join(" ");
4695
4752
  var BEELINE_DM_CAPABILITIES = [
@@ -4843,7 +4900,7 @@ function openRouterRoutingFor(providers) {
4843
4900
  only: [...providers],
4844
4901
  order: [...providers],
4845
4902
  allow_fallbacks: true,
4846
- require_parameters: true
4903
+ require_parameters: false
4847
4904
  };
4848
4905
  }
4849
4906
  function openRouterRoutingCacheDir(runtimeDir) {
@@ -5657,6 +5714,52 @@ function sanitizeAgentReply(message) {
5657
5714
  }
5658
5715
  return lines.slice(1).join("\n").trim();
5659
5716
  }
5717
+ var CORNER_OPEN_ECHO = /^(?:(?:ok(?:ay)?|done|sure|great|alright)[,.!:]?\s+)?(?:i(?:'ve| have|'ll| will|'m| am)?\s+)?(?:just\s+)?open(?:ed|ing)?\s+(?:up\s+)?(?:a\s+|the\s+|your\s+)?(?:new\s+|write(?:-enabled)?\s+|repository\s+)*corner\b/iu;
5718
+ var CORNER_OPEN_ECHO_MAX_CHARS = 320;
5719
+ function stripCornerOpenEcho(message) {
5720
+ const visible = message.trim();
5721
+ if (!visible)
5722
+ return "";
5723
+ const paragraphs = visible.split(/\n\s*\n/);
5724
+ const first = paragraphs[0].trim();
5725
+ if (first.length > CORNER_OPEN_ECHO_MAX_CHARS || !CORNER_OPEN_ECHO.test(first))
5726
+ return visible;
5727
+ return paragraphs.slice(1).join("\n\n").trim();
5728
+ }
5729
+ var STATUS_FILLER = new Set("a an the this that it its is are was were be been being has have had do does did now still remains remain remaining ready for review reviewing pr pull request all every both and or so to of in on at as with by no not nothing new further more else needed needs need required action actions change changes changed status state update updates updated waiting wait awaiting pending running progress idle done ok okay good great well fine confirmed confirm already again yet just currently right i we will can should would github remote upstream branch head latest current report reported noted note see looks look like everything nothing appears appear seems seem there here which what from without any".split(/\s+/));
5730
+ var STATUS_SYNONYMS = {
5731
+ passed: ["pass", "passed", "passing", "passes", "green", "succeeded", "success", "successful", "successfully"],
5732
+ failed: ["fail", "failed", "failing", "fails", "red", "broke", "broken", "failure", "failures"],
5733
+ check: ["check", "checks", "ci", "run", "runs", "workflow", "workflows", "build", "builds", "test", "tests", "suite"],
5734
+ started: ["start", "started", "starting", "began", "begun", "kicked", "off", "queued"],
5735
+ merged: ["merge", "merged", "merging", "landed", "closed"]
5736
+ };
5737
+ var STATUS_RESTATEMENT_MAX_CHARS = 200;
5738
+ var STATUS_RESTATEMENT_MAX_SENTENCES = 2;
5739
+ function statusWords(text2) {
5740
+ return (text2.toLowerCase().match(/[\p{L}\p{N}'-]+/gu) ?? []).map((word) => word.replace(/^'+|'+$/g, "").replace(/'s$/, ""));
5741
+ }
5742
+ function isCornerStatusRestatement(reply, systemLines) {
5743
+ const text2 = reply.trim();
5744
+ if (!text2)
5745
+ return true;
5746
+ if (text2.length > STATUS_RESTATEMENT_MAX_CHARS)
5747
+ return false;
5748
+ if (/https?:\/\/\S+/i.test(text2))
5749
+ return false;
5750
+ const sentences = text2.split(/[.!?]+(?:\s+|$)/).filter((part) => part.trim());
5751
+ if (sentences.length > STATUS_RESTATEMENT_MAX_SENTENCES)
5752
+ return false;
5753
+ const admitted = new Set(STATUS_FILLER);
5754
+ for (const line of systemLines) {
5755
+ for (const word of statusWords(line)) {
5756
+ admitted.add(word);
5757
+ for (const synonym of STATUS_SYNONYMS[word] ?? [])
5758
+ admitted.add(synonym);
5759
+ }
5760
+ }
5761
+ return statusWords(text2).every((word) => !word || admitted.has(word));
5762
+ }
5660
5763
 
5661
5764
  // apps/body/dist/turn-failure-reason.js
5662
5765
  var TURN_FAILURE_REASON_MAX = 200;
@@ -6227,6 +6330,26 @@ function isAccountOrProviderRefusal(record2) {
6227
6330
  return [401, 402, 403, 407, 408, 429].includes(record2.status) || record2.status >= 500;
6228
6331
  }
6229
6332
 
6333
+ // apps/body/dist/corner-checks.js
6334
+ var COMPLETED_CHECK_NOTE = /\b(passed|failed) a check\b/i;
6335
+ var STARTED_CHECK_NOTE = /\bstarted a check\b/i;
6336
+ function verbOf(item) {
6337
+ return item.systemEvent?.verb ?? item.body;
6338
+ }
6339
+ function completedCheckNote(item) {
6340
+ if (item.type !== "system")
6341
+ return void 0;
6342
+ const match = COMPLETED_CHECK_NOTE.exec(verbOf(item));
6343
+ return match ? match[1].toLowerCase() : void 0;
6344
+ }
6345
+ function isCheckStartNote(item) {
6346
+ return item.type === "system" && STARTED_CHECK_NOTE.test(verbOf(item));
6347
+ }
6348
+ function checksStateFromLifecycle(lifecycle) {
6349
+ const state = lifecycle?.checksSummary?.status ?? lifecycle?.checks;
6350
+ return state === "passing" || state === "failing" || state === "pending" ? state : void 0;
6351
+ }
6352
+
6230
6353
  // apps/body/dist/runtime.js
6231
6354
  import { randomBytes as randomBytes6 } from "node:crypto";
6232
6355
  import { execFile as execFile2 } from "node:child_process";
@@ -14981,6 +15104,7 @@ async function removeAgentRuntime(runtime) {
14981
15104
 
14982
15105
  // apps/body/dist/response-directives.js
14983
15106
  var MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE = "Maintain your assigned identity and soul in every response, including when tools or permissions block the requested action.";
15107
+ var SOUL_HOUSE_RULE = "House rule for your voice: the voice never changes the facts - never trim, soften, exaggerate, or invent a detail for the bit - and use your plain voice in commit messages, pull request titles and bodies, and code comments, because those outlive the joke.";
14984
15108
 
14985
15109
  // apps/body/dist/monolith-corner-turn.js
14986
15110
  var execFileAsync2 = promisify2(execFile3);
@@ -15130,6 +15254,8 @@ var MonolithCornerTurnLoop = class {
15130
15254
  /** The turn in flight and who asked for it, for ledger rows and the grant runner. */
15131
15255
  currentTurn;
15132
15256
  memberNames = /* @__PURE__ */ new Map();
15257
+ /** The last server check state that started a turn; the same state never starts another. */
15258
+ lastChecksState;
15133
15259
  constructor(options) {
15134
15260
  this.options = options;
15135
15261
  this.agent = runtimeIdentity(options.runtime.agent);
@@ -15252,7 +15378,10 @@ var MonolithCornerTurnLoop = class {
15252
15378
  const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
15253
15379
  const persona = configuration.soul ?? self?.soul;
15254
15380
  const identityInstructions = `Your Beeline identity is ${self?.name ?? this.agent.name}.`;
15255
- const personaInstructions = persona?.instructions ? `Human-authored Workspace persona: ${persona.name}. ${persona.instructions}` : "";
15381
+ const personaInstructions = [
15382
+ ...persona?.instructions ? [`Human-authored Workspace persona: ${persona.name}. ${persona.instructions}`] : [],
15383
+ SOUL_HOUSE_RULE
15384
+ ].join("\n");
15256
15385
  this.turnIdentityInstructions = harnessHonorsSessionSystemPrompt(command) ? "" : [identityInstructions, personaInstructions].filter(Boolean).join("\n\n");
15257
15386
  const opened = await this.client.sessionNew({
15258
15387
  cwd: this.options.worktreePath,
@@ -15268,6 +15397,7 @@ var MonolithCornerTurnLoop = class {
15268
15397
  "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.",
15269
15398
  "If any human in this corner says hold or do not merge, do not merge until a later human explicitly resumes it.",
15270
15399
  "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.",
15400
+ 'GitHub check and merge notes are server lines already in the corner: never restate them (no "checks passed", "CI is green", "PR ready for review"). On a checks turn, say nothing unless you act - a merge or a pushed fix - and then one short line about that.',
15271
15401
  "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.",
15272
15402
  "Never push directly to the target branch. Never merge a different pull request."
15273
15403
  ].filter(Boolean).join("\n\n")
@@ -15291,8 +15421,9 @@ var MonolithCornerTurnLoop = class {
15291
15421
  }
15292
15422
  };
15293
15423
  }
15294
- async prompt(requestId, trigger, attachments = [], requestedById) {
15424
+ async prompt(requestId, trigger, attachments = [], requestedById, restates) {
15295
15425
  const { api, cornerId } = this.options;
15426
+ const spoken = (text2) => restates && isCornerStatusRestatement(text2, restates) ? "" : text2;
15296
15427
  const requester = requestedById ? {
15297
15428
  pubkey: requestedById,
15298
15429
  ...this.memberNames.get(requestedById) ? { name: this.memberNames.get(requestedById) } : {}
@@ -15350,7 +15481,7 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
15350
15481
  const start = narrationPostedChars;
15351
15482
  narrationPostedChars = segmentEnd;
15352
15483
  narrationSegments += 1;
15353
- const trimmed = segment.trim();
15484
+ const trimmed = spoken(segment.trim());
15354
15485
  if (!trimmed)
15355
15486
  return;
15356
15487
  this.activityTail = this.activityTail.catch(() => void 0).then(async () => {
@@ -15407,11 +15538,12 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
15407
15538
  result
15408
15539
  });
15409
15540
  reply = explained.recoveredText ? stripAgentReplyPreamble(explained.recoveredText).trim() : "";
15410
- if (!reply)
15541
+ if (!reply && !(restates && !isAccountOrProviderRefusal(explained.record))) {
15411
15542
  throw new Error(explained.reason);
15543
+ }
15412
15544
  console.warn(`[thin-core] corner ${cornerId} turn ${requestId}: ${explained.reason}`);
15413
15545
  }
15414
- const durableTail = narrationPostedChars > 0 ? stripAgentReplyPreamble(result.agentText.slice(narrationPostedChars)).trim() : reply;
15546
+ const durableTail = spoken(narrationPostedChars > 0 ? stripAgentReplyPreamble(result.agentText.slice(narrationPostedChars)).trim() : reply);
15415
15547
  if (durableTail) {
15416
15548
  await api.execute("postRoomMessage", {
15417
15549
  roomId: cornerId,
@@ -15449,6 +15581,20 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
15449
15581
  this.currentTurn = void 0;
15450
15582
  }
15451
15583
  }
15584
+ /** The server's check state for this head, or the notes' own verdict when the server carries none. */
15585
+ async checksState(notes) {
15586
+ try {
15587
+ const restore = await this.options.api.execute("getCornerRestoreState", {
15588
+ cornerId: this.options.cornerId
15589
+ });
15590
+ const fromServer = checksStateFromLifecycle(restore.lifecycle);
15591
+ if (fromServer)
15592
+ return fromServer;
15593
+ } catch (error) {
15594
+ console.error(`[thin-core] corner ${this.options.cornerId} check state read failed:`, error);
15595
+ }
15596
+ return notes.some((note) => completedCheckNote(note) === "failed") ? "failing" : "passing";
15597
+ }
15452
15598
  async run() {
15453
15599
  const { api, cornerId, signal } = this.options;
15454
15600
  let cursor3 = (await api.execute("getRoomInbox", { roomId: cornerId, startAtLatest: true })).cursor;
@@ -15469,6 +15615,7 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
15469
15615
  await this.options.onCloseRequested();
15470
15616
  return;
15471
15617
  }
15618
+ const checkNotes = [];
15472
15619
  for (const item of inbox.items) {
15473
15620
  if (item.type === "message") {
15474
15621
  if (item.authorId === this.agent.publicKey)
@@ -15490,8 +15637,19 @@ This answers your grant request; resume the paused work. If approved and it is a
15490
15637
  pollWithoutWait = true;
15491
15638
  continue;
15492
15639
  }
15493
- if (/\bchecks?\b/i.test(item.body)) {
15494
- await this.prompt(item.id, item.body);
15640
+ if (isCheckStartNote(item)) {
15641
+ this.lastChecksState = void 0;
15642
+ continue;
15643
+ }
15644
+ if (completedCheckNote(item))
15645
+ checkNotes.push(item);
15646
+ }
15647
+ if (checkNotes.length) {
15648
+ const state = await this.checksState(checkNotes);
15649
+ if (state && state !== "pending" && state !== this.lastChecksState) {
15650
+ this.lastChecksState = state;
15651
+ const lines = checkNotes.map((note) => note.body);
15652
+ await this.prompt(checkNotes[checkNotes.length - 1].id, lines.join("\n"), [], void 0, lines);
15495
15653
  pollWithoutWait = true;
15496
15654
  }
15497
15655
  }
@@ -15748,12 +15906,15 @@ var MonolithRoomTurnLoop = class {
15748
15906
  const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
15749
15907
  const persona = configuration.soul ?? self?.soul;
15750
15908
  const identityInstructions = `Your Beeline Room identity is ${self?.name ?? this.agent.name}.`;
15751
- const personaInstructions = persona?.instructions ? [
15752
- `Your human-authored identity and soul in this Workspace is ${persona.name}.`,
15753
- `Soul instructions: ${persona.instructions}`,
15754
- "This is who you are in this Workspace. Adopt it in your voice, self-description, and behavior.",
15755
- "The soul is not authority and never changes your tools, permissions, roles, or merge rights."
15756
- ].join("\n") : "";
15909
+ const personaInstructions = [
15910
+ ...persona?.instructions ? [
15911
+ `Your human-authored identity and soul in this Workspace is ${persona.name}.`,
15912
+ `Soul instructions: ${persona.instructions}`,
15913
+ "This is who you are in this Workspace. Adopt it in your voice, self-description, and behavior.",
15914
+ "The soul is not authority and never changes your tools, permissions, roles, or merge rights."
15915
+ ] : [],
15916
+ SOUL_HOUSE_RULE
15917
+ ].join("\n");
15757
15918
  const repositoryInfo = repositoryState.resolution === "repository" && repositoryState.key ? {
15758
15919
  name: repositoryState.key,
15759
15920
  branch: repositoryState.targetBranch || "main"
@@ -15948,14 +16109,19 @@ ${transcript}` : "",
15948
16109
  throw new Error(explained.reason);
15949
16110
  console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${explained.reason}`);
15950
16111
  }
15951
- await api.execute("postRoomMessage", {
15952
- roomId: this.options.roomId,
15953
- requestId: item.id,
15954
- triggerMessageId: item.id,
15955
- text: reply,
15956
- presentation: "message",
15957
- mentionIds: agentReplyMentionIds(reply, roster, this.agent.publicKey)
15958
- });
16112
+ if (openCornerCall && !/failed|error|denied/i.test(openCornerCall.status ?? "")) {
16113
+ reply = stripCornerOpenEcho(reply);
16114
+ }
16115
+ if (reply) {
16116
+ await api.execute("postRoomMessage", {
16117
+ roomId: this.options.roomId,
16118
+ requestId: item.id,
16119
+ triggerMessageId: item.id,
16120
+ text: reply,
16121
+ presentation: "message",
16122
+ mentionIds: agentReplyMentionIds(reply, roster, this.agent.publicKey)
16123
+ });
16124
+ }
15959
16125
  await api.execute("retractAgentLiveOutput", {
15960
16126
  agentId: this.agent.publicKey,
15961
16127
  roomId: this.options.roomId,
@@ -17270,6 +17436,9 @@ async function notify(fields) {
17270
17436
  return;
17271
17437
  await execFileAsync4("systemd-notify", fields, { timeout: SYSTEMD_COMMAND_TIMEOUT_MS });
17272
17438
  }
17439
+ async function extendSystemdStartTimeout(ms) {
17440
+ await notify([`EXTEND_TIMEOUT_USEC=${Math.max(0, Math.round(ms)) * 1e3}`]).catch(() => void 0);
17441
+ }
17273
17442
  var SystemdNotifier = class {
17274
17443
  async ready(status) {
17275
17444
  await notify(["--ready", `--status=${status}`]);
@@ -17730,14 +17899,32 @@ function brassEnabled(env, output) {
17730
17899
  return "256";
17731
17900
  return "plain";
17732
17901
  }
17902
+ var BRASS_TRUECOLOR = "\x1B[38;2;215;175;95m";
17903
+ var BRASS_256 = "\x1B[38;5;179m";
17733
17904
  function brass(value, env = process.env, output = stdout4) {
17734
17905
  const level = brassEnabled(env, output);
17735
17906
  if (level === "truecolor")
17736
- return `\x1B[38;2;194;147;60m${value}\x1B[39m`;
17907
+ return `${BRASS_TRUECOLOR}${value}\x1B[39m`;
17737
17908
  if (level === "256")
17738
- return `\x1B[38;5;178m${value}\x1B[39m`;
17909
+ return `${BRASS_256}${value}\x1B[39m`;
17739
17910
  return value;
17740
17911
  }
17912
+ function brassRails(text2, env = process.env, output = stdout4) {
17913
+ const level = brassEnabled(env, output);
17914
+ if (level === "plain")
17915
+ return text2;
17916
+ return text2.replace(/\u001b\[(?:36|96)m/g, level === "truecolor" ? BRASS_TRUECOLOR : BRASS_256);
17917
+ }
17918
+ function paintWizardBrass(output = stdout4) {
17919
+ const original = output.write;
17920
+ const patched = function write(chunk, ...rest) {
17921
+ return original.call(this, typeof chunk === "string" ? brassRails(chunk, process.env, output) : chunk, ...rest);
17922
+ };
17923
+ output.write = patched;
17924
+ return () => {
17925
+ output.write = original;
17926
+ };
17927
+ }
17741
17928
  var clackPrompts = {
17742
17929
  async select(input) {
17743
17930
  clackPromptOutput();
@@ -17762,12 +17949,12 @@ var clackPrompts = {
17762
17949
  }), "Connection cancelled.");
17763
17950
  }
17764
17951
  };
17765
- function connectModelPickerFromAxes(axes, fallbackModel, harness) {
17952
+ function connectModelPickerFromAxes(axes, fallbackModel, harness, rawAxes = axes) {
17766
17953
  const filtered = axes.find((axis) => axis.category === "model");
17767
17954
  if (filtered?.options.length) {
17768
17955
  return { currentValue: filtered.currentValue, options: filtered.options };
17769
17956
  }
17770
- const raw = axes.find((axis) => axis.category === "model" && axis.options.length);
17957
+ const raw = rawAxes.find((axis) => axis.category === "model" && axis.options.length);
17771
17958
  if (raw) {
17772
17959
  return { currentValue: raw.currentValue, options: raw.options };
17773
17960
  }
@@ -17785,7 +17972,7 @@ async function loadConnectModelCatalog(input) {
17785
17972
  ...input.apiKey ? { apiKey: input.apiKey } : {},
17786
17973
  model: defaultConnectModel(input.harness, input.provider)
17787
17974
  }));
17788
- return connectModelPickerFromAxes(catalog.catalog, defaultConnectModel(input.harness, input.provider), input.harness);
17975
+ return connectModelPickerFromAxes(catalog.catalog, defaultConnectModel(input.harness, input.provider), input.harness, catalog.raw);
17789
17976
  }
17790
17977
  async function collectConnectWizard(prompts = clackPrompts, loadModels = loadConnectModelCatalog, keyStore = fileConnectKeyStore, env = process.env, verifyKey = (input) => verifyProviderKey(input)) {
17791
17978
  const harness = await prompts.select({
@@ -17862,25 +18049,11 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
17862
18049
  placeholder: "Type to filter available models\u2026",
17863
18050
  maxItems: 12
17864
18051
  });
17865
- const name = await prompts.text({
17866
- message: brass("Agent name"),
17867
- validate: (value) => {
17868
- if (!value.trim())
17869
- return "Agent name is required";
17870
- return isReasonableAgentName(value) ? void 0 : `Use letters, spaces, hyphens, or apostrophes (${AGENT_NAME_MAX_LENGTH} characters max)`;
17871
- }
17872
- });
17873
- const soul = await prompts.text({
17874
- message: brass("Input soul"),
17875
- validate: (value) => value.trim() ? void 0 : "Soul is required"
17876
- });
17877
18052
  return {
17878
- name: name.trim().replace(/\s+/g, " "),
17879
18053
  harness,
17880
18054
  ...provider ? { provider } : {},
17881
18055
  ...apiKey ? { apiKey: apiKey.trim() } : {},
17882
- model: model.trim(),
17883
- soul: soul.trim()
18056
+ model: model.trim()
17884
18057
  };
17885
18058
  }
17886
18059
  async function jsonRequest(url, body, fetchImpl) {
@@ -17906,11 +18079,19 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl) {
17906
18079
  harness: selection.harness,
17907
18080
  ...selection.provider ? { provider: selection.provider } : {},
17908
18081
  model: selection.model,
17909
- soul: selection.soul,
17910
- avatar_seed: avatarSeed,
17911
- agent_name: selection.name
18082
+ avatar_seed: avatarSeed
17912
18083
  }, fetchImpl);
17913
18084
  }
18085
+ async function renameConnectedAgent(baseUrl, pairingCode, name, fetchImpl) {
18086
+ const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
18087
+ if (!normalizedPairingCode)
18088
+ throw new Error("invalid pairing code");
18089
+ const renamed = await jsonRequest(`${baseUrl}/auth/agent/connect/name`, { pairing_code: normalizedPairingCode, agent_name: name.trim().replace(/\s+/g, " ") }, fetchImpl);
18090
+ return renamed.agent_name;
18091
+ }
18092
+ function seededIdentityLine(grant) {
18093
+ return grant.agent_face ? `${grant.agent_name} the ${grant.agent_face}` : grant.agent_name;
18094
+ }
17914
18095
  async function installCurrentRelease(fetchImpl) {
17915
18096
  const manifestUrl = resolveManifestUrl(process.env);
17916
18097
  const response = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(3e4) });
@@ -18003,15 +18184,23 @@ async function runConnectCommand(code, options = {}) {
18003
18184
  if (!stdin3.isTTY || !stdout4.isTTY) {
18004
18185
  throw new Error("`usebeeline connect` needs an interactive terminal");
18005
18186
  }
18187
+ const restoreRails = paintWizardBrass();
18188
+ try {
18189
+ await runConnectWizard(code, options.fetchImpl ?? fetch);
18190
+ } finally {
18191
+ restoreRails();
18192
+ }
18193
+ }
18194
+ async function runConnectWizard(code, fetchImpl) {
18006
18195
  intro(brass("Beeline connect"));
18007
- const fetchImpl = options.fetchImpl ?? fetch;
18008
18196
  const pairingCode = code?.trim() || await clackPrompts.text({
18009
18197
  message: brass("Pairing code from the app"),
18010
18198
  validate: (value) => normalizeAgentPairingCode(value) ? void 0 : "Enter the pairing code shown in the app"
18011
18199
  });
18012
18200
  const selection = await collectConnectWizard(clackPrompts, loadConnectModelCatalog, fileConnectKeyStore, process.env, (input) => verifyProviderKey({ ...input, fetchImpl }));
18013
18201
  const baseUrl = (process.env.BEELINE_AUTH_URL ?? "https://server.usebeeline.app").replace(/\/$/, "");
18014
- const grant = await brassSpinner("Connecting to your Beeline Workspace\u2026", () => requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl), (connectedGrant) => `Connected to ${connectedGrant.workspace_name}`);
18202
+ const claimed = await brassSpinner("Connecting to your Beeline Workspace\u2026", () => requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl), (connectedGrant) => `Connected to ${connectedGrant.workspace_name}`);
18203
+ const grant = { ...claimed, agent_name: await confirmSeededName(baseUrl, pairingCode, claimed, fetchImpl) };
18015
18204
  const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
18016
18205
  const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
18017
18206
  const grantPath = resolve19(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
@@ -18031,10 +18220,38 @@ async function runConnectCommand(code, options = {}) {
18031
18220
  });
18032
18221
  await brassSpinner("Starting your agent\u2026", () => runInstalledFinish(installedRelease.binary, grantPath), () => `Started ${grant.agent_name}`);
18033
18222
  console.log("");
18034
- console.log(`${brass("Agent")} ${grant.agent_name}`);
18223
+ console.log(`${brass("Agent")} ${seededIdentityLine(grant)}`);
18035
18224
  console.log(`${brass("Workspace")} ${grant.workspace_name}`);
18036
18225
  outro(brass("Say hi in the app."));
18037
18226
  }
18227
+ async function confirmSeededName(baseUrl, pairingCode, grant, fetchImpl, prompts = clackPrompts) {
18228
+ log.info(brass(`Your agent is ${seededIdentityLine(grant)}.`));
18229
+ const choice = await prompts.select({
18230
+ message: brass("Name"),
18231
+ initialValue: "keep",
18232
+ options: [
18233
+ { value: "keep", label: `Keep ${grant.agent_name}`, hint: "default" },
18234
+ { value: "rename", label: "Rename this agent" }
18235
+ ]
18236
+ });
18237
+ if (choice === "keep")
18238
+ return grant.agent_name;
18239
+ const renamed = await prompts.text({
18240
+ message: brass("Agent name"),
18241
+ initialValue: grant.agent_name,
18242
+ validate: (value) => {
18243
+ if (!value.trim())
18244
+ return "Agent name is required";
18245
+ return isReasonableAgentName(value) ? void 0 : `Use letters, spaces, hyphens, or apostrophes (${AGENT_NAME_MAX_LENGTH} characters max)`;
18246
+ }
18247
+ });
18248
+ try {
18249
+ return await renameConnectedAgent(baseUrl, pairingCode, renamed, fetchImpl);
18250
+ } catch (error) {
18251
+ log.warn(brass(`Could not rename this agent (${error instanceof Error ? error.message : String(error)}); it stays ${grant.agent_name}. Rename it in the app.`));
18252
+ return grant.agent_name;
18253
+ }
18254
+ }
18038
18255
  function isDevicePairingGrant(value) {
18039
18256
  if (!value || typeof value !== "object")
18040
18257
  return false;
@@ -18818,12 +19035,36 @@ var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
18818
19035
  var UpdateFunctionalProbeError = class extends Error {
18819
19036
  reason;
18820
19037
  code = "BEELINE_UPDATE_FUNCTIONAL_PROBE_FAILED";
19038
+ /** Present when the turn failed on a provider refusal with an HTTP status. */
19039
+ providerRefusal;
18821
19040
  constructor(reason, detail, options) {
18822
19041
  super(`functional update probe failed (${reason}): ${detail}`, options);
18823
19042
  this.reason = reason;
18824
19043
  this.name = "UpdateFunctionalProbeError";
19044
+ this.providerRefusal = options?.providerRefusal;
18825
19045
  }
18826
19046
  };
19047
+ async function probeOutcome(run2) {
19048
+ try {
19049
+ await run2();
19050
+ return { kind: "served" };
19051
+ } catch (error) {
19052
+ if (error instanceof UpdateFunctionalProbeError && error.providerRefusal) {
19053
+ return { kind: "refused", ...error.providerRefusal };
19054
+ }
19055
+ return { kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
19056
+ }
19057
+ }
19058
+ function describeCurrentReleaseOutcome(outcome) {
19059
+ switch (outcome.kind) {
19060
+ case "served":
19061
+ return "the current release answered";
19062
+ case "refused":
19063
+ return `the current release got a different refusal (${outcome.reason})`;
19064
+ case "unavailable":
19065
+ return `the current release could not be compared (${outcome.reason})`;
19066
+ }
19067
+ }
18827
19068
  async function runUpdateFunctionalProbe(input) {
18828
19069
  const command = input.config.agentCommand ?? input.config.agentBinary;
18829
19070
  const harness = input.config.agentKind ?? command;
@@ -18833,7 +19074,7 @@ async function runUpdateFunctionalProbe(input) {
18833
19074
  if (input.sandboxRequired && !input.config.bwrapPath) {
18834
19075
  throw new UpdateFunctionalProbeError("sandbox-unavailable", "the configured bubblewrap boundary did not pass its startup self-test");
18835
19076
  }
18836
- const root = resolve23(input.runtimeDir, "update-functional-probe");
19077
+ const root = input.probeRoot ?? resolve23(input.runtimeDir, "update-functional-probe");
18837
19078
  const cwd = resolve23(root, "checkout");
18838
19079
  const homeRoot = resolve23(root, "agent-home");
18839
19080
  await rm7(root, { recursive: true, force: true });
@@ -18915,12 +19156,28 @@ async function runUpdateFunctionalProbe(input) {
18915
19156
  });
18916
19157
  const modelSide = isAccountOrProviderRefusal(explained.record) || explained.record?.kind === "empty";
18917
19158
  if (!modelSide) {
18918
- throw new UpdateFunctionalProbeError("turn-failed", `the harness completed a session/prompt without an agent answer: ${explained.reason}`);
19159
+ const refusal = explained.record?.kind === "error" && explained.record.status !== void 0 ? { status: explained.record.status, reason: explained.record.reason } : void 0;
19160
+ const detail = `the harness completed a session/prompt without an agent answer: ${explained.reason}`;
19161
+ if (!refusal || !input.compareWithCurrentRelease) {
19162
+ throw new UpdateFunctionalProbeError("turn-failed", detail, {
19163
+ ...refusal ? { providerRefusal: refusal } : {}
19164
+ });
19165
+ }
19166
+ const current = await input.compareWithCurrentRelease(refusal);
19167
+ if (current.kind !== "refused" || current.status !== refusal.status) {
19168
+ throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { providerRefusal: refusal });
19169
+ }
19170
+ const reason = `${explained.reason} (the current release gets the same ${refusal.status})`;
19171
+ console.warn(`[body] update probe: the provider refused this release and the current release alike (${refusal.reason}); that refusal is not this bundle's doing, so the probe passes as inconclusive`);
19172
+ modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: reason };
19173
+ } else {
19174
+ 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`);
19175
+ modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: explained.reason };
18919
19176
  }
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 };
18922
19177
  }
18923
19178
  } catch (error) {
19179
+ if (error instanceof UpdateFunctionalProbeError)
19180
+ throw error;
18924
19181
  throw new UpdateFunctionalProbeError("turn-failed", error instanceof Error ? error.message : String(error), { cause: error });
18925
19182
  }
18926
19183
  } catch (error) {
@@ -18942,6 +19199,137 @@ async function runUpdateFunctionalProbe(input) {
18942
19199
  }
18943
19200
  }
18944
19201
 
19202
+ // apps/body/dist/current-release-probe.js
19203
+ import { spawn as spawn7 } from "node:child_process";
19204
+ import { dirname as dirname14, join as join7 } from "node:path";
19205
+ init_self_update();
19206
+ var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 8e4;
19207
+ var UPDATE_PROBE_COMMAND = "update-probe";
19208
+ function parseReport(line) {
19209
+ let parsed;
19210
+ try {
19211
+ parsed = JSON.parse(line);
19212
+ } catch {
19213
+ return void 0;
19214
+ }
19215
+ if (!parsed || typeof parsed !== "object")
19216
+ return void 0;
19217
+ const report = parsed;
19218
+ if (report.probe === "served")
19219
+ return { probe: "served" };
19220
+ if (report.probe === "refused" && typeof report.status === "number" && typeof report.reason === "string") {
19221
+ return { probe: "refused", status: report.status, reason: report.reason };
19222
+ }
19223
+ if (report.probe === "failed" && typeof report.reason === "string") {
19224
+ return { probe: "failed", reason: report.reason };
19225
+ }
19226
+ return void 0;
19227
+ }
19228
+ function outcomeFromReport(report) {
19229
+ switch (report.probe) {
19230
+ case "served":
19231
+ return { kind: "served" };
19232
+ case "refused":
19233
+ return { kind: "refused", status: report.status, reason: report.reason };
19234
+ case "failed":
19235
+ return { kind: "unavailable", reason: report.reason };
19236
+ }
19237
+ }
19238
+ async function probeReleaseInSubprocess(input) {
19239
+ const bundleDir = join7(input.layout.releasesRoot, input.releaseId);
19240
+ const entrypoint = await resolveBundleEntrypoint(bundleDir);
19241
+ if (!entrypoint) {
19242
+ return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
19243
+ }
19244
+ const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
19245
+ return new Promise((resolve26) => {
19246
+ const child = spawn7(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
19247
+ let stdout6 = "";
19248
+ let stderr = "";
19249
+ let settled = false;
19250
+ const finish = (outcome) => {
19251
+ if (settled)
19252
+ return;
19253
+ settled = true;
19254
+ clearTimeout(timer);
19255
+ resolve26(outcome);
19256
+ };
19257
+ const timer = setTimeout(() => {
19258
+ child.kill("SIGKILL");
19259
+ finish({
19260
+ kind: "unavailable",
19261
+ reason: `release ${input.releaseId} did not finish its probe within ${timeoutMs}ms`
19262
+ });
19263
+ }, timeoutMs);
19264
+ child.stdout.on("data", (chunk) => {
19265
+ stdout6 += chunk.toString();
19266
+ });
19267
+ child.stderr.on("data", (chunk) => {
19268
+ stderr += chunk.toString();
19269
+ });
19270
+ child.on("error", (error) => {
19271
+ finish({ kind: "unavailable", reason: `release ${input.releaseId} could not be spawned: ${error.message}` });
19272
+ });
19273
+ child.on("close", (code, signal) => {
19274
+ const lines = stdout6.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
19275
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
19276
+ const report = parseReport(lines[index]);
19277
+ if (report) {
19278
+ finish(outcomeFromReport(report));
19279
+ return;
19280
+ }
19281
+ }
19282
+ const tail = stderr.trim().split(/\r?\n/).slice(-3).join(" | ").slice(0, 300);
19283
+ finish({
19284
+ kind: "unavailable",
19285
+ reason: `release ${input.releaseId} printed no probe report (exit ${signal ?? code})` + (tail ? `: ${tail}` : "")
19286
+ });
19287
+ });
19288
+ });
19289
+ }
19290
+ async function runUpdateProbeCommand(args, options = {}) {
19291
+ const env = options.env ?? process.env;
19292
+ const write = options.write ?? ((line) => process.stdout.write(`${line}
19293
+ `));
19294
+ const configFlag = args.indexOf("--config");
19295
+ const configArg = configFlag >= 0 ? args[configFlag + 1] : void 0;
19296
+ if (!configArg)
19297
+ throw new Error(`${UPDATE_PROBE_COMMAND} requires --config <runtime.json>`);
19298
+ const configPath = await resolveRuntimeConfigPath(configArg);
19299
+ const runtime = await readRuntimeRecord(configPath);
19300
+ const agent = runtimeAgentCommand(runtime);
19301
+ const config = loadBodyConfig({
19302
+ workspaceRoot: join7(dirname14(configPath), "workspace"),
19303
+ llmEnvFile: runtime.llmEnvFile,
19304
+ env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
19305
+ agent
19306
+ });
19307
+ if (runtime.sharedSkills)
19308
+ config.sharedSkills = [...runtime.sharedSkills];
19309
+ if (runtime.modelSelection)
19310
+ config.modelSelection = runtime.modelSelection;
19311
+ config.runtimeConfigPath = configPath;
19312
+ const sandbox = detectBwrapSandbox({ ...runtime.sandbox ? { policy: runtime.sandbox } : {}, env });
19313
+ if (sandbox.path)
19314
+ config.bwrapPath = sandbox.path;
19315
+ if (runtime.sandboxMaskPaths?.length) {
19316
+ config.sandboxMaskPaths = [...config.sandboxMaskPaths ?? [], ...runtime.sandboxMaskPaths];
19317
+ }
19318
+ const layout = beelineInstallLayout(env);
19319
+ const releaseId = (layout && await activeReleaseId(layout).catch(() => void 0)) ?? "unknown";
19320
+ const runtimeDir = dirname14(configPath);
19321
+ const outcome = await probeOutcome(() => (options.probe ?? runUpdateFunctionalProbe)({
19322
+ config,
19323
+ runtimeDir,
19324
+ releaseId,
19325
+ sandboxRequired: runtime.sandbox !== "off",
19326
+ // The successor's probe still holds `<runtimeDir>/update-functional-probe`.
19327
+ probeRoot: join7(runtimeDir, "current-release-probe")
19328
+ }));
19329
+ const report = outcome.kind === "served" ? { probe: "served" } : outcome.kind === "refused" ? { probe: "refused", status: outcome.status, reason: outcome.reason } : { probe: "failed", reason: outcome.reason };
19330
+ write(JSON.stringify(report));
19331
+ }
19332
+
18945
19333
  // apps/body/dist/release-status.js
18946
19334
  import { readFile as readFile12, readdir as readdir4, rename as rename6, writeFile as writeFile13 } from "node:fs/promises";
18947
19335
  import { resolve as resolve24 } from "node:path";
@@ -19004,7 +19392,7 @@ var DaemonExitError = class extends Error {
19004
19392
  };
19005
19393
  async function runStoredDaemon(pathOrPointer) {
19006
19394
  const configPath = await resolveRuntimeConfigPath(pathOrPointer);
19007
- daemonFailureRuntimeDir = dirname14(configPath);
19395
+ daemonFailureRuntimeDir = dirname15(configPath);
19008
19396
  const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
19009
19397
  let runtime = accessMigration.runtime;
19010
19398
  if (!runtime.transport) {
@@ -19016,7 +19404,7 @@ async function runStoredDaemon(pathOrPointer) {
19016
19404
  runtime = activated.runtime;
19017
19405
  const daemonApi = activated.client;
19018
19406
  const agent = runtimeAgentCommand(runtime);
19019
- await writeFile14(resolve25(dirname14(configPath), "daemon.pid"), `${process.pid}
19407
+ await writeFile14(resolve25(dirname15(configPath), "daemon.pid"), `${process.pid}
19020
19408
  `, { mode: 384 });
19021
19409
  const env = {
19022
19410
  ...process.env,
@@ -19024,7 +19412,7 @@ async function runStoredDaemon(pathOrPointer) {
19024
19412
  BUZZ_DEV_MCP_BIN: runtime.mcpBinary
19025
19413
  };
19026
19414
  const config = loadBodyConfig({
19027
- workspaceRoot: resolve25(dirname14(configPath), "workspace"),
19415
+ workspaceRoot: resolve25(dirname15(configPath), "workspace"),
19028
19416
  llmEnvFile: runtime.llmEnvFile,
19029
19417
  env,
19030
19418
  agent
@@ -19059,7 +19447,7 @@ async function runStoredDaemon(pathOrPointer) {
19059
19447
  const stop = () => controller.abort();
19060
19448
  process.once("SIGINT", stop);
19061
19449
  process.once("SIGTERM", stop);
19062
- const runtimeDir = dirname14(configPath);
19450
+ const runtimeDir = dirname15(configPath);
19063
19451
  const layout = beelineInstallLayout(process.env);
19064
19452
  const notifier = new SystemdNotifier();
19065
19453
  let rollbackAlertDrain;
@@ -19126,6 +19514,7 @@ async function runStoredDaemon(pathOrPointer) {
19126
19514
  onEstablished: async () => {
19127
19515
  let functionalProof;
19128
19516
  if (layout && pendingSuccessor) {
19517
+ const currentReleaseId = (await readUpdateAttempt(layout))?.previousReleaseId;
19129
19518
  const gate = await gateManagedSuccessor({
19130
19519
  layout,
19131
19520
  runtimeDir,
@@ -19135,7 +19524,18 @@ async function runStoredDaemon(pathOrPointer) {
19135
19524
  config,
19136
19525
  runtimeDir,
19137
19526
  releaseId: loadedRelease ?? "unknown",
19138
- sandboxRequired: runtime.sandbox !== "off"
19527
+ sandboxRequired: runtime.sandbox !== "off",
19528
+ ...currentReleaseId ? {
19529
+ compareWithCurrentRelease: async (refusal) => {
19530
+ console.warn(`[thin-core] successor probe refused by the provider (${refusal.reason}); probing the current release ${currentReleaseId} for the same refusal`);
19531
+ await extendSystemdStartTimeout(CURRENT_RELEASE_PROBE_TIMEOUT_MS + 15e3);
19532
+ return probeReleaseInSubprocess({
19533
+ layout,
19534
+ releaseId: currentReleaseId,
19535
+ runtimeConfigPath: configPath
19536
+ });
19537
+ }
19538
+ } : {}
19139
19539
  })
19140
19540
  });
19141
19541
  if (gate.kind === "failed") {
@@ -19185,7 +19585,7 @@ async function runStoredDaemon(pathOrPointer) {
19185
19585
  throw error;
19186
19586
  } finally {
19187
19587
  await notifier.stopping(stoppingStatus).catch(() => void 0);
19188
- const pidPath = resolve25(dirname14(configPath), "daemon.pid");
19588
+ const pidPath = resolve25(dirname15(configPath), "daemon.pid");
19189
19589
  const recorded = Number((await readFile13(pidPath, "utf8").catch(() => "")).trim());
19190
19590
  if (recorded === process.pid) {
19191
19591
  await unlink3(pidPath).catch(() => void 0);
@@ -19242,7 +19642,7 @@ async function main() {
19242
19642
  const agentPubkey = agentFlag >= 0 ? args[agentFlag + 1] : void 0;
19243
19643
  if (!configPath && agentPubkey) {
19244
19644
  const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
19245
- configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
19645
+ configPath = configs.find((candidate) => dirname15(candidate).endsWith(agentPubkey));
19246
19646
  }
19247
19647
  if (!configPath && agentPubkey) {
19248
19648
  throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
@@ -19256,6 +19656,10 @@ async function main() {
19256
19656
  await runUpdateCommand(args);
19257
19657
  return;
19258
19658
  }
19659
+ if (command === UPDATE_PROBE_COMMAND) {
19660
+ await runUpdateProbeCommand(args);
19661
+ return;
19662
+ }
19259
19663
  if (command === "start") {
19260
19664
  await runStartCommand(args, interactiveUi);
19261
19665
  return;
@@ -19266,7 +19670,7 @@ async function main() {
19266
19670
  if (!agentPubkey)
19267
19671
  throw new Error("stop requires --agent <pubkey>");
19268
19672
  const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
19269
- const configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
19673
+ const configPath = configs.find((candidate) => dirname15(candidate).endsWith(agentPubkey));
19270
19674
  if (!configPath)
19271
19675
  throw new Error(`no stored runtime found for agent ${agentPubkey}`);
19272
19676
  const runtime = await readRuntimeRecord(configPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.40",
3
+ "version": "0.0.44",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "repository": {
6
6
  "type": "git",