usebeeline 0.1.9 → 0.1.11

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.
@@ -212,8 +212,8 @@ async function runSquireBrokerLink(options) {
212
212
  const pending = /* @__PURE__ */ new Map();
213
213
  const queued = [];
214
214
  let resolveDone;
215
- const done = new Promise((resolve42) => {
216
- resolveDone = resolve42;
215
+ const done = new Promise((resolve43) => {
216
+ resolveDone = resolve43;
217
217
  });
218
218
  const failResponse = (id) => `${JSON.stringify({
219
219
  jsonrpc: "2.0",
@@ -13834,10 +13834,10 @@ function squireLoginWallIn(value, depth = 0) {
13834
13834
  if (!item)
13835
13835
  return void 0;
13836
13836
  const needsUser = record(item.needs_user);
13837
- if (needsUser?.wall === "google_session") {
13837
+ if (needsUser?.wall === "google_session" || needsUser?.wall === "oauth_sign_in") {
13838
13838
  const message = shortString(needsUser.message);
13839
13839
  if (message)
13840
- return { wall: "google_session", message };
13840
+ return { wall: needsUser.wall, message };
13841
13841
  }
13842
13842
  const oauth = record(item.oauth);
13843
13843
  if (oauth?.state === "awaiting_human") {
@@ -14126,7 +14126,7 @@ function configureNetworkFamilyDefaults(network = net) {
14126
14126
  configureNetworkFamilyDefaults();
14127
14127
 
14128
14128
  // apps/body/dist/cli.js
14129
- import { dirname as dirname30, resolve as resolve41 } from "node:path";
14129
+ import { dirname as dirname30, resolve as resolve42 } from "node:path";
14130
14130
  import { stdin as stdin4, stdout as stdout5 } from "node:process";
14131
14131
 
14132
14132
  // node_modules/@clack/core/dist/index.mjs
@@ -15566,7 +15566,7 @@ function parseCursorModelsOutput(output) {
15566
15566
  };
15567
15567
  }
15568
15568
  async function enumerateCursorModels(env = process.env) {
15569
- return new Promise((resolve42) => {
15569
+ return new Promise((resolve43) => {
15570
15570
  const child = spawn2("cursor-agent", ["models"], {
15571
15571
  env,
15572
15572
  stdio: ["ignore", "pipe", "ignore"]
@@ -15580,11 +15580,11 @@ async function enumerateCursorModels(env = process.env) {
15580
15580
  });
15581
15581
  child.on("error", () => {
15582
15582
  clearTimeout(timer);
15583
- resolve42(void 0);
15583
+ resolve43(void 0);
15584
15584
  });
15585
15585
  child.on("close", (code) => {
15586
15586
  clearTimeout(timer);
15587
- resolve42(code === 0 ? parseCursorModelsOutput(output) : void 0);
15587
+ resolve43(code === 0 ? parseCursorModelsOutput(output) : void 0);
15588
15588
  });
15589
15589
  });
15590
15590
  }
@@ -16011,13 +16011,13 @@ var CursorAcpServer = class {
16011
16011
  consumeLine(line);
16012
16012
  });
16013
16013
  try {
16014
- const { code, signal } = await new Promise((resolve42) => {
16014
+ const { code, signal } = await new Promise((resolve43) => {
16015
16015
  let settled = false;
16016
16016
  const finish = (exitCode, exitSignal) => {
16017
16017
  if (settled)
16018
16018
  return;
16019
16019
  settled = true;
16020
- resolve42({ code: exitCode, signal: exitSignal });
16020
+ resolve43({ code: exitCode, signal: exitSignal });
16021
16021
  };
16022
16022
  child.once("error", (error) => {
16023
16023
  spawnError = error instanceof Error ? error.message : String(error);
@@ -16162,10 +16162,10 @@ function releaseSquireConnectSession(log2, owner = "") {
16162
16162
  function squireChromeProfileDir() {
16163
16163
  return process.env.TRUSTY_SQUIRE_PROFILE_DIR ?? squireHostPaths(homedir2()).profileDir;
16164
16164
  }
16165
- var defaultShellRunner = (command, args) => new Promise((resolve42) => {
16165
+ var defaultShellRunner = (command, args) => new Promise((resolve43) => {
16166
16166
  execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
16167
16167
  const code = error?.code;
16168
- resolve42({
16168
+ resolve43({
16169
16169
  code: typeof code === "number" ? code : error ? 1 : 0,
16170
16170
  stdout: String(stdout6 ?? ""),
16171
16171
  stderr: String(stderr ?? "")
@@ -16190,7 +16190,7 @@ function killConnectTree(child) {
16190
16190
  }
16191
16191
  child.kill();
16192
16192
  }
16193
- var defaultStreamedRunner = (command, args, env, owner = "") => new Promise((resolve42) => {
16193
+ var defaultStreamedRunner = (command, args, env, owner = "") => new Promise((resolve43) => {
16194
16194
  const child = spawn5(command, args, {
16195
16195
  stdio: ["ignore", "pipe", "pipe"],
16196
16196
  env: env ?? squireConnectProcessEnv(),
@@ -16204,7 +16204,7 @@ var defaultStreamedRunner = (command, args, env, owner = "") => new Promise((res
16204
16204
  const safetyTimer = setTimeout(() => {
16205
16205
  if (!resolved) {
16206
16206
  resolved = true;
16207
- resolve42({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
16207
+ resolve43({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
16208
16208
  } });
16209
16209
  }
16210
16210
  killConnectTree(child);
@@ -16239,7 +16239,7 @@ var defaultStreamedRunner = (command, args, env, owner = "") => new Promise((res
16239
16239
  const finish = (result) => {
16240
16240
  if (!resolved) {
16241
16241
  resolved = true;
16242
- resolve42(result);
16242
+ resolve43(result);
16243
16243
  }
16244
16244
  };
16245
16245
  const settle = (result) => {
@@ -16729,12 +16729,12 @@ var StdioSquireMcpClient = class {
16729
16729
  const child = this.child;
16730
16730
  if (!child)
16731
16731
  return Promise.reject(new Error("Squire MCP session is not running"));
16732
- return new Promise((resolve42, reject) => {
16732
+ return new Promise((resolve43, reject) => {
16733
16733
  const timer = setTimeout(() => {
16734
16734
  this.pending.delete(id);
16735
16735
  reject(new Error(`${method} timed out`));
16736
16736
  }, method === "initialize" ? INITIALIZE_TIMEOUT_MS : CALL_TIMEOUT_MS);
16737
- this.pending.set(id, { resolve: resolve42, reject, timer });
16737
+ this.pending.set(id, { resolve: resolve43, reject, timer });
16738
16738
  child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
16739
16739
  `);
16740
16740
  });
@@ -16756,8 +16756,10 @@ var StdioSquireMcpClient = class {
16756
16756
  continue;
16757
16757
  }
16758
16758
  const id = typeof message.id === "number" ? message.id : void 0;
16759
- if (id === void 0)
16759
+ if (id === void 0) {
16760
+ this.options.onNotification?.(message);
16760
16761
  continue;
16762
+ }
16761
16763
  const entry = this.pending.get(id);
16762
16764
  if (!entry)
16763
16765
  continue;
@@ -16845,6 +16847,7 @@ var SquireTaskRelay = class {
16845
16847
  contextFile;
16846
16848
  authorize;
16847
16849
  makeClient;
16850
+ onApprovalDecided;
16848
16851
  server;
16849
16852
  url;
16850
16853
  token = randomUUID3();
@@ -16860,13 +16863,16 @@ var SquireTaskRelay = class {
16860
16863
  processGroup: true,
16861
16864
  onSpawn: callbacks.onSpawn,
16862
16865
  onExit: callbacks.onExit,
16863
- log: callbacks.onDiagnostic
16864
- })) {
16866
+ log: callbacks.onDiagnostic,
16867
+ onNotification: callbacks.onNotification
16868
+ }), onApprovalDecided = () => {
16869
+ }) {
16865
16870
  this.agentId = agentId;
16866
16871
  this.roomId = roomId;
16867
16872
  this.contextFile = contextFile;
16868
16873
  this.authorize = authorize;
16869
16874
  this.makeClient = makeClient;
16875
+ this.onApprovalDecided = onApprovalDecided;
16870
16876
  }
16871
16877
  async listen() {
16872
16878
  if (this.closed)
@@ -16890,9 +16896,9 @@ var SquireTaskRelay = class {
16890
16896
  response.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: error instanceof Error ? error.message : "Squire relay failed" }));
16891
16897
  }
16892
16898
  });
16893
- await new Promise((resolve42, reject) => {
16899
+ await new Promise((resolve43, reject) => {
16894
16900
  server.once("error", reject);
16895
- server.listen(0, "127.0.0.1", resolve42);
16901
+ server.listen(0, "127.0.0.1", resolve43);
16896
16902
  });
16897
16903
  this.server = server;
16898
16904
  const address = server.address();
@@ -16992,6 +16998,7 @@ var SquireTaskRelay = class {
16992
16998
  };
16993
16999
  task.client = this.makeClient({
16994
17000
  onSpawn: (pid) => this.log("relay-spawn", task, { pid }),
17001
+ onNotification: (message) => this.handleApprovalNotification(task, message),
16995
17002
  onDiagnostic: (message) => {
16996
17003
  if (message.startsWith("squire mcp exited"))
16997
17004
  return;
@@ -17128,7 +17135,10 @@ var SquireTaskRelay = class {
17128
17135
  if (pending && ids.length) {
17129
17136
  task.pendingApprovals.set(approvalKey, {
17130
17137
  requestId: active.requestId,
17131
- sessionIds: new Set(ids)
17138
+ sessionIds: new Set(ids),
17139
+ tool: approval.tool,
17140
+ title: approval.title,
17141
+ detail: approval.detail
17132
17142
  });
17133
17143
  this.log("approval-pending", task, {
17134
17144
  tool: safeToolName ?? null,
@@ -17169,6 +17179,37 @@ var SquireTaskRelay = class {
17169
17179
  task.pendingApprovals.delete(id);
17170
17180
  }
17171
17181
  }
17182
+ /**
17183
+ * Squire's own unsolicited decision on an approval this task is still
17184
+ * holding a connection open for. Matched back to the pending entry by the
17185
+ * same hash `handleSerial` keyed it under (the approval id Squire itself
17186
+ * returned), so a notification for an approval this relay never tracked —
17187
+ * or one already released — is silently ignored, never trusted blind.
17188
+ */
17189
+ handleApprovalNotification(task, message) {
17190
+ if (message.method !== "notifications/approval_decided")
17191
+ return;
17192
+ const params = message.params;
17193
+ const approvalId = params?.approval_id;
17194
+ const status = params?.status;
17195
+ if (typeof approvalId !== "string" || !approvalId || status !== "approved" && status !== "denied")
17196
+ return;
17197
+ const approvalKey = createHash("sha256").update(approvalId).digest("hex");
17198
+ const pending = task.pendingApprovals.get(approvalKey);
17199
+ if (!pending)
17200
+ return;
17201
+ task.pendingApprovals.delete(approvalKey);
17202
+ this.onApprovalDecided({
17203
+ requestId: pending.requestId,
17204
+ approvalId,
17205
+ status,
17206
+ tool: pending.tool,
17207
+ title: pending.title,
17208
+ detail: pending.detail
17209
+ });
17210
+ if (!this.active && !this.approvalRequestId && !task.pendingApprovals.size)
17211
+ this.scheduleIdle();
17212
+ }
17172
17213
  };
17173
17214
  function runSquireTaskProxy(env = process.env) {
17174
17215
  const url = env.BEELINE_SQUIRE_RELAY_URL;
@@ -17864,11 +17905,11 @@ var RegistryMcpHostBroker = class {
17864
17905
  })();
17865
17906
  });
17866
17907
  });
17867
- await new Promise((resolve42, reject) => {
17908
+ await new Promise((resolve43, reject) => {
17868
17909
  this.server.once("error", reject);
17869
17910
  this.server.listen(this.socketPath, () => {
17870
17911
  this.server.off("error", reject);
17871
- resolve42();
17912
+ resolve43();
17872
17913
  });
17873
17914
  });
17874
17915
  chmodSync(this.socketPath, 384);
@@ -17877,12 +17918,12 @@ var RegistryMcpHostBroker = class {
17877
17918
  const server = this.server;
17878
17919
  this.server = void 0;
17879
17920
  if (server)
17880
- await new Promise((resolve42) => server.close(() => resolve42()));
17921
+ await new Promise((resolve43) => server.close(() => resolve43()));
17881
17922
  rmSync(this.socketPath, { force: true });
17882
17923
  }
17883
17924
  };
17884
17925
  async function brokerRequest(socketPath, request) {
17885
- return new Promise((resolve42, reject) => {
17926
+ return new Promise((resolve43, reject) => {
17886
17927
  const socket = createConnection3(socketPath);
17887
17928
  let output = "";
17888
17929
  socket.setEncoding("utf8");
@@ -17899,7 +17940,7 @@ async function brokerRequest(socketPath, request) {
17899
17940
  throw new Error(parsed.error);
17900
17941
  if (!Array.isArray(parsed.messages) || !parsed.messages.every((entry) => typeof entry === "string"))
17901
17942
  throw new Error("Registry MCP broker returned an invalid response");
17902
- resolve42(parsed.messages);
17943
+ resolve43(parsed.messages);
17903
17944
  } catch (error) {
17904
17945
  reject(error);
17905
17946
  }
@@ -18013,6 +18054,7 @@ var SERVER_EVENT_KINDS = [
18013
18054
  "check-failed",
18014
18055
  "merged",
18015
18056
  "grant-decided",
18057
+ "squire-approval-decided",
18016
18058
  "connector-offer-decided",
18017
18059
  "turn-cancelled",
18018
18060
  "choice-answered",
@@ -18020,6 +18062,25 @@ var SERVER_EVENT_KINDS = [
18020
18062
  "poll-closed",
18021
18063
  "workflow-handoff"
18022
18064
  ];
18065
+ var SERVER_EVENT_KIND_DETAIL = {
18066
+ "joined": "fires when a person or agent joins this Room, or when a Workspace arrival projects into it",
18067
+ "schedule-ran": "fires when a schedule you created runs",
18068
+ "corner-opened": "fires when a corner opens under this Room",
18069
+ "check-passed": "fires when a corner's pull request checks turn green",
18070
+ "check-failed": "fires when a corner's pull request checks turn red",
18071
+ "merged": "fires when a corner's pull request merges",
18072
+ "grant-decided": "carries the grant id and status and resumes the turn that asked for the grant",
18073
+ "squire-approval-decided": "carries what was approved or denied and the Squire approval id, and resumes the turn that asked",
18074
+ "connector-offer-decided": "fires when a human accepts or declines an offered connector, or a reconnect ceremony finishes, and resumes the turn that offered it",
18075
+ "turn-cancelled": "fires when a running turn is stopped",
18076
+ "choice-answered": "fires when a human answers a posted choice, starting a new turn for the asking agent",
18077
+ "choice-skipped": "fires when a posted choice's window closes unanswered, starting a new turn for the asking agent",
18078
+ "poll-closed": "fires when a posted poll closes, starting a new turn for the asking agent",
18079
+ "workflow-handoff": "fires when a workflow run starts or hands off to its next role"
18080
+ };
18081
+ function eventKindCatalogLines(kinds = SERVER_EVENT_KINDS) {
18082
+ return kinds.map((kind) => `${kind} ${SERVER_EVENT_KIND_DETAIL[kind]}`);
18083
+ }
18023
18084
 
18024
18085
  // packages/api-contract/dist/turn-silence.js
18025
18086
  var TURN_SILENCE_KINDS = [
@@ -28531,13 +28592,13 @@ var RoomSupervisor = class {
28531
28592
  */
28532
28593
  retryDelay(failures, signal, since = this.#wakes) {
28533
28594
  const delay = this.delaysMs[failures - 1];
28534
- return new Promise((resolve42) => {
28595
+ return new Promise((resolve43) => {
28535
28596
  let timer;
28536
28597
  const done = () => {
28537
28598
  clearTimeout(timer);
28538
28599
  this.#parked.delete(done);
28539
28600
  signal?.removeEventListener("abort", done);
28540
- resolve42();
28601
+ resolve43();
28541
28602
  };
28542
28603
  this.#parked.add(done);
28543
28604
  signal?.addEventListener("abort", done, { once: true });
@@ -28729,12 +28790,12 @@ async function retryBeforeReady(work, options) {
28729
28790
  if (wait2 <= 0)
28730
28791
  break;
28731
28792
  await options.extendStartTimeout(wait2 + 15e3);
28732
- await new Promise((resolve42) => {
28793
+ await new Promise((resolve43) => {
28733
28794
  const timer = setTimeout(done, wait2);
28734
28795
  function done() {
28735
28796
  clearTimeout(timer);
28736
28797
  options.signal?.removeEventListener("abort", done);
28737
- resolve42();
28798
+ resolve43();
28738
28799
  }
28739
28800
  wake = done;
28740
28801
  options.signal?.addEventListener("abort", done, { once: true });
@@ -29195,9 +29256,26 @@ var AcpRequestTimeoutError = class extends Error {
29195
29256
  this.name = "AcpRequestTimeoutError";
29196
29257
  }
29197
29258
  };
29198
- function isSilentPromptTimeout(error) {
29199
- return error instanceof AcpRequestTimeoutError && error.inactivity && error.method === "session/prompt";
29200
- }
29259
+ var TURN_BACKSTOP_MS = 30 * 6e4;
29260
+ var AcpTurnBackstopError = class extends Error {
29261
+ timeoutMs;
29262
+ lastActivity;
29263
+ cause = "turn_backstop";
29264
+ constructor(timeoutMs, lastActivity, stderrTail = "", detail = "") {
29265
+ const meaningful = flattenHarnessCause(stderrTail);
29266
+ super(`turn_backstop: no ACP traffic for ${timeoutMs / 6e4} minutes; last activity: ${lastActivity}${detail ? ` (${detail})` : ""}${meaningful ? `; harness stderr: ${meaningful}` : ""}`);
29267
+ this.timeoutMs = timeoutMs;
29268
+ this.lastActivity = lastActivity;
29269
+ this.name = "AcpTurnBackstopError";
29270
+ }
29271
+ };
29272
+ var AcpRuntimeExitedError = class extends Error {
29273
+ cause = "runtime_exited";
29274
+ constructor(detail) {
29275
+ super(`runtime_exited: ${detail}`);
29276
+ this.name = "AcpRuntimeExitedError";
29277
+ }
29278
+ };
29201
29279
  function killChildProcessGroup(child, signal) {
29202
29280
  if (!child.pid)
29203
29281
  return;
@@ -29247,9 +29325,16 @@ function parseAvailableCommands(value) {
29247
29325
  }
29248
29326
  return result;
29249
29327
  }
29328
+ function isHarnessNotifyChunk(update) {
29329
+ const meta = update._meta;
29330
+ const piAcp = meta?.piAcp;
29331
+ return Boolean(piAcp?.notify);
29332
+ }
29250
29333
  function agentMessageChunkText(update) {
29251
29334
  if (update.sessionUpdate !== "agent_message_chunk")
29252
29335
  return "";
29336
+ if (isHarnessNotifyChunk(update))
29337
+ return "";
29253
29338
  const content = update.content;
29254
29339
  if (content?.type === "text")
29255
29340
  return content.text ?? "";
@@ -29521,16 +29606,26 @@ var AcpClient = class extends EventEmitter {
29521
29606
  });
29522
29607
  this.child.stdout.setEncoding("utf8");
29523
29608
  this.child.stdout.on("data", (chunk) => this.onData(chunk));
29609
+ const child = this.child;
29610
+ const pipeClosed = (pipe) => {
29611
+ setTimeout(() => {
29612
+ if (this.child === child && this.alive) {
29613
+ const stderr = flattenHarnessCause(this.stderrTail);
29614
+ this.failRuntime(`ACP agent ${this.agentLabel} ${pipe} pipe closed${stderr ? `: ${stderr}` : ""}`);
29615
+ }
29616
+ }, 25);
29617
+ };
29618
+ this.child.stdout.on("end", () => pipeClosed("stdout"));
29619
+ this.child.stdout.on("close", () => pipeClosed("stdout"));
29620
+ this.child.stdout.on("error", (error) => this.failRuntime(`stdout: ${error.message}`));
29621
+ this.child.stdin.on("close", () => pipeClosed("stdin"));
29622
+ this.child.stdin.on("error", (error) => this.failRuntime(`stdin: ${error.message}`));
29524
29623
  this.child.on("exit", (code, signal) => {
29525
- void this.handleChildExit(code, signal);
29624
+ if (this.child === child)
29625
+ void this.handleChildExit(code, signal);
29526
29626
  });
29527
29627
  this.child.on("error", (err) => {
29528
- this.alive = false;
29529
- for (const [, p] of this.pending) {
29530
- clearTimeout(p.timer);
29531
- p.reject(err);
29532
- }
29533
- this.pending.clear();
29628
+ this.failRuntime(`ACP agent ${this.agentLabel}: ${err.message}`);
29534
29629
  if (this.listenerCount("error") > 0)
29535
29630
  this.emit("error", err);
29536
29631
  });
@@ -29556,13 +29651,28 @@ var AcpClient = class extends EventEmitter {
29556
29651
  */
29557
29652
  async handleChildExit(code, signal) {
29558
29653
  this.alive = false;
29559
- const oomKilled = signal === "SIGKILL" && !this.stopping && await this.oomKills.consume().catch(() => false);
29654
+ for (const p of this.pending.values())
29655
+ this.clearTimer(p);
29656
+ let oomKilled = false;
29657
+ if (signal === "SIGKILL" && !this.stopping) {
29658
+ let diagnosticTimer;
29659
+ try {
29660
+ oomKilled = await Promise.race([
29661
+ this.oomKills.consume().catch(() => false),
29662
+ new Promise((resolve43) => {
29663
+ diagnosticTimer = setTimeout(() => resolve43(false), 100);
29664
+ })
29665
+ ]);
29666
+ } finally {
29667
+ clearTimeout(diagnosticTimer);
29668
+ }
29669
+ }
29560
29670
  const exitText = oomKilled ? `ACP agent ${this.agentLabel} was killed by the kernel OOM killer (signal=SIGKILL)` : `ACP agent ${this.agentLabel} exited code=${code} signal=${signal}`;
29561
29671
  const meaningful = flattenHarnessCause(this.stderrTail);
29562
29672
  const stderrSuffix = meaningful ? `: ${meaningful}` : "";
29563
29673
  for (const [, p] of this.pending) {
29564
29674
  this.clearTimer(p);
29565
- p.reject(new Error(`${exitText}${stderrSuffix}`));
29675
+ p.reject(new AcpRuntimeExitedError(`${exitText}${stderrSuffix}`));
29566
29676
  }
29567
29677
  this.pending.clear();
29568
29678
  this.activeRunIds.clear();
@@ -29571,9 +29681,15 @@ var AcpClient = class extends EventEmitter {
29571
29681
  this.sessionCommands.clear();
29572
29682
  this.emit("exit", { code, signal });
29573
29683
  }
29684
+ failRuntime(detail) {
29685
+ this.alive = false;
29686
+ for (const p of this.pending.values()) {
29687
+ this.clearTimer(p);
29688
+ p.reject(new AcpRuntimeExitedError(detail));
29689
+ }
29690
+ this.pending.clear();
29691
+ }
29574
29692
  async stop() {
29575
- if (!this.alive)
29576
- return;
29577
29693
  const child = this.child;
29578
29694
  if (!child)
29579
29695
  return;
@@ -29705,10 +29821,10 @@ var AcpClient = class extends EventEmitter {
29705
29821
  }
29706
29822
  /**
29707
29823
  * Run one prompt turn. `timeoutMs` is an idle window, not a hard cap on
29708
- * turn length: every `session/update` for this session (message chunk,
29709
- * tool call, or otherwise) re-arms it, so an actively-working agent can
29710
- * run indefinitely while a genuinely wedged one (zero activity for
29711
- * `timeoutMs`) still gets cancelled. `onChunk` is the ACP-boundary
29824
+ * turn length: every inbound ACP message for this session re-arms it.
29825
+ * Open tool calls suspend the backstop until completion. A silent session
29826
+ * with no open tool is cancelled without stopping the runtime.
29827
+ * `onChunk` is the ACP-boundary
29712
29828
  * streaming hook: called for every incremental `agent_message_chunk`
29713
29829
  * delta as it arrives, so a caller can project live text without waiting
29714
29830
  * for the turn to finish. A harness that only emits a final message
@@ -29723,18 +29839,15 @@ var AcpClient = class extends EventEmitter {
29723
29839
  get lastPromptBytes() {
29724
29840
  return this.lastPromptByteCount;
29725
29841
  }
29726
- async sessionPrompt(sessionId, text2, timeoutMs = 12e4, onChunk, onActivity, onToolCalls) {
29842
+ async sessionPrompt(sessionId, text2, timeoutMs = TURN_BACKSTOP_MS, onChunk, onActivity, onToolCalls) {
29727
29843
  this.lastPromptByteCount = typeof text2 === "string" ? Buffer.byteLength(text2, "utf8") : void 0;
29728
29844
  const updates = [];
29729
29845
  let promptRunId;
29730
- let requestId;
29731
29846
  const onUpdate = (u4) => {
29732
29847
  if (u4.sessionId !== sessionId)
29733
29848
  return;
29734
29849
  updates.push(u4);
29735
29850
  promptRunId ??= this.activeRunIdFromUpdate(u4.update);
29736
- if (requestId !== void 0)
29737
- this.resetPendingIdleTimeout(requestId);
29738
29851
  onActivity?.(agentStreamSnapshot(updates, this.agentLabel));
29739
29852
  onToolCalls?.(toolCallEntries(updates));
29740
29853
  if (onChunk) {
@@ -29751,9 +29864,7 @@ var AcpClient = class extends EventEmitter {
29751
29864
  const result = await this.request("session/prompt", {
29752
29865
  sessionId,
29753
29866
  prompt: typeof text2 === "string" ? [{ type: "text", text: text2 }] : [...text2]
29754
- }, timeoutMs, (id) => {
29755
- requestId = id;
29756
- }, promptPayloadNote(text2));
29867
+ }, timeoutMs, true, promptPayloadNote(text2));
29757
29868
  const agentText = finalAgentMessageText(updates, this.agentLabel);
29758
29869
  const toolCalls = toolCallEntries(updates);
29759
29870
  return {
@@ -29836,22 +29947,22 @@ var AcpClient = class extends EventEmitter {
29836
29947
  }
29837
29948
  }
29838
29949
  /**
29839
- * Re-arm a pending request's idle timer from its full `idleTimeoutMs`,
29840
- * called on every ACP activity signal for that request (streamed text
29841
- * delta, session/update, tool call). A request only times out after this
29842
- * much wall-clock time with zero activity, not after a fixed deadline —
29843
- * so an actively-working turn can run indefinitely while a genuinely
29844
- * wedged one still gets caught.
29950
+ * A tool owns its deadline. Silence while it is open is never a turn failure.
29951
+ * After the last tool closes, start a full backstop window for this session.
29845
29952
  */
29846
29953
  resetPendingIdleTimeout(id) {
29847
29954
  const p = this.pending.get(id);
29848
29955
  if (!p || p.idleTimeoutMs === void 0)
29849
29956
  return;
29850
29957
  this.clearTimer(p);
29851
- const { idleTimeoutMs, method, reject, detail } = p;
29958
+ if (p.openToolCalls?.size)
29959
+ return;
29960
+ const { idleTimeoutMs, reject, detail, sessionId, lastActivity } = p;
29852
29961
  p.timer = setTimeout(() => {
29853
29962
  this.pending.delete(id);
29854
- reject(new AcpRequestTimeoutError(method ?? "request", idleTimeoutMs, this.stderrTail, true, detail ?? ""));
29963
+ if (sessionId)
29964
+ this.sessionCancel(sessionId);
29965
+ reject(new AcpTurnBackstopError(idleTimeoutMs, lastActivity ?? "session/prompt", this.stderrTail, detail));
29855
29966
  }, idleTimeoutMs);
29856
29967
  }
29857
29968
  onData(chunk) {
@@ -29874,6 +29985,25 @@ var AcpClient = class extends EventEmitter {
29874
29985
  handleMessage(msg) {
29875
29986
  const id = msg.id;
29876
29987
  const method = msg.method;
29988
+ const params = msg.params;
29989
+ const response = !method && id !== void 0 ? this.pending.get(Number(id)) : void 0;
29990
+ const sessionId = params?.sessionId ?? response?.sessionId;
29991
+ if (sessionId) {
29992
+ for (const [requestId, pending] of this.pending) {
29993
+ if (pending.method !== "session/prompt" || pending.sessionId !== sessionId)
29994
+ continue;
29995
+ const update = params?.update;
29996
+ pending.lastActivity = typeof update?.sessionUpdate === "string" ? update.sessionUpdate : method ?? `response:${response?.method ?? "request"}`;
29997
+ if (typeof update?.toolCallId === "string" && (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update")) {
29998
+ if (update.status === "pending" || update.status === "in_progress") {
29999
+ pending.openToolCalls?.add(update.toolCallId);
30000
+ } else if (update.status === "completed" || update.status === "failed") {
30001
+ pending.openToolCalls?.delete(update.toolCallId);
30002
+ }
30003
+ }
30004
+ this.resetPendingIdleTimeout(requestId);
30005
+ }
30006
+ }
29877
30007
  if (id !== void 0 && !method) {
29878
30008
  const n3 = Number(id);
29879
30009
  const p = this.pending.get(n3);
@@ -29901,19 +30031,19 @@ var AcpClient = class extends EventEmitter {
29901
30031
  return;
29902
30032
  }
29903
30033
  if (method === "session/update") {
29904
- const params = msg.params;
29905
- if (params?.sessionId && params.update) {
29906
- if (params.update.sessionUpdate === "available_commands_update") {
29907
- const commands = parseAvailableCommands(params.update.availableCommands);
29908
- this.sessionCommands.set(params.sessionId, commands);
30034
+ const params2 = msg.params;
30035
+ if (params2?.sessionId && params2.update) {
30036
+ if (params2.update.sessionUpdate === "available_commands_update") {
30037
+ const commands = parseAvailableCommands(params2.update.availableCommands);
30038
+ this.sessionCommands.set(params2.sessionId, commands);
29909
30039
  this.commandsHandler?.(commands);
29910
- this.emit("commands", { sessionId: params.sessionId, commands });
30040
+ this.emit("commands", { sessionId: params2.sessionId, commands });
29911
30041
  }
29912
- const toolCallId = params.update.toolCallId;
29913
- const sessionUpdate = params.update.sessionUpdate;
30042
+ const toolCallId = params2.update.toolCallId;
30043
+ const sessionUpdate = params2.update.sessionUpdate;
29914
30044
  if (typeof toolCallId === "string" && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
29915
- const metadataKey = `${params.sessionId}\0${toolCallId}`;
29916
- const status = params.update.status;
30045
+ const metadataKey = `${params2.sessionId}\0${toolCallId}`;
30046
+ const status = params2.update.status;
29917
30047
  if (status === "completed" || status === "failed") {
29918
30048
  this.toolCallMetadata.delete(metadataKey);
29919
30049
  } else {
@@ -29921,25 +30051,25 @@ var AcpClient = class extends EventEmitter {
29921
30051
  this.toolCallMetadata.set(metadataKey, {
29922
30052
  ...existing,
29923
30053
  toolCallId,
29924
- ...typeof params.update.title === "string" ? { title: params.update.title } : {},
29925
- ...typeof params.update.kind === "string" ? { kind: params.update.kind } : {},
29926
- ..."rawInput" in params.update ? { rawInput: params.update.rawInput } : {},
30054
+ ...typeof params2.update.title === "string" ? { title: params2.update.title } : {},
30055
+ ...typeof params2.update.kind === "string" ? { kind: params2.update.kind } : {},
30056
+ ..."rawInput" in params2.update ? { rawInput: params2.update.rawInput } : {},
29927
30057
  // `content`/`locations` carry the touched file paths for adapters
29928
30058
  // whose permission request omits them (codex-acp's file-change
29929
30059
  // request has no rawInput at all) — the corner worktree guard in
29930
30060
  // `session-sandbox.ts` reads them.
29931
- ..."content" in params.update ? { content: params.update.content } : {},
29932
- ..."locations" in params.update ? { locations: params.update.locations } : {}
30061
+ ..."content" in params2.update ? { content: params2.update.content } : {},
30062
+ ..."locations" in params2.update ? { locations: params2.update.locations } : {}
29933
30063
  });
29934
30064
  }
29935
30065
  }
29936
30066
  const u4 = {
29937
- sessionId: params.sessionId,
29938
- update: params.update
30067
+ sessionId: params2.sessionId,
30068
+ update: params2.update
29939
30069
  };
29940
- const activeRunId = this.activeRunIdFromUpdate(params.update);
30070
+ const activeRunId = this.activeRunIdFromUpdate(params2.update);
29941
30071
  if (activeRunId)
29942
- this.activeRunIds.set(params.sessionId, activeRunId);
30072
+ this.activeRunIds.set(params2.sessionId, activeRunId);
29943
30073
  this.emit("session/update", u4);
29944
30074
  }
29945
30075
  return;
@@ -29954,7 +30084,7 @@ var AcpClient = class extends EventEmitter {
29954
30084
  const current = this.activeRunIds.get(sessionId);
29955
30085
  if (current)
29956
30086
  return Promise.resolve(current);
29957
- return new Promise((resolve42, reject) => {
30087
+ return new Promise((resolve43, reject) => {
29958
30088
  const onUpdate = (update) => {
29959
30089
  if (update.sessionId !== sessionId)
29960
30090
  return;
@@ -29962,7 +30092,7 @@ var AcpClient = class extends EventEmitter {
29962
30092
  if (!runId)
29963
30093
  return;
29964
30094
  cleanup();
29965
- resolve42(runId);
30095
+ resolve43(runId);
29966
30096
  };
29967
30097
  const timer = setTimeout(() => {
29968
30098
  cleanup();
@@ -30034,32 +30164,30 @@ var AcpClient = class extends EventEmitter {
30034
30164
  result: { outcome: { outcome: "cancelled" } }
30035
30165
  });
30036
30166
  }
30037
- /**
30038
- * `onStart`, when given, receives the assigned request id synchronously
30039
- * and marks the request as idle-resettable at `timeoutMs`: callers can
30040
- * then call `resetPendingIdleTimeout(id)` on activity to defer the
30041
- * timeout instead of it firing at a fixed deadline.
30042
- */
30043
- request(method, params, timeoutMs = 6e4, onStart, detail = "") {
30167
+ /** Prompt backstops reset on session traffic; other requests use a fixed deadline. */
30168
+ request(method, params, timeoutMs = 6e4, backstop = false, detail = "") {
30044
30169
  if (!this.child || !this.alive) {
30045
30170
  return Promise.reject(new Error("AcpClient not started"));
30046
30171
  }
30047
30172
  const id = this.nextId++;
30048
30173
  const payload = { jsonrpc: "2.0", id, method, params };
30049
- return new Promise((resolve42, reject) => {
30050
- const timer = setTimeout(() => {
30174
+ return new Promise((resolve43, reject) => {
30175
+ const timer = backstop ? void 0 : setTimeout(() => {
30051
30176
  this.pending.delete(id);
30052
- reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
30177
+ reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, false, detail));
30053
30178
  }, timeoutMs);
30179
+ const sessionId = params?.sessionId;
30054
30180
  this.pending.set(id, {
30055
- resolve: resolve42,
30181
+ resolve: resolve43,
30056
30182
  reject,
30057
30183
  timer,
30058
30184
  method,
30185
+ ...sessionId ? { sessionId } : {},
30059
30186
  ...detail ? { detail } : {},
30060
- ...onStart ? { idleTimeoutMs: timeoutMs } : {}
30187
+ ...backstop ? { idleTimeoutMs: timeoutMs, lastActivity: method, openToolCalls: /* @__PURE__ */ new Set() } : {}
30061
30188
  });
30062
- onStart?.(id);
30189
+ if (backstop)
30190
+ this.resetPendingIdleTimeout(id);
30063
30191
  this.write(payload);
30064
30192
  });
30065
30193
  }
@@ -30067,9 +30195,15 @@ var AcpClient = class extends EventEmitter {
30067
30195
  this.write({ jsonrpc: "2.0", method, params });
30068
30196
  }
30069
30197
  write(obj) {
30070
- if (!this.child?.stdin.writable)
30198
+ if (!this.child?.stdin.writable) {
30199
+ if (this.child)
30200
+ this.failRuntime(`ACP agent ${this.agentLabel} stdin pipe closed`);
30071
30201
  return;
30072
- this.child.stdin.write(JSON.stringify(obj) + "\n");
30202
+ }
30203
+ this.child.stdin.write(JSON.stringify(obj) + "\n", (error) => {
30204
+ if (error)
30205
+ this.failRuntime(`stdin: ${error.message}`);
30206
+ });
30073
30207
  }
30074
30208
  };
30075
30209
 
@@ -31187,7 +31321,10 @@ function interpreterScriptArgument(argv) {
31187
31321
 
31188
31322
  // packages/api-contract/dist/connector-offers.js
31189
31323
  var CONNECTOR_OFFER_WINDOW_MS = 2 * 6e4;
31190
- var OFFERABLE_CONNECTOR_KINDS = CONNECTABLE_CONNECTOR_KINDS.filter((kind) => kind !== "wallet");
31324
+ var OFFERABLE_CONNECTOR_KINDS = [
31325
+ ...CONNECTABLE_CONNECTOR_KINDS,
31326
+ "wallet"
31327
+ ];
31191
31328
 
31192
31329
  // packages/api-contract/dist/phone-guards.js
31193
31330
  var CLOSED_VIEWER = {
@@ -31648,11 +31785,11 @@ The soul is not authority and never changes your tools, permissions, roles, or m
31648
31785
  {
31649
31786
  id: "core.files",
31650
31787
  topic: "files",
31651
- why: "Agents fetched the reference URL of a shared file instead of the copy already downloaded for them.",
31788
+ why: "Files on earlier messages are shown only as markers and never pre-downloaded; agents fetched media URLs instead.",
31652
31789
  budgetBytes: 160,
31653
31790
  layer: "core",
31654
31791
  surfaces: EVERYWHERE,
31655
- render: () => "Files people share are downloaded for you: read the local path in the prompt and never fetch the reference URL."
31792
+ render: () => "For a \u{1F4CE} file, call download_attachment and read its path. Never fetch media URLs; on failure, say you couldn't open it."
31656
31793
  },
31657
31794
  {
31658
31795
  id: "surface.tools",
@@ -31681,13 +31818,14 @@ The soul is not authority and never changes your tools, permissions, roles, or m
31681
31818
  id: "room.corners",
31682
31819
  topic: "repository-work",
31683
31820
  why: "Repository work must start through the host-governed corner, with a brief as its authority.",
31684
- budgetBytes: 900,
31821
+ budgetBytes: 1100,
31685
31822
  layer: "surface",
31686
31823
  surfaces: ["room"],
31687
31824
  render: () => [
31688
31825
  "Repository changes happen only in a corner. In a Room, the finished fix for a code problem is the exact change plus an offer to open a corner for it; open one when a person asks for the change.",
31689
31826
  "Before opening one, consult beeline-triage and beeline-spec, then call open_corner with a name of at most three words, an objective of at most 24 words, and the brief (a spec plus the approving message). The objective only titles the work; the brief is its authority. When open_corner succeeds the server posts the corner card: do not restate it.",
31690
- "For corners you belong to, use inspect_corner for status, steer_corner to pass Room input down, and ask_corner for one answer. Never post into a corner without a Room command."
31827
+ "For corners you belong to, use inspect_corner for status, steer_corner to pass Room input down, and ask_corner for one answer. Never post into a corner without a Room command.",
31828
+ "A corner ends when its pull request merges; since a person may merge minutes after it opens, anything still needed must already live in a different corner before that pull request opens, not after. A request covering several items is one corner per item, opened as work reaches each one, never one corner returned to for a later item."
31691
31829
  ].join(" ")
31692
31830
  },
31693
31831
  {
@@ -32016,11 +32154,11 @@ var BEELINE_TRIAGE_SKILL_NAME = "beeline-triage";
32016
32154
  var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
32017
32155
  var BEELINE_SPEC_SKILL_NAME = "beeline-spec";
32018
32156
  var BEELINE_ROOM_MECHANICS = [
32019
- "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.",
32020
- "To create a file you can send, call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture. To put a real photograph in an artifact, call beeline-agent fetch_image with the photo URL; it writes the bytes to your session scratch and returns the path, mime, and size \u2014 read them, base64-encode, and embed as a data: URL. The validator still refuses every http(s) image reference, and drawing an SVG stand-in is not a photograph.",
32157
+ "Files and photos on the message you are answering are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline). A file on an earlier message shows only as a \u{1F4CE} marker with an id; call beeline-agent download_attachment with that message id and attachment id, then read the returned path. Never fetch a media URL; if the download fails, tell the requester you could not open the file.",
32158
+ "To create a file you can send, call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture.",
32021
32159
  "To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / update_schedule / delete_schedule manage them, including schedules other agents created for themselves.",
32022
32160
  `To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
32023
- `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant. choice-answered, choice-skipped, and poll-closed start a new input turn for the asking agent; they do not resume a paused grant. A corner merging wakes nobody in its parent Room by default. If you still have work to do after a corner merges (a hand-off, a release step, a reply that has to describe landed code), subscribe to merged in the parent Room, from a turn there, before the merge. Each merge in that Room then wakes you and names the corner and pull request: act only on the corner you are waiting for, post nothing for any other merge, and take merged back out of your list once nothing is left to wait for. Never post just to acknowledge a merge: the merge card already announces it.`,
32161
+ `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want; each one then wakes you for a turn. Available kinds: ${eventKindCatalogLines().join("; ")}. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. A corner merging wakes nobody in its parent Room by default. If you still have work to do after a corner merges (a hand-off, a release step, a reply that has to describe landed code), subscribe to merged in the parent Room, from a turn there, before the merge. Each merge in that Room then wakes you and names the corner and pull request: act only on the corner you are waiting for, post nothing for any other merge, and take merged back out of your list once nothing is left to wait for. Never post just to acknowledge a merge: the merge card already announces it. If you are waiting on a human decision - a grant, a connector offer, a posted choice or poll, or a Squire approval (payment, credential, sign-in, or vouch) - subscribe to its kind instead of asking them to type a confirmation in the Room; the decision wakes you with the answer.`,
32024
32162
  "If you need reach outside the sandbox, call beeline-agent request_grant. If you already know discrete options, call beeline-agent ask_choice (one human, optional) or open_poll (every human in this Room, required deadline). A poll is refused below two electors and above fifty, and in a DM. A plurality is a fact, never permission to deploy, delete, merge, or spend. Open-ended asks stay tagged prose. Never put Always / Once / No on a preference.",
32025
32163
  "To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing."
32026
32164
  ].join(" ");
@@ -32057,6 +32195,10 @@ A **tool** is something you can use once a human adds it; a **key** is the crede
32057
32195
 
32058
32196
  The tools this build knows, what each is for, and which the owner of this machine already has are one call away: beeline-agent workbench_status. That read is the Workbench of the machine and owner you actually run on, not the person who asked. Connected apps are listed once per app with a stable ID. Their sign-in and credentials stay with the server. Trusty Squire is vaulted credentials plus a browser for the separate key and browser routes. Tailscale installs its CLI on the selected helper and opens Tailscale's own browser sign-in; once connected, use the CLI for tailnet resources and tailscale file cp or tailscale file get for Taildrop. The wallet is created only from the Workbench page. When you cannot reach a tool, say: I can't reach X on this machine because Y; to fix it, Z. Give one explanation, then take or offer Z.
32059
32197
 
32198
+ **Inspect your own host before answering.** When a question is about a system on the machine you run on, inspect its live state with the tools you have: processes, logs, local endpoints or service status. Say what you checked. Memory and Room history are not evidence of current state.
32199
+
32200
+ **Use available tools before asking the person.** Before asking a person for a code, a status or a file, call workbench_status and check whether a tool or connected app you already have can get it, then use it. This includes an emailed verification code in an already-connected inbox. Ask only when none can, and say why. The key-ownership and grant rules still apply.
32201
+
32060
32202
  **Offer the tool at the moment you need it.** When the work in front of you needs a tool the person does not have, do not send them to a settings page and do not stop at naming it. Call workbench_status first - a tool they already have is used, not offered. In a Room or DM, call offer_connector with the connectorType and one short reason: a card appears there, spoken by you, addressed to the person you are answering, with one action. Connector offers are unavailable in corners; use a Room or DM for those. Your turn pauses on the card - say in prose what you are waiting for and end the turn; you are woken when it is added, and then you carry on.
32061
32203
 
32062
32204
  **Research before you offer, and say so.** Never offer a tool you cannot describe. If someone asks you to install or add something you do not already know - by name, by purpose, or by what it will hold - say plainly that you are looking it up first, find out what it is and what it does with credentials, and state what you learned in your reply BEFORE the card appears. A person reading "Add X?" must already have read, in your own words, what X is. Refusing to act blind is part of being trusted with keys.
@@ -32067,9 +32209,9 @@ Keys belong to the human who provisioned them. Your owner's keys serve whoever a
32067
32209
 
32068
32210
  ## Connect an app
32069
32211
 
32070
- When a request needs an app or service, call beeline-agent \`workbench_status\` first. For a connected app, use its ID with \`list_app_tools\`, then call \`execute_app_tool\` with the selected tool slug and arguments. The server executes against that person\u2019s account and decides permission on every call. An empty tool list or \`needs_connection\` means the account must be reconnected; \`needs_permission\` means wait for the connected person's decision. Do not switch paths after denial or provider unavailability. No app token, provider key, or sign-in URL belongs in chat or a model prompt.
32212
+ When a request needs an app or service, call beeline-agent \`workbench_status\` first. For a connected app, use its ID with \`list_app_tools\`, then call \`execute_app_tool\` with the selected tool slug and arguments. The server executes against that person\u2019s account and decides permission on every call. If tool discovery reports an error, follow its cause; \`needs_connection\` means the account must be reconnected, and \`needs_permission\` means wait for the connected person's decision. Do not switch paths after denial or provider unavailability. No app token, provider key, or sign-in URL belongs in chat or a model prompt.
32071
32213
 
32072
- For an app that is not connected, call \`connect_app\` with its name, one short reason, and a request-specific \`continuation\`: one sentence stating what you will do right after sign-in. Do not include a link, credential, account identifier, or private data in that sentence. The server chooses the route and returns \`next\`. In a corner, call it from that corner so the sign-in card and connection wake return there. A managed OAuth app presents a sign-in card to its owner; end the turn and wait for the connection wake. If the provider reports managed OAuth unsupported, follow the server's Squire route and its instructions. A provider outage is unavailable, not permission to change routes. After the person connects, read Workbench again and use the app ID. Trusty Squire handles the separate API key and browser routes; never ask for a raw key in chat. Every use is authorized as \`app:<key>\` and recorded once.
32214
+ For an app that is not connected, call \`connect_app\` with its name, one short reason, and a request-specific \`continuation\`: one sentence stating what you will do right after sign-in. Do not include a link, credential, account identifier, or private data in that sentence. The server chooses the route and returns \`next\`. In a corner, call it from that corner so the sign-in card and connection wake return there. A managed OAuth app presents a sign-in card to its owner; end the turn and wait for the connection wake. If the provider reports managed OAuth unsupported, follow the server's Squire route and its instructions. A provider outage is unavailable, not permission to change routes. After the person connects, read Workbench again and use the app ID. Every use is authorized as \`app:<key>\` and recorded once.
32073
32215
 
32074
32216
  ## Showing a mock
32075
32217
 
@@ -32135,6 +32277,15 @@ The record informs the verdict but never replaces approve_merge or pr_checks_sta
32135
32277
  - Record every command and exit code.
32136
32278
  - A review with no executed command is invalid and must FAIL.
32137
32279
 
32280
+ ### Design rules
32281
+
32282
+ When the diff touches UI and the repository has a DESIGN.md, check every changed style against its rules: colours from theme tokens only, the type roles, the spacing scale, the allowed radii, elevation, the shared components, and touch targets.
32283
+
32284
+ - Run the repository's design lints. In Beeline, from \`apps/mobile\`: \`npx vitest run sources/buzz/calm-lint sources/buzz/design-lint\`.
32285
+ - FAIL when a design baseline count grows, or when an allowlist entry is added without naming the design decision behind it.
32286
+ - Require a screenshot or rendered proof of every visual change in each theme the app ships (in Beeline, Obsidian and Bone).
32287
+ - A design-rule violation is an engineering finding and blocks PASS.
32288
+
32138
32289
  ## 4. Adversarial pass
32139
32290
 
32140
32291
  - For every changed function, name one concrete input or sequence that breaks it.
@@ -33409,6 +33560,14 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
33409
33560
  }
33410
33561
  await applyGrantedHostRoutes(root, operatorHome, grantedHostRoutes, agentKind, failClosed, resourceAuthFile, extraHostRoutes, squireScope);
33411
33562
  await provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting);
33563
+ if (appliesToHarness(agentKind, "pi")) {
33564
+ const settings2 = {
33565
+ httpIdleTimeoutMs: 3e5,
33566
+ retry: { enabled: true, maxRetries: 3, baseDelayMs: 2e3 }
33567
+ };
33568
+ await writeIsolatedHarnessFile(resolve15(root, "pi", "settings.json"), `${JSON.stringify(settings2, null, 2)}
33569
+ `);
33570
+ }
33412
33571
  }
33413
33572
  function appliesToHarness(selected, harness) {
33414
33573
  return !selected || selected === harness;
@@ -34390,7 +34549,7 @@ function clearInstallFailure(stateDir) {
34390
34549
  } catch {
34391
34550
  }
34392
34551
  }
34393
- var runSandboxInstallCommand = (command, args) => new Promise((resolve42) => {
34552
+ var runSandboxInstallCommand = (command, args) => new Promise((resolve43) => {
34394
34553
  execFile6(command, [...args], {
34395
34554
  timeout: INSTALL_TIMEOUT_MS,
34396
34555
  maxBuffer: INSTALL_OUTPUT_LIMIT * 2,
@@ -34400,7 +34559,7 @@ var runSandboxInstallCommand = (command, args) => new Promise((resolve42) => {
34400
34559
  }, (error, stdout6, stderr) => {
34401
34560
  const code = error && "code" in error && typeof error.code === "number" ? error.code : error ? null : 0;
34402
34561
  const output = [stdout6, stderr].join("").trim();
34403
- resolve42({ code, output: output || (error ? error.message : "") });
34562
+ resolve43({ code, output: output || (error ? error.message : "") });
34404
34563
  });
34405
34564
  });
34406
34565
  function lastLine(output, fallback) {
@@ -35073,6 +35232,7 @@ var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
35073
35232
  var UPDATE_PROBE_RETRY_DELAY_MS = 2e3;
35074
35233
  var ACP_ERROR_CODE = /\bACP error (-\d+):/;
35075
35234
  var ACP_PROMPT_INACTIVITY = /\bACP session\/prompt timed out after \d+ms of inactivity\b/;
35235
+ var ACP_TURN_BACKSTOP = /\bturn_backstop: no ACP traffic for [\d.]+ minutes; last activity:/;
35076
35236
  function isServerInternalCode(code) {
35077
35237
  return code === -32603 || code <= -32e3 && code >= -32099;
35078
35238
  }
@@ -35080,8 +35240,9 @@ function classifyAcpTurnFailure(error) {
35080
35240
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
35081
35241
  if (!message)
35082
35242
  return void 0;
35083
- if (ACP_PROMPT_INACTIVITY.test(message))
35243
+ if (ACP_TURN_BACKSTOP.test(message) || ACP_PROMPT_INACTIVITY.test(message)) {
35084
35244
  return { kind: "prompt-inactivity" };
35245
+ }
35085
35246
  const code = Number(ACP_ERROR_CODE.exec(message)?.[1]);
35086
35247
  if (Number.isFinite(code) && isServerInternalCode(code))
35087
35248
  return { kind: "server-internal", code };
@@ -35136,7 +35297,7 @@ function describeCurrentReleaseOutcome(outcome) {
35136
35297
  async function retryWhileAdapterReinstalls(run2, options = {}) {
35137
35298
  const attempts = options.attempts ?? 12;
35138
35299
  const delayMs = options.delayMs ?? 5e3;
35139
- const sleep4 = options.sleep ?? ((ms) => new Promise((resolve42) => setTimeout(resolve42, ms)));
35300
+ const sleep4 = options.sleep ?? ((ms) => new Promise((resolve43) => setTimeout(resolve43, ms)));
35140
35301
  for (let attempt = 1; ; attempt += 1) {
35141
35302
  try {
35142
35303
  return await run2();
@@ -37809,7 +37970,7 @@ async function probeReleaseInSubprocess(input) {
37809
37970
  return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
37810
37971
  }
37811
37972
  const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
37812
- return new Promise((resolve42) => {
37973
+ return new Promise((resolve43) => {
37813
37974
  const child = spawn13(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
37814
37975
  let stdout6 = "";
37815
37976
  let stderr = "";
@@ -37819,7 +37980,7 @@ async function probeReleaseInSubprocess(input) {
37819
37980
  return;
37820
37981
  settled = true;
37821
37982
  clearTimeout(timer);
37822
- resolve42(outcome);
37983
+ resolve43(outcome);
37823
37984
  };
37824
37985
  const timer = setTimeout(() => {
37825
37986
  child.kill("SIGKILL");
@@ -37903,8 +38064,8 @@ import { mkdir as mkdir28, rename as rename12, stat as stat9, writeFile as write
37903
38064
  import { dirname as dirname29 } from "node:path";
37904
38065
 
37905
38066
  // apps/body/dist/agent-runtime.js
37906
- import { homedir as homedir18 } from "node:os";
37907
- import { dirname as dirname28, join as join19, resolve as resolve40 } from "node:path";
38067
+ import { homedir as homedir19 } from "node:os";
38068
+ import { dirname as dirname28, join as join19, resolve as resolve41 } from "node:path";
37908
38069
 
37909
38070
  // apps/body/dist/model-availability.js
37910
38071
  var SECRET_VALUE = /(?:token|key|secret|password)=?[^\s,;]*/gi;
@@ -38038,10 +38199,10 @@ var LOGIN_URL = /https:\/\/login\.tailscale\.com\/[A-Za-z0-9_/-]+/u;
38038
38199
  function clipped(value) {
38039
38200
  return value.length <= OUTPUT_LIMIT ? value : value.slice(value.length - OUTPUT_LIMIT);
38040
38201
  }
38041
- var runTailscaleCommand = (command, args) => new Promise((resolve42) => {
38202
+ var runTailscaleCommand = (command, args) => new Promise((resolve43) => {
38042
38203
  execFile7(command, [...args], { timeout: COMMAND_TIMEOUT_MS, maxBuffer: OUTPUT_LIMIT * 2 }, (error, stdout6, stderr) => {
38043
38204
  const code = error && "code" in error && typeof error.code === "number" ? error.code : error ? null : 0;
38044
- resolve42({ code, stdout: clipped(stdout6), stderr: clipped(stderr) });
38205
+ resolve43({ code, stdout: clipped(stdout6), stderr: clipped(stderr) });
38045
38206
  });
38046
38207
  });
38047
38208
  function loginUrlFrom(status, output) {
@@ -38605,7 +38766,7 @@ import { execFile as execFile13 } from "node:child_process";
38605
38766
  import { createHash as createHash13 } from "node:crypto";
38606
38767
  import { existsSync as existsSync8, mkdirSync as mkdirSync3 } from "node:fs";
38607
38768
  import { mkdir as mkdir26, readdir as readdir9, realpath as realpath2, rm as rm14 } from "node:fs/promises";
38608
- import { dirname as dirname26, relative as relative3, resolve as resolve35, sep as sep2 } from "node:path";
38769
+ import { dirname as dirname26, relative as relative3, resolve as resolve36, sep as sep2 } from "node:path";
38609
38770
  import { promisify as promisify8 } from "node:util";
38610
38771
 
38611
38772
  // apps/body/dist/grant-runner.js
@@ -39066,8 +39227,30 @@ function captureConnectionUsage(recorder, turn, calls) {
39066
39227
  }
39067
39228
  }
39068
39229
 
39069
- // apps/body/dist/turn-usage.js
39230
+ // apps/body/dist/model-context-window.js
39070
39231
  import { readFile as readFile21 } from "node:fs/promises";
39232
+ import { join as join13 } from "node:path";
39233
+ async function modelContextWindowTokens(model, piHome) {
39234
+ if (!model || !piHome)
39235
+ return void 0;
39236
+ try {
39237
+ const catalog = JSON.parse(await readFile21(join13(piHome, "models.json"), "utf8"));
39238
+ const parts = model.split("/");
39239
+ const preferredProvider = parts.length > 2 ? parts.shift() : void 0;
39240
+ const modelId = parts.join("/");
39241
+ const providers = preferredProvider ? [catalog.providers?.[preferredProvider]].filter(Boolean) : Object.values(catalog.providers ?? {});
39242
+ for (const provider of providers) {
39243
+ const window = provider?.models?.find((entry) => entry.id === modelId)?.contextWindow;
39244
+ if (window && Number.isFinite(window) && window > 0)
39245
+ return Math.floor(window);
39246
+ }
39247
+ } catch {
39248
+ }
39249
+ return void 0;
39250
+ }
39251
+
39252
+ // apps/body/dist/turn-usage.js
39253
+ import { readFile as readFile22 } from "node:fs/promises";
39071
39254
  function tokenCount(value) {
39072
39255
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
39073
39256
  }
@@ -39079,61 +39262,77 @@ function promptTokens(usage2) {
39079
39262
  const cacheWrite = tokenCount(usage2.cacheWrite) ?? tokenCount(usage2.cache_write) ?? 0;
39080
39263
  return input + cacheRead + cacheWrite;
39081
39264
  }
39082
- async function readHarnessTurnUsage(input) {
39265
+ async function readUsageCalls(input, latestTurn) {
39083
39266
  const file = await piSessionFilePath(input.agentEnv, input.sessionId);
39084
39267
  if (!file)
39268
+ return latestTurn ? void 0 : [];
39269
+ const raw = await readFile22(file, "utf8").catch(() => void 0);
39270
+ if (raw === void 0)
39085
39271
  return void 0;
39086
- let raw;
39087
- try {
39088
- raw = await readFile21(file, "utf8");
39089
- } catch {
39090
- return void 0;
39091
- }
39092
- let latest;
39272
+ const calls = [];
39093
39273
  for (const line of raw.split(/\r?\n/)) {
39094
- if (!line.trim())
39095
- continue;
39096
39274
  let entry;
39097
39275
  try {
39098
39276
  entry = JSON.parse(line);
39099
39277
  } catch {
39100
39278
  continue;
39101
39279
  }
39102
- if (entry.type !== "message" || !entry.message)
39280
+ if (!entry || entry.type !== "message" || !entry.message)
39103
39281
  continue;
39104
- if (entry.message.role === "user") {
39105
- latest = void 0;
39106
- continue;
39107
- }
39282
+ if (entry.message.role === "user" && latestTurn)
39283
+ calls.length = 0;
39108
39284
  if (entry.message.role !== "assistant")
39109
39285
  continue;
39110
39286
  const usage2 = entry.message.usage;
39111
- if (!usage2 || typeof usage2 !== "object" || Array.isArray(usage2))
39112
- continue;
39113
- const tokens = promptTokens(usage2);
39114
- if (tokens === void 0)
39115
- continue;
39287
+ const tokens = usage2 && typeof usage2 === "object" && !Array.isArray(usage2) ? promptTokens(usage2) : void 0;
39116
39288
  const model = entry.message.model;
39117
- latest = {
39118
- inputTokens: tokens,
39289
+ calls.push({
39290
+ ...tokens !== void 0 ? { inputTokens: tokens } : {},
39119
39291
  ...typeof model === "string" && model ? { model } : {}
39120
- };
39292
+ });
39121
39293
  }
39122
- return latest;
39294
+ return calls;
39295
+ }
39296
+ function summarize(calls) {
39297
+ if (!calls.length)
39298
+ return void 0;
39299
+ const measured = calls.filter((call) => call.inputTokens !== void 0);
39300
+ return {
39301
+ ...calls[calls.length - 1],
39302
+ ...measured.length ? { totalInputTokens: measured.reduce((sum, call) => sum + call.inputTokens, 0) } : {},
39303
+ modelCalls: measured.length,
39304
+ modelCallsWithoutUsage: calls.length - measured.length
39305
+ };
39123
39306
  }
39307
+ var TurnUsageAccumulator = class {
39308
+ calls = [];
39309
+ async measure(input, run2) {
39310
+ const before = await readUsageCalls(input, false);
39311
+ try {
39312
+ return await run2();
39313
+ } finally {
39314
+ const after = await readUsageCalls(input, false);
39315
+ if (before && after)
39316
+ this.calls.push(...after.slice(before.length));
39317
+ }
39318
+ }
39319
+ get usage() {
39320
+ return summarize(this.calls);
39321
+ }
39322
+ };
39124
39323
 
39125
39324
  // apps/body/dist/server-command-intake.js
39126
39325
  import { randomUUID as randomUUID10 } from "node:crypto";
39127
39326
  import { mkdir as mkdir17, writeFile as writeFile16 } from "node:fs/promises";
39128
39327
  import { writeFileSync as writeFileSync4 } from "node:fs";
39129
39328
  import { tmpdir as tmpdir4 } from "node:os";
39130
- import { dirname as dirname21, join as join13 } from "node:path";
39329
+ import { dirname as dirname21, join as join14 } from "node:path";
39131
39330
  var CommandExecutionContext = class {
39132
39331
  generationId = randomUUID10();
39133
39332
  path;
39134
39333
  current;
39135
39334
  constructor(root) {
39136
- this.path = join13(root ?? tmpdir4(), `beeline-command-${this.generationId}.json`);
39335
+ this.path = join14(root ?? tmpdir4(), `beeline-command-${this.generationId}.json`);
39137
39336
  }
39138
39337
  async enter(command) {
39139
39338
  this.current = command;
@@ -39281,11 +39480,11 @@ async function runServerCommandIntake(options) {
39281
39480
  if (attempt === 3)
39282
39481
  throw error;
39283
39482
  const waitMs = Math.max(250 * 2 ** attempt, error instanceof DaemonApiError ? error.retryAfterMs ?? 0 : 0);
39284
- await new Promise((resolve42) => {
39483
+ await new Promise((resolve43) => {
39285
39484
  const done = () => {
39286
39485
  clearTimeout(timer);
39287
39486
  signal?.removeEventListener("abort", done);
39288
- resolve42();
39487
+ resolve43();
39289
39488
  };
39290
39489
  const timer = setTimeout(done, waitMs);
39291
39490
  signal?.addEventListener("abort", done, { once: true });
@@ -39376,11 +39575,11 @@ async function runServerCommandIntake(options) {
39376
39575
  }
39377
39576
  }
39378
39577
  options.onPoll?.();
39379
- const reconcile = await new Promise((resolve42) => {
39578
+ const reconcile = await new Promise((resolve43) => {
39380
39579
  const done = (needed) => {
39381
39580
  signal?.removeEventListener("abort", aborted);
39382
39581
  wake = void 0;
39383
- resolve42(needed);
39582
+ resolve43(needed);
39384
39583
  };
39385
39584
  const aborted = () => done(false);
39386
39585
  wake = done;
@@ -39422,9 +39621,9 @@ async function runServerCommandIntake(options) {
39422
39621
  // apps/body/dist/monolith-corner-turn.js
39423
39622
  import { execFile as execFile12 } from "node:child_process";
39424
39623
  import { createHash as createHash12 } from "node:crypto";
39425
- import { mkdir as mkdir25, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
39426
- import { homedir as homedir17, hostname as hostname3 } from "node:os";
39427
- import { dirname as dirname25, join as join18, resolve as resolve34 } from "node:path";
39624
+ import { mkdir as mkdir25, readFile as readFile27, writeFile as writeFile20 } from "node:fs/promises";
39625
+ import { homedir as homedir18, hostname as hostname3 } from "node:os";
39626
+ import { dirname as dirname25, join as join18, resolve as resolve35 } from "node:path";
39428
39627
  import { promisify as promisify7 } from "node:util";
39429
39628
 
39430
39629
  // apps/body/dist/agent-command-catalog.js
@@ -39443,7 +39642,7 @@ function agentCommandCatalogPublisher(input) {
39443
39642
 
39444
39643
  // apps/body/dist/attachment-delivery.js
39445
39644
  import { mkdir as mkdir18, writeFile as writeFile17 } from "node:fs/promises";
39446
- import { basename as basename8, extname, join as join14 } from "node:path";
39645
+ import { basename as basename8, extname, join as join15 } from "node:path";
39447
39646
  var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
39448
39647
  var EXPIRED_REASON = "expired: these attachment bytes are past their retention window";
39449
39648
  var FETCH_TIMEOUT_MS = 3e4;
@@ -39493,6 +39692,10 @@ async function fetchBoundedBytes(url, fetchImpl = fetch) {
39493
39692
  return { status: response.status, ok: true, bytes, mimeType };
39494
39693
  }
39495
39694
  var MAX_INLINE_IMAGE_BYTES = 5 * 1024 * 1024;
39695
+ function safeAttachmentNames(attachments) {
39696
+ const taken = /* @__PURE__ */ new Set();
39697
+ return attachments.map((attachment, index) => safeFileName(attachment, index, taken));
39698
+ }
39496
39699
  function safeFileName(attachment, index, taken) {
39497
39700
  const raw = basename8(attachment.name ?? "").replace(/[^\w.-]+/g, "_").replace(/^\.+/, "");
39498
39701
  let name = raw || `attachment-${index + 1}`;
@@ -39506,8 +39709,7 @@ function safeFileName(attachment, index, taken) {
39506
39709
  async function deliverAttachments(attachments, dir, fetchImpl = fetch, cached = []) {
39507
39710
  if (!attachments.length)
39508
39711
  return [];
39509
- const taken = /* @__PURE__ */ new Set();
39510
- const names = attachments.map((attachment, index) => safeFileName(attachment, index, taken));
39712
+ const names = safeAttachmentNames(attachments);
39511
39713
  return Promise.all(attachments.map(async (attachment, index) => {
39512
39714
  const tooLarge = (bytes) => ({
39513
39715
  attachment,
@@ -39540,7 +39742,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch, cached =
39540
39742
  return { attachment, reason: EXPIRED_REASON };
39541
39743
  if (!fetched.ok)
39542
39744
  throw new Error(`HTTP ${fetched.status}`);
39543
- const path = join14(dir, names[index]);
39745
+ const path = join15(dir, names[index]);
39544
39746
  await writeFile17(path, fetched.bytes);
39545
39747
  const bytes = fetched.bytes;
39546
39748
  const mimeType = attachment.mimeType ?? fetched.mimeType;
@@ -39564,6 +39766,24 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch, cached =
39564
39766
  }
39565
39767
  }));
39566
39768
  }
39769
+ var MEDIA_ID = /\/v1\/media\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[?#]|$)/i;
39770
+ function attachmentId(attachment) {
39771
+ return MEDIA_ID.exec(attachment.url)?.[1]?.toLowerCase();
39772
+ }
39773
+ function readableSize(bytes) {
39774
+ if (bytes < 1024)
39775
+ return `${bytes} B`;
39776
+ if (bytes < 1024 * 1024)
39777
+ return `${Math.round(bytes / 1024)} KB`;
39778
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
39779
+ }
39780
+ function attachmentMarkerLines(attachments) {
39781
+ return attachments.map((attachment) => {
39782
+ const metadata = [attachment.mimeType, attachment.size ? readableSize(attachment.size) : ""].filter(Boolean).join(", ");
39783
+ const id = attachmentId(attachment);
39784
+ return `\u{1F4CE} ${attachment.name ?? "attachment"}${metadata ? ` (${metadata})` : ""} ${id ? `id=${id}` : "id unavailable"}${attachment.expired ? " (expired)" : ""}`;
39785
+ });
39786
+ }
39567
39787
  function withoutImageData(delivered) {
39568
39788
  return delivered.map(({ image: _image, ...rest }) => rest);
39569
39789
  }
@@ -39742,6 +39962,7 @@ var AgentTurnStream = class {
39742
39962
  * one bounds the lane at ONE write in flight plus ONE waiting.
39743
39963
  */
39744
39964
  pending;
39965
+ latestChunk = "";
39745
39966
  /**
39746
39967
  * The stream as it stood when a corner handed everything up to that point to
39747
39968
  * its durable work ledger — this turn's persisted stream offset. Held as the
@@ -39773,6 +39994,7 @@ var AgentTurnStream = class {
39773
39994
  * it has.
39774
39995
  */
39775
39996
  onChunk = (_delta, full, currentRun) => {
39997
+ this.latestChunk = sanitizeAgentReply(currentRun ?? "");
39776
39998
  this.latest = full;
39777
39999
  this.latestRun = currentRun ?? "";
39778
40000
  const text2 = sanitizeAgentReply(this.tailOf(full));
@@ -39854,9 +40076,10 @@ var AgentTurnStream = class {
39854
40076
  if (this.inFlight || this.pending === void 0)
39855
40077
  return;
39856
40078
  const text2 = this.pending;
40079
+ const latestChunk = this.latestChunk;
39857
40080
  this.pending = void 0;
39858
40081
  const { api, agentId, roomId, requestId, label } = this.options;
39859
- this.inFlight = api.execute("postAgentDraft", { agentId, roomId, turnId: requestId, text: text2 }).then(() => void 0).catch((error) => console.error(`[thin-core] ${label} draft publish failed:`, error)).then(() => {
40082
+ this.inFlight = api.execute("postAgentDraft", { agentId, roomId, turnId: requestId, text: text2, latestChunk }).then(() => void 0).catch((error) => console.error(`[thin-core] ${label} draft publish failed:`, error)).then(() => {
39860
40083
  this.inFlight = void 0;
39861
40084
  this.publishPending();
39862
40085
  });
@@ -39884,6 +40107,7 @@ var AgentTurnStream = class {
39884
40107
  beginRun() {
39885
40108
  this.latest = "";
39886
40109
  this.latestRun = "";
40110
+ this.latestChunk = "";
39887
40111
  this.persisted = "";
39888
40112
  this.pending = void 0;
39889
40113
  }
@@ -40034,15 +40258,17 @@ function mountedMcpSet(servers) {
40034
40258
 
40035
40259
  // apps/body/dist/pi-mcp-bridge.js
40036
40260
  import { execFile as execFile9 } from "node:child_process";
40037
- import { mkdtemp as mkdtemp2, mkdir as mkdir19, readFile as readFile22, rm as rm11, unlink as unlink8 } from "node:fs/promises";
40261
+ import { mkdtemp as mkdtemp2, mkdir as mkdir19, readFile as readFile23, rm as rm11, unlink as unlink8 } from "node:fs/promises";
40038
40262
  import { tmpdir as tmpdir5 } from "node:os";
40039
40263
  import { resolve as resolve28 } from "node:path";
40040
40264
  import { promisify as promisify5 } from "node:util";
40041
40265
  var PI_MCP_BRIDGE_FILENAME = "beeline-mcp-bridge.js";
40042
40266
  var execFileAsync5 = promisify5(execFile9);
40043
40267
  var nativeMcpProbes = /* @__PURE__ */ new Map();
40044
- async function piHasNativeMcp(command) {
40045
- let probe = nativeMcpProbes.get(command);
40268
+ async function piHasNativeMcp(command, env) {
40269
+ const path = env?.PATH ?? process.env.PATH ?? "";
40270
+ const cacheKey = `${command}\0${path}`;
40271
+ let probe = nativeMcpProbes.get(cacheKey);
40046
40272
  if (!probe) {
40047
40273
  probe = (async () => {
40048
40274
  const home = await mkdtemp2(resolve28(tmpdir5(), "beeline-pi-mcp-probe-"));
@@ -40050,7 +40276,7 @@ async function piHasNativeMcp(command) {
40050
40276
  const { stdout: stdout6 } = await execFileAsync5(command, ["mcp", "list", "--json"], {
40051
40277
  cwd: home,
40052
40278
  timeout: 1200,
40053
- env: { PATH: process.env.PATH, HOME: home, PI_CODING_AGENT_DIR: home, PI_OFFLINE: "1" }
40279
+ env: { PATH: path, HOME: home, PI_CODING_AGENT_DIR: home, PI_OFFLINE: "1" }
40054
40280
  });
40055
40281
  const result = JSON.parse(stdout6);
40056
40282
  return Array.isArray(result.servers) && Array.isArray(result.errors);
@@ -40060,13 +40286,13 @@ async function piHasNativeMcp(command) {
40060
40286
  await rm11(home, { recursive: true, force: true });
40061
40287
  }
40062
40288
  })();
40063
- nativeMcpProbes.set(command, probe);
40289
+ nativeMcpProbes.set(cacheKey, probe);
40064
40290
  }
40065
40291
  return probe;
40066
40292
  }
40067
40293
  async function isolatedPiConfig(piHome) {
40068
40294
  try {
40069
- const parsed = JSON.parse(await readFile22(resolve28(piHome, "mcp.json"), "utf8"));
40295
+ const parsed = JSON.parse(await readFile23(resolve28(piHome, "mcp.json"), "utf8"));
40070
40296
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
40071
40297
  } catch (error) {
40072
40298
  if (error.code !== "ENOENT")
@@ -40124,7 +40350,7 @@ async function installPiMcpBridge(input) {
40124
40350
  const path = resolve28(directory, PI_MCP_BRIDGE_FILENAME);
40125
40351
  try {
40126
40352
  const config = await isolatedPiConfig(input.piHome);
40127
- const nativeMcp = input.nativeMcp ?? await piHasNativeMcp(input.piCommand ?? "pi");
40353
+ const nativeMcp = input.nativeMcp ?? await piHasNativeMcp(input.piCommand ?? "pi", input.agentEnv);
40128
40354
  if (nativeMcp) {
40129
40355
  const mcpServers2 = { ...config.mcpServers };
40130
40356
  for (const server of input.servers)
@@ -40345,20 +40571,22 @@ export default async function (pi) {
40345
40571
 
40346
40572
  // apps/body/dist/corner-branch-sync.js
40347
40573
  import { execFile as execFile10 } from "node:child_process";
40574
+ import { access as access2, readFile as readFile24 } from "node:fs/promises";
40575
+ import { resolve as resolve29 } from "node:path";
40348
40576
  var CORNER_GIT_SYNC_TIMEOUT_MS = 12e4;
40349
40577
  var CORNER_GIT_SYNC_KILL_GRACE_MS = 1e3;
40350
40578
  var CornerGitSyncInterruptedError = class extends Error {
40351
40579
  };
40352
40580
  async function syncCornerBranch(input) {
40353
- const git = async (args) => {
40581
+ const git = async (args, cleanup = false) => {
40354
40582
  const controller = new AbortController();
40355
- const signal = input.signal ? AbortSignal.any([input.signal, controller.signal]) : controller.signal;
40583
+ const signal = input.signal && !cleanup ? AbortSignal.any([input.signal, controller.signal]) : controller.signal;
40356
40584
  const timer = setTimeout(() => controller.abort(), CORNER_GIT_SYNC_TIMEOUT_MS);
40357
40585
  const interrupted = () => new CornerGitSyncInterruptedError(`corner git sync ${args[0]} ${controller.signal.aborted ? `timed out after ${CORNER_GIT_SYNC_TIMEOUT_MS}ms` : "stopped"}`);
40358
40586
  try {
40359
40587
  if (signal.aborted)
40360
40588
  throw interrupted();
40361
- return await new Promise((resolve42, reject) => {
40589
+ return await new Promise((resolve43, reject) => {
40362
40590
  let killTimer;
40363
40591
  let stdout6 = "";
40364
40592
  let failure = null;
@@ -40372,35 +40600,41 @@ async function syncCornerBranch(input) {
40372
40600
  });
40373
40601
  const abort = () => {
40374
40602
  if (!child) {
40375
- cleanup();
40603
+ cleanup2();
40376
40604
  reject(interrupted());
40377
40605
  } else {
40378
40606
  killTimer = setTimeout(() => child.kill("SIGKILL"), CORNER_GIT_SYNC_KILL_GRACE_MS);
40379
40607
  }
40380
40608
  };
40381
40609
  signal.addEventListener("abort", abort, { once: true });
40382
- const cleanup = () => {
40610
+ const cleanup2 = () => {
40383
40611
  signal.removeEventListener("abort", abort);
40384
40612
  if (killTimer)
40385
40613
  clearTimeout(killTimer);
40386
40614
  };
40387
40615
  if (child)
40388
40616
  child.once("close", () => {
40389
- cleanup();
40617
+ cleanup2();
40390
40618
  if (signal.aborted)
40391
40619
  reject(interrupted());
40392
40620
  else if (failure)
40393
40621
  reject(failure);
40394
40622
  else
40395
- resolve42(stdout6);
40623
+ resolve43(stdout6);
40396
40624
  });
40397
40625
  else
40398
- input.git(args).then(resolve42, reject).finally(cleanup);
40626
+ input.git(args).then(resolve43, reject).finally(cleanup2);
40399
40627
  });
40400
40628
  } finally {
40401
40629
  clearTimeout(timer);
40402
40630
  }
40403
40631
  };
40632
+ const marker = await readFile24(resolve29(input.worktreePath, ".git"), "utf8").catch(() => "");
40633
+ const gitDir = resolve29(input.worktreePath, marker.startsWith("gitdir: ") ? marker.slice(8).trim() : ".git");
40634
+ for (const directory of ["rebase-merge", "rebase-apply"]) {
40635
+ if (await access2(resolve29(gitDir, directory)).then(() => true, () => false))
40636
+ await git(["rebase", "--abort"], true);
40637
+ }
40404
40638
  const remoteRef = `refs/remotes/origin/${input.featureBranch}`;
40405
40639
  const fetched = await git([
40406
40640
  "fetch",
@@ -40427,12 +40661,12 @@ async function syncCornerBranch(input) {
40427
40661
  await git(behind ? ["merge", "--ff-only", remote] : ["rebase", remote]);
40428
40662
  return behind ? "fast-forwarded" : "rebased";
40429
40663
  } catch (error) {
40430
- if (error instanceof CornerGitSyncInterruptedError)
40431
- throw error;
40432
- await git(["rebase", "--abort"]).catch((error2) => {
40664
+ await git(["rebase", "--abort"], true).catch((error2) => {
40433
40665
  if (error2 instanceof CornerGitSyncInterruptedError)
40434
40666
  throw error2;
40435
40667
  });
40668
+ if (error instanceof CornerGitSyncInterruptedError)
40669
+ throw error;
40436
40670
  await git(["reset", "--hard", remote]);
40437
40671
  return "realigned";
40438
40672
  }
@@ -40446,7 +40680,8 @@ async function contains(git, head, ancestor) {
40446
40680
  }
40447
40681
 
40448
40682
  // apps/body/dist/room-session.js
40449
- import { resolve as resolve29 } from "node:path";
40683
+ import { resolve as resolve30 } from "node:path";
40684
+ import { homedir as homedir16 } from "node:os";
40450
40685
 
40451
40686
  // apps/body/dist/read-only-policy.js
40452
40687
  var READ_ONLY_MCP_SERVER_NAME = "beeline-readonly-mcp";
@@ -40655,6 +40890,7 @@ function beelineAgentMcpServer(config, api, context) {
40655
40890
  { name: "BEELINE_DAEMON_TOKEN", value: connection.daemonToken },
40656
40891
  { name: "BEELINE_HELPER_VERSION", value: connection.helperVersion },
40657
40892
  { name: "BEELINE_DAEMON_AGENT_ID", value: connection.agentId },
40893
+ { name: "BEELINE_OPERATOR_HOME", value: config.operatorHome ?? homedir16() },
40658
40894
  { name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
40659
40895
  { name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
40660
40896
  ...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
@@ -40681,21 +40917,21 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
40681
40917
  command: config.readonlyMcpCommand,
40682
40918
  args: [...config.readonlyMcpArgs ?? []],
40683
40919
  env: [
40684
- { name: "BEELINE_READONLY_ROOT", value: resolve29(cwd) },
40920
+ { name: "BEELINE_READONLY_ROOT", value: resolve30(cwd) },
40685
40921
  ...config.agentHomeRoot ? [
40686
40922
  {
40687
40923
  name: "BEELINE_READONLY_AGENT_SKILLS_ROOT",
40688
- value: resolve29(config.agentHomeRoot, skillDir, "skills")
40924
+ value: resolve30(config.agentHomeRoot, skillDir, "skills")
40689
40925
  }
40690
40926
  ] : [],
40691
- ...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve29(agentMemoryDir) }] : []
40927
+ ...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve30(agentMemoryDir) }] : []
40692
40928
  ]
40693
40929
  };
40694
40930
  }
40695
40931
 
40696
40932
  // apps/body/dist/institutional-context.js
40697
40933
  import { performance as performance2 } from "node:perf_hooks";
40698
- var INSTITUTIONAL_CONTEXT_TIMEOUT_MS = 200;
40934
+ var INSTITUTIONAL_CONTEXT_TIMEOUT_MS = 500;
40699
40935
  var INSTITUTIONAL_MEMORY_LIVE_FLAG = "BEELINE_INSTITUTIONAL_MEMORY_ENABLED";
40700
40936
  function institutionalMemoryFlagEnabled(env, flag) {
40701
40937
  return env[flag] !== "false";
@@ -40742,15 +40978,15 @@ async function awaitInstitutionalContext(fetch2, log2 = console.warn, budgetMs =
40742
40978
 
40743
40979
  // apps/body/dist/codegraph.js
40744
40980
  import { execFile as execFile11 } from "node:child_process";
40745
- import { access as access2, appendFile, mkdir as mkdir20, readFile as readFile23 } from "node:fs/promises";
40746
- import { dirname as dirname22, isAbsolute as isAbsolute3, resolve as resolve30 } from "node:path";
40981
+ import { access as access3, appendFile, mkdir as mkdir20, readFile as readFile25 } from "node:fs/promises";
40982
+ import { dirname as dirname22, isAbsolute as isAbsolute3, resolve as resolve31 } from "node:path";
40747
40983
  import { promisify as promisify6 } from "node:util";
40748
40984
  var execFileAsync6 = promisify6(execFile11);
40749
40985
  var CODEGRAPH_MCP_SERVER_NAME = "codegraph";
40750
40986
  var CODEGRAPH_INDEX_PATH = ".codegraph/codegraph.db";
40751
40987
  var CODEGRAPH_PREPARE_TIMEOUT_MS = 12e4;
40752
40988
  function codegraphIndexDirectory(cwd) {
40753
- return resolve30(cwd, dirname22(CODEGRAPH_INDEX_PATH));
40989
+ return resolve31(cwd, dirname22(CODEGRAPH_INDEX_PATH));
40754
40990
  }
40755
40991
  function codegraphMcpServer(config, cwd, options) {
40756
40992
  if (!config.codegraphCommand)
@@ -40758,7 +40994,7 @@ function codegraphMcpServer(config, cwd, options) {
40758
40994
  return {
40759
40995
  name: CODEGRAPH_MCP_SERVER_NAME,
40760
40996
  command: config.codegraphCommand,
40761
- args: ["serve", "--mcp", "--path", resolve30(cwd), ...options.readonly ? ["--no-watch"] : []],
40997
+ args: ["serve", "--mcp", "--path", resolve31(cwd), ...options.readonly ? ["--no-watch"] : []],
40762
40998
  env: [
40763
40999
  { name: "CODEGRAPH_TELEMETRY", value: "0" },
40764
41000
  // One MCP child per harness session is easier to contain than a daemon
@@ -40776,7 +41012,7 @@ async function isGitWorktree(cwd) {
40776
41012
  cwd,
40777
41013
  timeout: 1e4
40778
41014
  });
40779
- return resolve30(stdout6.trim()) === resolve30(cwd);
41015
+ return resolve31(stdout6.trim()) === resolve31(cwd);
40780
41016
  } catch {
40781
41017
  return false;
40782
41018
  }
@@ -40790,9 +41026,9 @@ async function excludeIndexFromGitStatus(cwd) {
40790
41026
  const gitPath = stdout6.trim();
40791
41027
  if (!gitPath)
40792
41028
  return;
40793
- const path = isAbsolute3(gitPath) ? gitPath : resolve30(cwd, gitPath);
41029
+ const path = isAbsolute3(gitPath) ? gitPath : resolve31(cwd, gitPath);
40794
41030
  await mkdir20(dirname22(path), { recursive: true });
40795
- const existing = await readFile23(path, "utf8").catch(() => "");
41031
+ const existing = await readFile25(path, "utf8").catch(() => "");
40796
41032
  if (existing.split("\n").includes(".codegraph/"))
40797
41033
  return;
40798
41034
  await appendFile(path, `${existing && !existing.endsWith("\n") ? "\n" : ""}.codegraph/
@@ -40805,8 +41041,8 @@ async function prepareCodegraphIndex(config, cwd) {
40805
41041
  if (!command || !await isGitWorktree(cwd))
40806
41042
  return false;
40807
41043
  await excludeIndexFromGitStatus(cwd);
40808
- const indexPath = resolve30(cwd, CODEGRAPH_INDEX_PATH);
40809
- const indexed = await access2(indexPath).then(() => true, () => false);
41044
+ const indexPath = resolve31(cwd, CODEGRAPH_INDEX_PATH);
41045
+ const indexed = await access3(indexPath).then(() => true, () => false);
40810
41046
  const args = indexed ? ["sync", "--quiet", cwd] : ["init", "--yes", cwd];
40811
41047
  try {
40812
41048
  await execFileAsync6(command, args, {
@@ -40824,38 +41060,16 @@ async function prepareCodegraphIndex(config, cwd) {
40824
41060
 
40825
41061
  // apps/body/dist/monolith-room-turn.js
40826
41062
  import { mkdir as mkdir22, writeFile as writeFile18 } from "node:fs/promises";
40827
- import { homedir as homedir16, hostname as hostname2 } from "node:os";
41063
+ import { homedir as homedir17, hostname as hostname2 } from "node:os";
40828
41064
  import { dirname as dirname23, join as join16 } from "node:path";
40829
41065
 
40830
41066
  // packages/api-contract/dist/scheduled-prompts.js
40831
41067
  var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
40832
41068
  var SCHEDULE_RAN_VERB = "ran a schedule for";
40833
41069
 
40834
- // apps/body/dist/model-context-window.js
40835
- import { readFile as readFile24 } from "node:fs/promises";
40836
- import { join as join15 } from "node:path";
40837
- async function modelContextWindowTokens(model, piHome) {
40838
- if (!model || !piHome)
40839
- return void 0;
40840
- try {
40841
- const catalog = JSON.parse(await readFile24(join15(piHome, "models.json"), "utf8"));
40842
- const parts = model.split("/");
40843
- const preferredProvider = parts.length > 2 ? parts.shift() : void 0;
40844
- const modelId = parts.join("/");
40845
- const providers = preferredProvider ? [catalog.providers?.[preferredProvider]].filter(Boolean) : Object.values(catalog.providers ?? {});
40846
- for (const provider of providers) {
40847
- const window = provider?.models?.find((entry) => entry.id === modelId)?.contextWindow;
40848
- if (window && Number.isFinite(window) && window > 0)
40849
- return Math.floor(window);
40850
- }
40851
- } catch {
40852
- }
40853
- return void 0;
40854
- }
40855
-
40856
41070
  // apps/body/dist/turn-trace.js
40857
41071
  import { appendFile as appendFile2, mkdir as mkdir21, readdir as readdir7, rm as rm12 } from "node:fs/promises";
40858
- import { resolve as resolve31 } from "node:path";
41072
+ import { resolve as resolve32 } from "node:path";
40859
41073
  import { performance as performance3 } from "node:perf_hooks";
40860
41074
  var TURN_PHASES = [
40861
41075
  /** Enqueued on `SessionScheduler` until this turn holds a slot. A capacity wait lives here. */
@@ -40891,6 +41105,7 @@ var TurnTrace = class {
40891
41105
  atAdmission;
40892
41106
  finished = false;
40893
41107
  promptSections;
41108
+ promptWindow;
40894
41109
  searchMemoryCalls;
40895
41110
  searchMemoryMisses;
40896
41111
  /** Every distinct tool call id this turn has seen, across retries. */
@@ -40972,6 +41187,10 @@ var TurnTrace = class {
40972
41187
  notePromptSections(report) {
40973
41188
  this.promptSections = Object.fromEntries(report.map((entry) => [entry.id, entry.bytes]));
40974
41189
  }
41190
+ /** Assembled text only; the harness's tool schema is outside this measurement. */
41191
+ notePromptWindow(prompt, modelContextTokens) {
41192
+ this.promptWindow = modelContextTokens === void 0 ? void 0 : { promptBytes: Buffer.byteLength(prompt), modelContextTokens };
41193
+ }
40975
41194
  /** The prompt left for the harness. Idempotent: a steer resume is the same attempt. */
40976
41195
  promptSent() {
40977
41196
  this.current.promptSentAt ??= this.now();
@@ -41081,6 +41300,7 @@ var TurnTrace = class {
41081
41300
  ...this.atAdmission ? { atAdmission: this.atAdmission } : {}
41082
41301
  },
41083
41302
  ...this.promptSections ? { promptSections: this.promptSections } : {},
41303
+ ...this.promptWindow ? { promptWindow: this.promptWindow } : {},
41084
41304
  ...this.searchMemoryCalls !== void 0 ? { searchMemoryCalls: this.searchMemoryCalls, searchMemoryMisses: this.searchMemoryMisses ?? 0 } : {}
41085
41305
  };
41086
41306
  }
@@ -41135,7 +41355,7 @@ function formatTurnTraceLine(record5) {
41135
41355
  return [head, ...record5.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
41136
41356
  }
41137
41357
  function turnTraceDirectory(runtimeDir) {
41138
- return resolve31(runtimeDir, "turn-traces");
41358
+ return resolve32(runtimeDir, "turn-traces");
41139
41359
  }
41140
41360
  var TURN_TRACE_RETENTION_DAYS = 7;
41141
41361
  function traceFileName(date) {
@@ -41152,7 +41372,7 @@ var TurnTraceFile = class {
41152
41372
  this.options = options;
41153
41373
  }
41154
41374
  path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
41155
- return resolve31(this.directory, traceFileName(now2));
41375
+ return resolve32(this.directory, traceFileName(now2));
41156
41376
  }
41157
41377
  write(record5) {
41158
41378
  this.tail = this.tail.catch(() => void 0).then(async () => {
@@ -41176,7 +41396,7 @@ var TurnTraceFile = class {
41176
41396
  this.prunedDay = day;
41177
41397
  const cutoff = traceFileName(new Date(now2.getTime() - TURN_TRACE_RETENTION_DAYS * 864e5));
41178
41398
  const names = await readdir7(this.directory).catch(() => []);
41179
- await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm12(resolve31(this.directory, name), { force: true })));
41399
+ await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm12(resolve32(this.directory, name), { force: true })));
41180
41400
  }
41181
41401
  };
41182
41402
 
@@ -41224,7 +41444,7 @@ var WarmTranscript = class {
41224
41444
  };
41225
41445
 
41226
41446
  // apps/body/dist/monolith-room-turn.js
41227
- var ROOM_PROMPT_INACTIVITY_TIMEOUT_MS = 18e4;
41447
+ var ROOM_PROMPT_BACKSTOP_MS = TURN_BACKSTOP_MS;
41228
41448
  function isRoomMcpPermissionRequest(request, mountedServers = ROOM_MOUNTED_MCP_SERVERS, hostServers = CODE_OWNED_HOST_MCP_NAMES) {
41229
41449
  if (isHostMcpPermissionRequest(request, hostServers))
41230
41450
  return false;
@@ -41288,6 +41508,14 @@ function deviceGrantResumePrompt(device, earlierReply) {
41288
41508
  ].join(" ");
41289
41509
  }
41290
41510
  function resumePrompt(item) {
41511
+ if (item.systemEvent?.kind === "squire-approval-decided") {
41512
+ return [
41513
+ `This is Trusty Squire's answer to the approval you were waiting on: ${item.body}.`,
41514
+ "Your paused work resumes now.",
41515
+ "If it was approved, continue exactly where you left off with Trusty Squire; do not ask the person to approve again, and do not restart the task.",
41516
+ "If it was denied, stop that Trusty Squire action and say plainly what you cannot do."
41517
+ ].join(" ");
41518
+ }
41291
41519
  if (item.systemEvent?.kind === "connector-offer-decided") {
41292
41520
  return [
41293
41521
  `This is the answer to your connector offer: ${item.body}.`,
@@ -41344,6 +41572,7 @@ var MonolithRoomTurnLoop = class {
41344
41572
  * and the terminal receipt is posted on a path that may run after it.
41345
41573
  */
41346
41574
  turnMetrics = {};
41575
+ turnUsage = new TurnUsageAccumulator();
41347
41576
  /** OpenRouter providers this activation pinned, in order (C92). */
41348
41577
  pinnedProviders = [];
41349
41578
  /** The one provider re-pinned after an empty completion, until the session ends. */
@@ -41385,7 +41614,10 @@ var MonolithRoomTurnLoop = class {
41385
41614
  generationId: call.generationId,
41386
41615
  target: "squire",
41387
41616
  ...resourceCallFacts({ method: "tools/call", params: { name: call.tool, arguments: call.args } }, "squire")
41388
- })).allowed, options.config.operatorHome ?? homedir16());
41617
+ })).allowed, options.config.operatorHome ?? homedir17(), void 0, (decision2) => {
41618
+ options.api.execute("postSquireApprovalDecision", { roomId: options.roomId, ...decision2 }).catch(() => {
41619
+ });
41620
+ });
41389
41621
  this.options = { ...options, api: this.commandContext.bind(options.api) };
41390
41622
  options.grantRunner?.register(options.roomId, {
41391
41623
  workspaceId: options.workspaceId,
@@ -41420,7 +41652,7 @@ var MonolithRoomTurnLoop = class {
41420
41652
  ...this.sessionScratchDir ? { scratch: this.sessionScratchDir } : {},
41421
41653
  ...this.sessionStateDirs.length ? { harnessStateDirs: this.sessionStateDirs } : {},
41422
41654
  maskPaths: [
41423
- ...credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ?? homedir16()),
41655
+ ...credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ?? homedir17()),
41424
41656
  ...siblingAgentMaskPaths(runtimeDirectory(this.options.runtime.supervisorRoot, this.options.runtime.agent.publicKey))
41425
41657
  ]
41426
41658
  };
@@ -41523,10 +41755,10 @@ var MonolithRoomTurnLoop = class {
41523
41755
  * Missing numbers are omitted rather than zeroed.
41524
41756
  */
41525
41757
  async captureTurnMetrics() {
41526
- const usage2 = this.sessionId ? await readHarnessTurnUsage({ agentEnv: this.agentEnv, sessionId: this.sessionId }) : void 0;
41758
+ const { model: _model, ...usage2 } = this.turnUsage.usage ?? {};
41527
41759
  const promptBytes = this.client?.lastPromptBytes;
41528
41760
  return {
41529
- ...usage2 ? { inputTokens: usage2.inputTokens } : {},
41761
+ ...usage2,
41530
41762
  ...promptBytes ? { promptBytes } : {}
41531
41763
  };
41532
41764
  }
@@ -41668,7 +41900,7 @@ var MonolithRoomTurnLoop = class {
41668
41900
  effort: selectionEffort,
41669
41901
  fastMode: configuration.fastMode
41670
41902
  };
41671
- const operatorHome = this.options.config.operatorHome ?? homedir16();
41903
+ const operatorHome = this.options.config.operatorHome ?? homedir17();
41672
41904
  const registryHostDeclarations = registryMcpHostDeclarations(configuration.registryMcpRoutes, this.commandContext.path, this.options.config.registryMcpBrokerSocket);
41673
41905
  const mountedHostRoutes = [...grantedHostRoutes, ...Object.keys(registryHostDeclarations)];
41674
41906
  const squireScope = {
@@ -41802,6 +42034,7 @@ var MonolithRoomTurnLoop = class {
41802
42034
  agentCommand: harnessLabel,
41803
42035
  piHome: agentEnv.PI_CODING_AGENT_DIR,
41804
42036
  piCommand: agentEnv.PI_ACP_PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND,
42037
+ agentEnv,
41805
42038
  servers: [...servers, ...grantedRouteServers]
41806
42039
  });
41807
42040
  const mountedServers = [
@@ -42013,6 +42246,7 @@ var MonolithRoomTurnLoop = class {
42013
42246
  const api = this.options.api;
42014
42247
  this.busy = true;
42015
42248
  this.turnMetrics = {};
42249
+ this.turnUsage = new TurnUsageAccumulator();
42016
42250
  const trace = this.beginTurnTrace(item.id);
42017
42251
  const institutionalContextFetch = startInstitutionalContextFetch(api, this.options.roomId);
42018
42252
  let statsStartedAt = 0;
@@ -42033,8 +42267,8 @@ var MonolithRoomTurnLoop = class {
42033
42267
  let timer;
42034
42268
  const stats = statsPromise && remaining > 0 ? await Promise.race([
42035
42269
  statsPromise,
42036
- new Promise((resolve42) => {
42037
- timer = setTimeout(() => resolve42(void 0), remaining);
42270
+ new Promise((resolve43) => {
42271
+ timer = setTimeout(() => resolve43(void 0), remaining);
42038
42272
  })
42039
42273
  ]).finally(() => {
42040
42274
  if (timer)
@@ -42095,7 +42329,7 @@ var MonolithRoomTurnLoop = class {
42095
42329
  const transcriptRows = conversation.items.filter((message) => message.type === "message" && message.id !== item.id && !active.steers.some((steerItem) => steerItem.id === message.id)).slice(-80).map((message) => ({
42096
42330
  id: message.id,
42097
42331
  authorId: message.authorId,
42098
- line: roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id), this.acceptsImages(), message.id)
42332
+ line: transcriptMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, message.id)
42099
42333
  }));
42100
42334
  const command = this.commandContext.current;
42101
42335
  const grantDecision = command?.action === "resume";
@@ -42133,6 +42367,7 @@ var MonolithRoomTurnLoop = class {
42133
42367
  }
42134
42368
  });
42135
42369
  trace.notePromptSections(assembled.report);
42370
+ trace.notePromptWindow(assembled.text, this.modelContextTokens);
42136
42371
  this.commandContext.notePromptSections([
42137
42372
  ...this.sessionPromptSectionIds,
42138
42373
  ...assembled.report.map((section) => section.id)
@@ -42156,14 +42391,14 @@ var MonolithRoomTurnLoop = class {
42156
42391
  try {
42157
42392
  stream.beginRun();
42158
42393
  trace.promptSent();
42159
- result2 = await this.client.sessionPrompt(this.sessionId, nextPrompt, ROOM_PROMPT_INACTIVITY_TIMEOUT_MS, (delta, full, currentRun) => {
42394
+ result2 = await this.turnUsage.measure({ agentEnv: this.agentEnv, sessionId: this.sessionId }, () => this.client.sessionPrompt(this.sessionId, nextPrompt, ROOM_PROMPT_BACKSTOP_MS, (delta, full, currentRun) => {
42160
42395
  trace.firstModelOutput();
42161
42396
  stream.onChunk(delta, full, currentRun);
42162
42397
  }, void 0, (calls) => {
42163
42398
  trace.toolCalls(calls);
42164
42399
  if (openedACorner(openCornerToolCall(calls)))
42165
42400
  liveCornerOpened = true;
42166
- });
42401
+ }));
42167
42402
  } catch (error) {
42168
42403
  promptError = error;
42169
42404
  }
@@ -42306,8 +42541,8 @@ var MonolithRoomTurnLoop = class {
42306
42541
  void finishTrace("cancelled").catch(() => void 0);
42307
42542
  return;
42308
42543
  }
42309
- if (liveCornerOpened && error instanceof AcpRequestTimeoutError && error.inactivity && error.method === "session/prompt") {
42310
- console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: inactivity timeout after opening a corner; the work continues in the corner`);
42544
+ if (liveCornerOpened && error instanceof AcpTurnBackstopError) {
42545
+ console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: turn_backstop after opening a corner; the work continues in the corner`);
42311
42546
  this.options.onCornerOpened?.();
42312
42547
  const lastRun = liveStream?.lastRunText ?? "";
42313
42548
  const streamed = isPureRetryNarration(lastRun) ? "" : durableReplyText(lastRun);
@@ -42330,8 +42565,6 @@ var MonolithRoomTurnLoop = class {
42330
42565
  void finishTrace("complete").catch(() => void 0);
42331
42566
  return;
42332
42567
  }
42333
- if (isSilentPromptTimeout(error))
42334
- await this.discardSession().catch(() => void 0);
42335
42568
  await liveStream?.retract().catch((retractError) => {
42336
42569
  console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
42337
42570
  });
@@ -42422,6 +42655,13 @@ function openCornerToolCall(calls) {
42422
42655
  function openedACorner(call) {
42423
42656
  return !!call && isCompletedToolCall(call);
42424
42657
  }
42658
+ function transcriptMessagePrompt(author, body, attachments, messageId) {
42659
+ return [
42660
+ `[message id: ${messageId}]`,
42661
+ `${author}: ${body.trim() || "(shared attachments)"}`,
42662
+ ...attachmentMarkerLines(attachments)
42663
+ ].join("\n");
42664
+ }
42425
42665
  function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true, messageId) {
42426
42666
  const message = body.trim() || "(shared attachments)";
42427
42667
  const rendered = author ? `${author}: ${message}` : message;
@@ -42434,9 +42674,9 @@ function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsI
42434
42674
 
42435
42675
  // apps/body/dist/corner-github-auth.js
42436
42676
  import { chmod as chmod9, mkdir as mkdir23, writeFile as writeFile19 } from "node:fs/promises";
42437
- import { delimiter as delimiter2, resolve as resolve32 } from "node:path";
42677
+ import { delimiter as delimiter2, resolve as resolve33 } from "node:path";
42438
42678
  async function installCornerGitHubWrappers(input) {
42439
- const bin = resolve32(input.root, "beeline-github-bin");
42679
+ const bin = resolve33(input.root, "beeline-github-bin");
42440
42680
  await mkdir23(bin, { recursive: true, mode: 448 });
42441
42681
  const common = {
42442
42682
  node: process.execPath,
@@ -42446,13 +42686,13 @@ async function installCornerGitHubWrappers(input) {
42446
42686
  featureBranch: input.featureBranch,
42447
42687
  targetBranch: input.targetBranch
42448
42688
  };
42449
- await writeLauncher(resolve32(bin, "git"), {
42689
+ await writeLauncher(resolve33(bin, "git"), {
42450
42690
  ...common,
42451
42691
  command: input.gitBinary,
42452
42692
  launcher: "git"
42453
42693
  });
42454
42694
  if (input.ghBinary)
42455
- await writeLauncher(resolve32(bin, "gh"), {
42695
+ await writeLauncher(resolve33(bin, "gh"), {
42456
42696
  ...common,
42457
42697
  command: input.ghBinary,
42458
42698
  launcher: "gh"
@@ -42546,7 +42786,7 @@ function cornerGitHubCommandRefusal(launcher, argv, featureBranch, targetBranch,
42546
42786
  }
42547
42787
  async function writeLauncher(path, config) {
42548
42788
  const source = `#!/usr/bin/env node
42549
- import { spawnSync } from 'node:child_process';
42789
+ import { spawn, spawnSync } from 'node:child_process';
42550
42790
  const config = ${JSON.stringify(config)};
42551
42791
  const cornerGitHubCommandRefusal = ${cornerGitHubCommandRefusal.toString()};
42552
42792
  const authFailure = /(?:authentication failed|bad credentials|could not read username|http(?:\\/\\d(?:\\.\\d)?)? 40[13]|status (?:code )?40[13])/i;
@@ -42561,7 +42801,28 @@ function resolvePushBranch(source) {
42561
42801
  return current.status === 0 ? { branch: current.stdout.trim() } : {};
42562
42802
  }
42563
42803
  const refusal = cornerGitHubCommandRefusal(config.launcher, process.argv.slice(2), config.featureBranch, config.targetBranch, resolvePushBranch);
42564
- if (refusal) { process.stderr.write(refusal + '\\n'); process.exit(1); }
42804
+ if (refusal) { process.stderr.write(refusal + '\\n'); process.exitCode = 1; }
42805
+ function needsToken(argv) {
42806
+ if (config.launcher === 'gh') return true;
42807
+ const optionsWithValue = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--super-prefix', '--config-env']);
42808
+ let index = 0;
42809
+ while (index < argv.length && argv[index].startsWith('-')) {
42810
+ if (argv[index] === '--version' || argv[index] === '--help') return false;
42811
+ index += optionsWithValue.has(argv[index]) ? 2 : 1;
42812
+ }
42813
+ // Unknown commands (including aliases) may contact a remote.
42814
+ const local = new Set([
42815
+ 'add', 'am', 'apply', 'bisect', 'blame', 'branch', 'cat-file', 'check-attr',
42816
+ 'check-ignore', 'check-ref-format', 'checkout', 'cherry-pick', 'clean', 'commit',
42817
+ 'config', 'describe', 'diff', 'diff-files', 'diff-index', 'diff-tree', 'for-each-ref',
42818
+ 'format-patch', 'fsck', 'gc', 'grep', 'hash-object', 'help', 'init', 'log', 'ls-files',
42819
+ 'ls-tree', 'merge', 'merge-base', 'mv', 'notes', 'rebase', 'reflog', 'reset',
42820
+ 'restore', 'rev-list', 'rev-parse', 'revert', 'rm', 'show', 'show-ref', 'stash',
42821
+ 'status', 'switch', 'symbolic-ref', 'tag', 'update-index', 'update-ref',
42822
+ 'verify-commit', 'verify-tag', 'worktree',
42823
+ ]);
42824
+ return !local.has(argv[index]);
42825
+ }
42565
42826
  function token() {
42566
42827
  let result;
42567
42828
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -42570,19 +42831,40 @@ function token() {
42570
42831
  if (attempt < 2) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250 * (attempt + 1));
42571
42832
  }
42572
42833
  process.stderr.write(result.stderr || 'Beeline could not refresh the repository credential.\\n');
42573
- process.exit(result.status || 1);
42834
+ process.exitCode = result.status || 1;
42835
+ return undefined;
42574
42836
  }
42575
- function run(value) {
42837
+ async function run(value) {
42576
42838
  const env = { ...process.env, GH_TOKEN: value, GITHUB_TOKEN: value, GIT_TERMINAL_PROMPT: '0' };
42577
- return spawnSync(config.command, process.argv.slice(2), { env, encoding: 'buffer', stdio: ['inherit', 'pipe', 'pipe'] });
42578
- }
42579
- let result = run(token());
42580
- const diagnostic = Buffer.concat([result.stdout || Buffer.alloc(0), result.stderr || Buffer.alloc(0)]).toString('utf8');
42581
- if (result.status !== 0 && authFailure.test(diagnostic)) result = run(token());
42582
- if (result.stdout) process.stdout.write(result.stdout);
42583
- if (result.stderr) process.stderr.write(result.stderr);
42584
- if (result.error) throw result.error;
42585
- process.exit(result.status ?? 1);
42839
+ const child = spawn(config.command, process.argv.slice(2), { env, stdio: ['inherit', 'pipe', 'pipe'] });
42840
+ let authenticationFailed = false;
42841
+ // Keep only enough diagnostic overlap to recognize an auth failure across chunks.
42842
+ for (const [source, destination] of [[child.stdout, process.stdout], [child.stderr, process.stderr]]) {
42843
+ let tail = '';
42844
+ source.on('data', (chunk) => {
42845
+ const diagnostic = tail + chunk.toString('utf8');
42846
+ authenticationFailed ||= authFailure.test(diagnostic);
42847
+ tail = diagnostic.slice(-1024);
42848
+ });
42849
+ source.pipe(destination, { end: false });
42850
+ }
42851
+ return new Promise((resolve, reject) => {
42852
+ child.once('error', reject);
42853
+ child.once('close', (status) => resolve({ status, authenticationFailed }));
42854
+ });
42855
+ }
42856
+ if (!refusal) {
42857
+ const authenticated = needsToken(process.argv.slice(2));
42858
+ const value = authenticated ? token() : '';
42859
+ if (value !== undefined) {
42860
+ let result = await run(value);
42861
+ if (authenticated && result.status !== 0 && result.authenticationFailed) {
42862
+ const refreshed = token();
42863
+ if (refreshed !== undefined) result = await run(refreshed);
42864
+ }
42865
+ process.exitCode ??= result.status ?? 1;
42866
+ }
42867
+ }
42586
42868
  `;
42587
42869
  await writeFile19(path, source, { mode: 448 });
42588
42870
  await chmod9(path, 448);
@@ -42591,29 +42873,29 @@ process.exit(result.status ?? 1);
42591
42873
  // apps/body/dist/warm-node-modules.js
42592
42874
  import { createHash as createHash11, randomUUID as randomUUID11 } from "node:crypto";
42593
42875
  import { constants as fsConstants3 } from "node:fs";
42594
- import { chmod as chmod10, copyFile as copyFile2, link, lstat as lstat4, mkdir as mkdir24, readdir as readdir8, readFile as readFile25, readlink as readlink2, rename as rename9, rm as rm13, stat as stat8, symlink as symlink3, utimes } from "node:fs/promises";
42595
- import { dirname as dirname24, join as join17, resolve as resolve33 } from "node:path";
42876
+ import { chmod as chmod10, copyFile as copyFile2, link, lstat as lstat4, mkdir as mkdir24, readdir as readdir8, readFile as readFile26, readlink as readlink2, rename as rename9, rm as rm13, stat as stat8, symlink as symlink3, utimes } from "node:fs/promises";
42877
+ import { dirname as dirname24, join as join17, resolve as resolve34 } from "node:path";
42596
42878
  var STORE_FORMAT = "v1";
42597
42879
  var STAGING_PREFIX = ".beeline-warm-";
42598
42880
  var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
42599
42881
  var WARM_STORE_MAX_ENTRIES = 3;
42600
42882
  function sharedNpmCacheDir(supervisorRoot) {
42601
- return resolve33(supervisorRoot, "beeline", "npm-cache");
42883
+ return resolve34(supervisorRoot, "beeline", "npm-cache");
42602
42884
  }
42603
42885
  function sharedPnpmStoreDir(supervisorRoot) {
42604
- return resolve33(supervisorRoot, "beeline", "pnpm-store");
42886
+ return resolve34(supervisorRoot, "beeline", "pnpm-store");
42605
42887
  }
42606
42888
  function sharedCargoTargetDir(supervisorRoot) {
42607
- return resolve33(supervisorRoot, "beeline", "cargo-target");
42889
+ return resolve34(supervisorRoot, "beeline", "cargo-target");
42608
42890
  }
42609
42891
  function warmNodeModulesStoreDir(supervisorRoot) {
42610
- return resolve33(supervisorRoot, "beeline", "node-modules");
42892
+ return resolve34(supervisorRoot, "beeline", "node-modules");
42611
42893
  }
42612
42894
  function isPlanRefusal(value) {
42613
42895
  return "failure" in value;
42614
42896
  }
42615
42897
  async function readWarmPlan(worktreePath) {
42616
- const lockfile = await readFile25(resolve33(worktreePath, "package-lock.json")).catch(() => void 0);
42898
+ const lockfile = await readFile26(resolve34(worktreePath, "package-lock.json")).catch(() => void 0);
42617
42899
  if (!lockfile)
42618
42900
  return { failure: "no-lockfile" };
42619
42901
  const packages = parseLockfilePackages(lockfile);
@@ -42652,11 +42934,11 @@ async function seedWarmNodeModules(input) {
42652
42934
  return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
42653
42935
  }
42654
42936
  for (const tree of plan.trees) {
42655
- if (await pathExists(resolve33(input.worktreePath, tree))) {
42937
+ if (await pathExists(resolve34(input.worktreePath, tree))) {
42656
42938
  return { reason: "present", key: plan.key };
42657
42939
  }
42658
42940
  }
42659
- const entry = resolve33(input.storeRoot, plan.key);
42941
+ const entry = resolve34(input.storeRoot, plan.key);
42660
42942
  if (!await isDirectory(entry))
42661
42943
  return { reason: "cold", key: plan.key };
42662
42944
  const [storeDevice, checkoutDevice] = await Promise.all([
@@ -42666,19 +42948,19 @@ async function seedWarmNodeModules(input) {
42666
42948
  if (storeDevice === void 0 || storeDevice !== checkoutDevice) {
42667
42949
  return { reason: "cross-device", key: plan.key };
42668
42950
  }
42669
- const staging = resolve33(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID11()}`);
42951
+ const staging = resolve34(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID11()}`);
42670
42952
  const placed = [];
42671
42953
  const now2 = (input.now ?? Date.now)();
42672
42954
  try {
42673
42955
  await sweepStaleStaging(input.storeRoot, now2);
42674
42956
  await utimes(entry, now2 / 1e3, now2 / 1e3).catch(() => void 0);
42675
42957
  for (const tree of plan.trees) {
42676
- await cloneTree(resolve33(entry, tree), resolve33(staging, tree), seedFile);
42958
+ await cloneTree(resolve34(entry, tree), resolve34(staging, tree), seedFile);
42677
42959
  }
42678
42960
  for (const tree of plan.trees) {
42679
- const target = resolve33(input.worktreePath, tree);
42961
+ const target = resolve34(input.worktreePath, tree);
42680
42962
  await mkdir24(dirname24(target), { recursive: true });
42681
- await rename9(resolve33(staging, tree), target);
42963
+ await rename9(resolve34(staging, tree), target);
42682
42964
  placed.push(target);
42683
42965
  }
42684
42966
  return { reason: "seeded", key: plan.key };
@@ -42696,11 +42978,11 @@ async function harvestWarmNodeModules(input) {
42696
42978
  if (isPlanRefusal(plan)) {
42697
42979
  return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
42698
42980
  }
42699
- const entry = resolve33(input.storeRoot, plan.key);
42981
+ const entry = resolve34(input.storeRoot, plan.key);
42700
42982
  if (await pathExists(entry))
42701
42983
  return { reason: "already-warm", key: plan.key };
42702
42984
  for (const tree of plan.trees) {
42703
- if (!await isDirectory(resolve33(input.worktreePath, tree))) {
42985
+ if (!await isDirectory(resolve34(input.worktreePath, tree))) {
42704
42986
  return { reason: "no-node-modules", key: plan.key, detail: tree };
42705
42987
  }
42706
42988
  }
@@ -42712,12 +42994,12 @@ async function harvestWarmNodeModules(input) {
42712
42994
  detail: `${missing.length} absent, e.g. ${missing.slice(0, 3).join(", ")}`
42713
42995
  };
42714
42996
  }
42715
- const staging = resolve33(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID11()}`);
42997
+ const staging = resolve34(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID11()}`);
42716
42998
  try {
42717
42999
  await mkdir24(input.storeRoot, { recursive: true, mode: 493 });
42718
43000
  await sweepStaleStaging(input.storeRoot, (input.now ?? Date.now)());
42719
43001
  for (const tree of plan.trees) {
42720
- await cloneTree(resolve33(input.worktreePath, tree), resolve33(staging, tree), harvestFile);
43002
+ await cloneTree(resolve34(input.worktreePath, tree), resolve34(staging, tree), harvestFile);
42721
43003
  }
42722
43004
  await input.onCopied?.();
42723
43005
  if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
@@ -42740,16 +43022,16 @@ async function stagedTreeIsPublishable(worktreePath, staging, key) {
42740
43022
  return false;
42741
43023
  const hidden = join17("node_modules", ".package-lock.json");
42742
43024
  const [copied, current] = await Promise.all([
42743
- readFile25(resolve33(staging, hidden)).catch(() => void 0),
42744
- readFile25(resolve33(worktreePath, hidden)).catch(() => void 0)
43025
+ readFile26(resolve34(staging, hidden)).catch(() => void 0),
43026
+ readFile26(resolve34(worktreePath, hidden)).catch(() => void 0)
42745
43027
  ]);
42746
43028
  if (!copied || !current || !copied.equals(current))
42747
43029
  return false;
42748
43030
  return (await missingInstalledPackages(worktreePath, staging)).length === 0;
42749
43031
  }
42750
43032
  async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
42751
- const wanted = parseLockfilePackages(await readFile25(resolve33(worktreePath, "package-lock.json")).catch(() => void 0));
42752
- const installed = parseLockfilePackages(await readFile25(resolve33(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
43033
+ const wanted = parseLockfilePackages(await readFile26(resolve34(worktreePath, "package-lock.json")).catch(() => void 0));
43034
+ const installed = parseLockfilePackages(await readFile26(resolve34(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
42753
43035
  if (!wanted)
42754
43036
  return ["package-lock.json is unreadable"];
42755
43037
  if (!installed)
@@ -42769,7 +43051,7 @@ async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
42769
43051
  required.push(path);
42770
43052
  }
42771
43053
  await mapWithLimit(required, INTEGRITY_READ_CONCURRENCY, async (path) => {
42772
- if (!(path in installed) || !await isInstalledPackage(resolve33(treeRoot, path))) {
43054
+ if (!(path in installed) || !await isInstalledPackage(resolve34(treeRoot, path))) {
42773
43055
  missing.push(path);
42774
43056
  }
42775
43057
  });
@@ -43119,6 +43401,8 @@ var MonolithCornerTurnLoop = class {
43119
43401
  * and the terminal receipt is posted on a path that may run after it.
43120
43402
  */
43121
43403
  turnMetrics = {};
43404
+ turnUsage = new TurnUsageAccumulator();
43405
+ modelContextTokens;
43122
43406
  /** OpenRouter providers this activation pinned, in order (C92). */
43123
43407
  pinnedProviders = [];
43124
43408
  /** Whether the pinned model takes images; `undefined` when the pin did not say. */
@@ -43178,7 +43462,10 @@ var MonolithCornerTurnLoop = class {
43178
43462
  generationId: call.generationId,
43179
43463
  target: "squire",
43180
43464
  ...resourceCallFacts({ method: "tools/call", params: { name: call.tool, arguments: call.args } }, "squire")
43181
- })).allowed, options.config.operatorHome ?? homedir17());
43465
+ })).allowed, options.config.operatorHome ?? homedir18(), void 0, (decision2) => {
43466
+ options.api.execute("postSquireApprovalDecision", { roomId: options.cornerId, ...decision2 }).catch(() => {
43467
+ });
43468
+ });
43182
43469
  this.options = { ...options, api: this.commandContext.bind(options.api) };
43183
43470
  options.grantRunner?.register(options.cornerId, {
43184
43471
  workspaceId: options.workspaceId,
@@ -43266,10 +43553,10 @@ var MonolithCornerTurnLoop = class {
43266
43553
  * Missing numbers are omitted rather than zeroed.
43267
43554
  */
43268
43555
  async captureTurnMetrics() {
43269
- const usage2 = this.sessionId ? await readHarnessTurnUsage({ agentEnv: this.agentEnv, sessionId: this.sessionId }) : void 0;
43556
+ const { model: _model, ...usage2 } = this.turnUsage.usage ?? {};
43270
43557
  const promptBytes = this.client?.lastPromptBytes;
43271
43558
  return {
43272
- ...usage2 ? { inputTokens: usage2.inputTokens } : {},
43559
+ ...usage2,
43273
43560
  ...promptBytes ? { promptBytes } : {}
43274
43561
  };
43275
43562
  }
@@ -43393,7 +43680,7 @@ var MonolithCornerTurnLoop = class {
43393
43680
  effort: configuration.effort ?? this.options.config.modelSelection?.effort,
43394
43681
  fastMode: configuration.fastMode
43395
43682
  };
43396
- const operatorHome = this.options.config.operatorHome ?? homedir17();
43683
+ const operatorHome = this.options.config.operatorHome ?? homedir18();
43397
43684
  const registryHostDeclarations = registryMcpHostDeclarations(configuration.registryMcpRoutes, this.commandContext.path, this.options.config.registryMcpBrokerSocket);
43398
43685
  const mountedHostRoutes = [...grantedHostRoutes, ...Object.keys(registryHostDeclarations)];
43399
43686
  const squireScope = {
@@ -43466,6 +43753,7 @@ var MonolithCornerTurnLoop = class {
43466
43753
  CARGO_TARGET_DIR: cargoTargetDir
43467
43754
  };
43468
43755
  this.agentEnv = agentEnv;
43756
+ this.modelContextTokens = await modelContextWindowTokens(selection.model, agentEnv.PI_CODING_AGENT_DIR);
43469
43757
  const agentArgs = agentArgsWithModelSelection({
43470
43758
  kind: this.options.config.agentKind,
43471
43759
  command,
@@ -43605,6 +43893,7 @@ var MonolithCornerTurnLoop = class {
43605
43893
  agentCommand: harnessLabel,
43606
43894
  piHome: agentEnv.PI_CODING_AGENT_DIR,
43607
43895
  piCommand: agentEnv.PI_ACP_PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND,
43896
+ agentEnv,
43608
43897
  servers: [...servers, ...grantedRouteServers]
43609
43898
  });
43610
43899
  const persona = configuration.soul ?? self?.soul;
@@ -43693,7 +43982,7 @@ var MonolithCornerTurnLoop = class {
43693
43982
  briefRevision: restore.brief?.revision
43694
43983
  }),
43695
43984
  ...this.options.config.agentHomeRoot && this.options.config.agentKind && !AGENT_SKILL_DIRS.includes(this.options.config.agentKind) ? [
43696
- `This harness has no verified native skill discovery. Read the release-managed review procedure at ${resolve34(this.options.config.agentHomeRoot, "codex", "skills", "beeline-review", "SKILL.md")} before the verdict.`
43985
+ `This harness has no verified native skill discovery. Read the release-managed review procedure at ${resolve35(this.options.config.agentHomeRoot, "codex", "skills", "beeline-review", "SKILL.md")} before the verdict.`
43697
43986
  ] : []
43698
43987
  ].join("\n");
43699
43988
  } catch {
@@ -43825,8 +44114,8 @@ var MonolithCornerTurnLoop = class {
43825
44114
  let timer;
43826
44115
  const stats = statsPromise && remaining > 0 ? await Promise.race([
43827
44116
  statsPromise,
43828
- new Promise((resolve42) => {
43829
- timer = setTimeout(() => resolve42(void 0), remaining);
44117
+ new Promise((resolve43) => {
44118
+ timer = setTimeout(() => resolve43(void 0), remaining);
43830
44119
  })
43831
44120
  ]).finally(() => {
43832
44121
  if (timer)
@@ -43856,6 +44145,7 @@ var MonolithCornerTurnLoop = class {
43856
44145
  throw new Error("corner turn stopped for daemon handoff");
43857
44146
  this.busy = true;
43858
44147
  this.turnMetrics = {};
44148
+ this.turnUsage = new TurnUsageAccumulator();
43859
44149
  await this.syncBranch();
43860
44150
  const [conversation, roster, restored, activeReviewerInstruction, institutionalContext, memberCorners] = await trace.measure("context-fetch", () => Promise.all([
43861
44151
  api.execute("getRoomConversation", {
@@ -43883,7 +44173,7 @@ var MonolithCornerTurnLoop = class {
43883
44173
  const delivered = this.attachmentDir && allAttachments.length ? await deliverAttachments(allAttachments, join18(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : [];
43884
44174
  const briefFileLines = await Promise.all((restored.brief?.attachments ?? []).map(async (file, index) => {
43885
44175
  const entry = delivered.find((item) => item.attachment === briefAttachments[index]);
43886
- const verified = entry?.path && createHash12("sha256").update(await readFile26(entry.path)).digest("hex") === file.sha256;
44176
+ const verified = entry?.path && createHash12("sha256").update(await readFile27(entry.path)).digest("hex") === file.sha256;
43887
44177
  return `- ${file.title} (${file.purpose}; ${file.required ? "required" : "optional"}; sha256 ${file.sha256}): ${verified ? entry.path : "UNAVAILABLE OR CONTENT MISMATCH"}`;
43888
44178
  }));
43889
44179
  const missingRequiredBriefFile = (restored.brief?.attachments ?? []).some((file, index) => file.required && briefFileLines[index]?.includes("UNAVAILABLE OR CONTENT MISMATCH"));
@@ -43891,18 +44181,20 @@ var MonolithCornerTurnLoop = class {
43891
44181
  const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
43892
44182
  if (requestedBy)
43893
44183
  this.currentTurn = { requestId, requester: requestedBy };
43894
- const transcriptRows = conversation.items.filter((message) => message.id !== requestId && message.id !== sourceMessageId && message.body.trim()).slice(-120).map((message) => ({
44184
+ const transcriptRows = conversation.items.filter((message) => message.id !== requestId && message.id !== sourceMessageId && (message.body.trim() || message.attachments?.length)).slice(-120).map((message) => ({
43895
44185
  id: message.id,
43896
44186
  authorId: message.authorId,
43897
44187
  line: [
43898
44188
  ...message.type === "message" ? [`[message id: ${message.id}]`] : [],
43899
- `${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`
44189
+ `${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body.trim() ? message.body : "(shared attachments)"}`,
44190
+ ...attachmentMarkerLines(message.attachments ?? [])
43900
44191
  ].join("\n")
43901
44192
  }));
43902
44193
  const buildPrompt = () => {
43903
44194
  const transcript = this.warmTranscript.select(this.sessionId, transcriptRows, this.agent.publicKey);
43904
44195
  const assembled = assembleTurnPrompt({
43905
44196
  surface: this.sessionSurface,
44197
+ modelContextTokens: this.modelContextTokens,
43906
44198
  sessionPrefix: this.turnSessionPrefix,
43907
44199
  objective: this.options.objective,
43908
44200
  ...restored.titleGenerated && restored.title ? { generatedTitle: restored.title } : {},
@@ -43928,6 +44220,7 @@ var MonolithCornerTurnLoop = class {
43928
44220
  }
43929
44221
  });
43930
44222
  trace.notePromptSections(assembled.report);
44223
+ trace.notePromptWindow(assembled.text, this.modelContextTokens);
43931
44224
  this.commandContext.notePromptSections([
43932
44225
  ...this.sessionPromptSectionIds,
43933
44226
  ...assembled.report.map((section) => section.id)
@@ -44042,7 +44335,7 @@ var MonolithCornerTurnLoop = class {
44042
44335
  narrationRunBoundary = 0;
44043
44336
  currentNarrationRun = "";
44044
44337
  trace.promptSent();
44045
- return this.client.sessionPrompt(this.sessionId, promptWithImages(prompt, attachmentImageBlocks(delivered, this.acceptsImages())), ROOM_PROMPT_INACTIVITY_TIMEOUT_MS, (delta, full, currentRun, runs) => {
44338
+ return this.turnUsage.measure({ agentEnv: this.agentEnv, sessionId: this.sessionId }, () => this.client.sessionPrompt(this.sessionId, promptWithImages(prompt, attachmentImageBlocks(delivered, this.acceptsImages())), ROOM_PROMPT_BACKSTOP_MS, (delta, full, currentRun, runs) => {
44046
44339
  trace.firstModelOutput();
44047
44340
  if (runs) {
44048
44341
  completedNarrationRuns = runs.slice(narrationRunBoundary);
@@ -44059,7 +44352,7 @@ var MonolithCornerTurnLoop = class {
44059
44352
  trace.toolCalls(calls);
44060
44353
  publishToolCalls(calls, true);
44061
44354
  publishToolCalls(calls, false, lastNarratedToolCall);
44062
- });
44355
+ }));
44063
44356
  };
44064
44357
  let loginRetryUsed = false;
44065
44358
  const runPrompt = async (prompt = buildPrompt()) => {
@@ -44205,7 +44498,7 @@ ${deviceGrantResumePrompt(grantedDevice, result.agentText)}`);
44205
44498
  await trace.measure("publish", () => stream.settle(repeatedNarration ? "" : durableReply, durableReply && !repeatedNarration ? {
44206
44499
  ...requestedById ? { triggerMessageId: requestId } : {}
44207
44500
  } : {}));
44208
- }, { priority: "interactive", roomKey: cornerId });
44501
+ }, { priority: "interactive", roomKey: this.options.parentRoomId });
44209
44502
  }, (error) => console.error(`[thin-core] corner ${cornerId} receipt heartbeat failed:`, error));
44210
44503
  if (laneUpgraded) {
44211
44504
  void finishTrace("complete").catch(() => void 0);
@@ -44235,8 +44528,6 @@ ${deviceGrantResumePrompt(grantedDevice, result.agentText)}`);
44235
44528
  void finishTrace("complete").catch(() => void 0);
44236
44529
  return;
44237
44530
  }
44238
- if (isSilentPromptTimeout(error))
44239
- await this.discardSession().catch(() => void 0);
44240
44531
  const reason = distillTurnFailureReason(error);
44241
44532
  startTraceStats();
44242
44533
  await api.execute("postAgentTurnReceipt", {
@@ -44312,7 +44603,7 @@ ${deviceGrantResumePrompt(grantedDevice, result.agentText)}`);
44312
44603
  } catch (error) {
44313
44604
  if (attempt === 2)
44314
44605
  throw error;
44315
- await new Promise((resolve42) => setTimeout(resolve42, 250 * (attempt + 1)));
44606
+ await new Promise((resolve43) => setTimeout(resolve43, 250 * (attempt + 1)));
44316
44607
  }
44317
44608
  }
44318
44609
  if (!token)
@@ -44924,7 +45215,7 @@ function cornerStartConfigKey(repository, objective) {
44924
45215
  function cornerRepositoryCacheDir(supervisorRoot, remote) {
44925
45216
  const normalizedRemote = roomCheckoutRemote(remote);
44926
45217
  const repositoryHash = createHash13("sha256").update(normalizedRemote).digest("hex").slice(0, 24);
44927
- return resolve35(supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
45218
+ return resolve36(supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
44928
45219
  }
44929
45220
  function isStandingCornerStartFault(error) {
44930
45221
  const text2 = error instanceof Error ? error.message : String(error);
@@ -44933,11 +45224,11 @@ function isStandingCornerStartFault(error) {
44933
45224
  async function materializeCornerWorktree(input) {
44934
45225
  const remote = roomCheckoutRemote(input.remote);
44935
45226
  const gitCommonDir = cornerRepositoryCacheDir(input.supervisorRoot, remote);
44936
- const path = resolve35(input.supervisorRoot, "beeline", "corners", input.cornerId);
45227
+ const path = resolve36(input.supervisorRoot, "beeline", "corners", input.cornerId);
44937
45228
  await mkdir26(dirname26(gitCommonDir), { recursive: true, mode: 448 });
44938
45229
  await mkdir26(dirname26(path), { recursive: true, mode: 448 });
44939
45230
  const authEnv = githubGitEnv(input.token);
44940
- if (!existsSync8(resolve35(gitCommonDir, "HEAD"))) {
45231
+ if (!existsSync8(resolve36(gitCommonDir, "HEAD"))) {
44941
45232
  await execFileAsync8("git", ["clone", "--bare", remote, gitCommonDir], {
44942
45233
  env: authEnv,
44943
45234
  maxBuffer: 4 * 1024 * 1024
@@ -44960,7 +45251,7 @@ async function materializeCornerWorktree(input) {
44960
45251
  return false;
44961
45252
  throw error;
44962
45253
  });
44963
- if (!existsSync8(resolve35(path, ".git"))) {
45254
+ if (!existsSync8(resolve36(path, ".git"))) {
44964
45255
  await rm14(path, { recursive: true, force: true });
44965
45256
  await execFileAsync8("git", [
44966
45257
  `--git-dir=${gitCommonDir}`,
@@ -45026,9 +45317,9 @@ async function mapWithConcurrency(values, limit, visit) {
45026
45317
  }));
45027
45318
  }
45028
45319
  async function discoverCornerWorktree(supervisorRoot, cornerId) {
45029
- const cornersRoot = resolve35(supervisorRoot, "beeline", "corners");
45030
- const path = resolve35(cornersRoot, cornerId);
45031
- if (dirname26(path) !== cornersRoot || !existsSync8(resolve35(path, ".git")))
45320
+ const cornersRoot = resolve36(supervisorRoot, "beeline", "corners");
45321
+ const path = resolve36(cornersRoot, cornerId);
45322
+ if (dirname26(path) !== cornersRoot || !existsSync8(resolve36(path, ".git")))
45032
45323
  return void 0;
45033
45324
  const [canonicalRoot, canonicalPath] = await Promise.all([
45034
45325
  realpath2(cornersRoot),
@@ -45040,8 +45331,8 @@ async function discoverCornerWorktree(supervisorRoot, cornerId) {
45040
45331
  const common = await execFileAsync8("git", ["-C", path, "rev-parse", "--git-common-dir"]).catch(() => void 0);
45041
45332
  if (!common)
45042
45333
  return void 0;
45043
- const gitCommonDir = resolve35(path, common.stdout.trim());
45044
- const repositoriesRoot = resolve35(supervisorRoot, "beeline", "repositories");
45334
+ const gitCommonDir = resolve36(path, common.stdout.trim());
45335
+ const repositoriesRoot = resolve36(supervisorRoot, "beeline", "repositories");
45045
45336
  const commonRelative = relative3(repositoriesRoot, gitCommonDir);
45046
45337
  if (!commonRelative || commonRelative === ".." || commonRelative.startsWith(`..${sep2}`) || commonRelative.startsWith(sep2)) {
45047
45338
  throw new Error(`refusing corner ${cornerId} with git directory outside repository cache`);
@@ -45049,7 +45340,7 @@ async function discoverCornerWorktree(supervisorRoot, cornerId) {
45049
45340
  return { path, gitCommonDir };
45050
45341
  }
45051
45342
  async function localCornerWorktreeIds(supervisorRoot) {
45052
- const root = resolve35(supervisorRoot, "beeline", "corners");
45343
+ const root = resolve36(supervisorRoot, "beeline", "corners");
45053
45344
  const entries = await readdir9(root, { withFileTypes: true }).catch((error) => {
45054
45345
  if (error.code === "ENOENT")
45055
45346
  return [];
@@ -45139,8 +45430,8 @@ async function originContains(gitCommonDir, head) {
45139
45430
  }
45140
45431
  var execFileAsync8 = promisify8(execFile13);
45141
45432
  async function removeCornerScratchWorkspace(input) {
45142
- const expected = resolve35(input.roomRoot, "scratch");
45143
- if (resolve35(input.scratchPath) !== expected) {
45433
+ const expected = resolve36(input.roomRoot, "scratch");
45434
+ if (resolve36(input.scratchPath) !== expected) {
45144
45435
  throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
45145
45436
  }
45146
45437
  await rm14(expected, { recursive: true, force: true });
@@ -45252,7 +45543,7 @@ var RoomRuntimeCoordinator = class {
45252
45543
  ...fixedWorkspaceCeiling ? { maxLiveSessions: Number(fixedWorkspaceCeiling) } : {},
45253
45544
  perRoomLiveSessions: resolvePerRoomLiveSessions(process.env),
45254
45545
  workspaceFloor: Number(process.env.BUZZY_BODY_MAX_SESSIONS_FLOOR ?? String(DEFAULT_WORKSPACE_LIVE_SESSIONS_FLOOR)),
45255
- activeRoomCount: () => this.running.size,
45546
+ activeRoomCount: () => this.activeRoomCount(),
45256
45547
  idleMs: resolveSessionIdleMs(process.env),
45257
45548
  maxWarmSessions: resolveMaxWarmSessions(process.env),
45258
45549
  reserveInteractiveSlot: true
@@ -45282,7 +45573,7 @@ var RoomRuntimeCoordinator = class {
45282
45573
  this.discoveryWakeListener?.();
45283
45574
  }
45284
45575
  activeRoomCount() {
45285
- return this.running.size;
45576
+ return new Set([...this.running.keys()].map((id) => this.monolithCornerParents.get(id) ?? id)).size;
45286
45577
  }
45287
45578
  surfaceHealthSnapshot() {
45288
45579
  return this.surfaceHealth.snapshot();
@@ -45589,13 +45880,13 @@ var RoomRuntimeCoordinator = class {
45589
45880
  return this.runtime.rooms.find((room) => room.channelId === roomId);
45590
45881
  }
45591
45882
  roomRoot(roomId) {
45592
- return this.roomRecord(roomId)?.root ?? resolve35(dirname26(this.configPath), "rooms", roomId);
45883
+ return this.roomRecord(roomId)?.root ?? resolve36(dirname26(this.configPath), "rooms", roomId);
45593
45884
  }
45594
45885
  roomAgentHomeRoot(workspaceRoot, required = false) {
45595
45886
  const flag = process.env.BUZZY_BODY_ROOM_HOME;
45596
45887
  if (!required && flag === "0")
45597
45888
  return void 0;
45598
- const home = resolve35(workspaceRoot, "agent-home");
45889
+ const home = resolve36(workspaceRoot, "agent-home");
45599
45890
  if (!required && flag !== "1" && !existsSync8(home) && existsSync8(workspaceRoot))
45600
45891
  return void 0;
45601
45892
  try {
@@ -45611,8 +45902,8 @@ var RoomRuntimeCoordinator = class {
45611
45902
  return {
45612
45903
  ...this.baseConfig,
45613
45904
  workspaceRoot,
45614
- agentPrivateRoot: resolve35(workspaceRoot, "agent-private"),
45615
- agentMemoryRoot: resolve35(dirname26(this.configPath), "memory"),
45905
+ agentPrivateRoot: resolve36(workspaceRoot, "agent-private"),
45906
+ agentMemoryRoot: resolve36(dirname26(this.configPath), "memory"),
45616
45907
  openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname26(this.configPath)),
45617
45908
  turnTraceDir: turnTraceDirectory(dirname26(this.configPath)),
45618
45909
  ...agentHomeRoot ? { agentHomeRoot } : {}
@@ -45695,11 +45986,11 @@ var RoomRuntimeCoordinator = class {
45695
45986
  const remote = roomCheckoutRemote(repository.remote);
45696
45987
  const targetBranch = repository.targetBranch || "main";
45697
45988
  const checkoutId = createHash13("sha256").update(`${roomId}\0${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
45698
- const path = resolve35(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
45989
+ const path = resolve36(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
45699
45990
  await mkdir26(dirname26(path), { recursive: true, mode: 448 });
45700
45991
  const token = remote.startsWith("https://github.com/") ? await this.parentRepositoryToken(roomId) : void 0;
45701
45992
  const env = token ? githubGitEnv(token.token) : process.env;
45702
- if (!existsSync8(resolve35(path, ".git"))) {
45993
+ if (!existsSync8(resolve36(path, ".git"))) {
45703
45994
  await execFileAsync8("git", ["clone", "--no-checkout", remote, path], {
45704
45995
  env,
45705
45996
  maxBuffer: 4 * 1024 * 1024
@@ -45776,7 +46067,7 @@ var RoomRuntimeCoordinator = class {
45776
46067
  if (!objective)
45777
46068
  throw new Error("corner has no authoritative objective fact");
45778
46069
  const repositoryBacked = repository.resolution === "repository" && restore.lane !== "no_code";
45779
- const scratchPath = resolve35(this.roomRoot(corner.cornerId), "scratch");
46070
+ const scratchPath = resolve36(this.roomRoot(corner.cornerId), "scratch");
45780
46071
  if (repositoryBacked && existsSync8(scratchPath) && !restore.featureBranch)
45781
46072
  throw new Error("promoted corner has no server-assigned feature branch");
45782
46073
  const targetBranch = repositoryBacked ? repository.targetBranch || "main" : void 0;
@@ -45791,7 +46082,7 @@ var RoomRuntimeCoordinator = class {
45791
46082
  }) : void 0;
45792
46083
  if (worktree && existsSync8(scratchPath))
45793
46084
  await this.reapCornerScratch({ path: scratchPath, cornerId: corner.cornerId });
45794
- const workspacePath = worktree?.path ?? resolve35(this.roomRoot(corner.cornerId), "scratch");
46085
+ const workspacePath = worktree?.path ?? resolve36(this.roomRoot(corner.cornerId), "scratch");
45795
46086
  if (!worktree)
45796
46087
  await mkdir26(workspacePath, { recursive: true, mode: 448 });
45797
46088
  if (worktree && shouldPostInitialCornerWorkingState(restore)) {
@@ -46114,7 +46405,7 @@ var RoomRuntimeCoordinator = class {
46114
46405
  throw new Error(`archived corner ${corner.cornerId} has no authoritative repository`);
46115
46406
  }
46116
46407
  const expectedCommonDir = cornerRepositoryCacheDir(this.runtime.supervisorRoot, repository.remote);
46117
- if (resolve35(discovered.gitCommonDir) !== expectedCommonDir) {
46408
+ if (resolve36(discovered.gitCommonDir) !== expectedCommonDir) {
46118
46409
  throw new Error(`archived corner ${corner.cornerId} repository cache does not match`);
46119
46410
  }
46120
46411
  if (!restore.featureBranch) {
@@ -46465,10 +46756,10 @@ var ThinDaemonCore = class {
46465
46756
 
46466
46757
  // apps/body/dist/force-update-journal.js
46467
46758
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
46468
- import { resolve as resolve36 } from "node:path";
46759
+ import { resolve as resolve37 } from "node:path";
46469
46760
  var NAME = "force-update-interruptions.json";
46470
46761
  function pathFor(runtimeDir) {
46471
- return resolve36(runtimeDir, NAME);
46762
+ return resolve37(runtimeDir, NAME);
46472
46763
  }
46473
46764
  function read(runtimeDir) {
46474
46765
  try {
@@ -46546,17 +46837,17 @@ function staleGeneration(error) {
46546
46837
  init_launchd();
46547
46838
  init_systemd();
46548
46839
  import { mkdir as mkdir27, rename as rename10 } from "node:fs/promises";
46549
- import { dirname as dirname27, resolve as resolve37 } from "node:path";
46840
+ import { dirname as dirname27, resolve as resolve38 } from "node:path";
46550
46841
  async function retireRemovedAgent(runtime, options = {}) {
46551
- const deletedRoot = resolve37(runtime.supervisorRoot, "beeline", "deleted-runtimes");
46552
- const target = resolve37(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
46842
+ const deletedRoot = resolve38(runtime.supervisorRoot, "beeline", "deleted-runtimes");
46843
+ const target = resolve38(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
46553
46844
  return relocateAgentRuntime(runtime, target, {
46554
46845
  ...options.run ? { run: options.run } : {}
46555
46846
  });
46556
46847
  }
46557
46848
  async function relocateAgentRuntime(runtime, target, options = {}) {
46558
46849
  const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
46559
- const destination = resolve37(target);
46850
+ const destination = resolve38(target);
46560
46851
  if (destination === source || destination.startsWith(`${source}/`)) {
46561
46852
  throw new Error("agent runtime destination must be outside the live runtime");
46562
46853
  }
@@ -46579,8 +46870,8 @@ init_self_update();
46579
46870
  init_systemd();
46580
46871
 
46581
46872
  // apps/body/dist/release-status.js
46582
- import { readFile as readFile27, readdir as readdir10, rename as rename11, writeFile as writeFile21 } from "node:fs/promises";
46583
- import { resolve as resolve38 } from "node:path";
46873
+ import { readFile as readFile28, readdir as readdir10, rename as rename11, writeFile as writeFile21 } from "node:fs/promises";
46874
+ import { resolve as resolve39 } from "node:path";
46584
46875
  var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
46585
46876
  var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
46586
46877
  var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
@@ -46597,7 +46888,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
46597
46888
  pid: options.pid ?? process.pid,
46598
46889
  readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
46599
46890
  };
46600
- const target = resolve38(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
46891
+ const target = resolve39(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
46601
46892
  const temporary = `${target}.${status.pid}.tmp`;
46602
46893
  await writeFile21(temporary, `${JSON.stringify(status, null, 2)}
46603
46894
  `, { mode: 384 });
@@ -46607,7 +46898,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
46607
46898
 
46608
46899
  // apps/body/dist/scratch-sweep.js
46609
46900
  import { lstat as lstat5, readdir as readdir11, rmdir as rmdir2, unlink as unlink9 } from "node:fs/promises";
46610
- import { resolve as resolve39 } from "node:path";
46901
+ import { resolve as resolve40 } from "node:path";
46611
46902
  var DEFAULT_SCRATCH_TTL_HOURS = 72;
46612
46903
  var NEVER_SWEEP_SUBDIR_NAMES = new Set(HOME_SUBDIRS.filter((name) => name !== "tmp"));
46613
46904
  function scratchTtlMs(env = process.env) {
@@ -46616,7 +46907,7 @@ function scratchTtlMs(env = process.env) {
46616
46907
  return (Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_SCRATCH_TTL_HOURS) * 60 * 60 * 1e3;
46617
46908
  }
46618
46909
  async function discoverAttachScratchRoots(runtimeDir) {
46619
- const roomsDir = resolve39(runtimeDir, "rooms");
46910
+ const roomsDir = resolve40(runtimeDir, "rooms");
46620
46911
  let entries;
46621
46912
  try {
46622
46913
  entries = await readdir11(roomsDir, { withFileTypes: true });
@@ -46627,7 +46918,7 @@ async function discoverAttachScratchRoots(runtimeDir) {
46627
46918
  for (const entry of entries) {
46628
46919
  if (!entry.isDirectory())
46629
46920
  continue;
46630
- const home = resolve39(roomsDir, entry.name, "agent-home");
46921
+ const home = resolve40(roomsDir, entry.name, "agent-home");
46631
46922
  const stats = await lstat5(home).catch(() => void 0);
46632
46923
  if (stats?.isDirectory())
46633
46924
  roots.push(home);
@@ -46646,7 +46937,7 @@ async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
46646
46937
  for (const entry of entries) {
46647
46938
  if (protectNamesHere && NEVER_SWEEP_SUBDIR_NAMES.has(entry.name))
46648
46939
  continue;
46649
- const path = resolve39(dir, entry.name);
46940
+ const path = resolve40(dir, entry.name);
46650
46941
  const stats = await lstat5(path).catch(() => void 0);
46651
46942
  if (!stats || stats.isSymbolicLink())
46652
46943
  continue;
@@ -46731,7 +47022,7 @@ async function runAgentRuntime(configPath, host, signal) {
46731
47022
  BUZZ_DEV_MCP_BIN: runtime.mcpBinary
46732
47023
  };
46733
47024
  const config = loadBodyConfig({
46734
- workspaceRoot: resolve40(runtimeDir, "workspace"),
47025
+ workspaceRoot: resolve41(runtimeDir, "workspace"),
46735
47026
  llmEnvFile: runtime.llmEnvFile,
46736
47027
  env,
46737
47028
  agent
@@ -47021,7 +47312,7 @@ async function runAgentRuntime(configPath, host, signal) {
47021
47312
  return exitReason ?? lifecycle.stopReason ?? "stopped";
47022
47313
  }
47023
47314
  function registryBrokerSocketPath(home, agentId) {
47024
- return join19(home ?? homedir18(), ".beeline", "registry-mcp-broker", `${process.pid}-${agentId.slice(0, 8)}.sock`);
47315
+ return join19(home ?? homedir19(), ".beeline", "registry-mcp-broker", `${process.pid}-${agentId.slice(0, 8)}.sock`);
47025
47316
  }
47026
47317
  function interruptHostedAgent(agent) {
47027
47318
  agent.lifecycle.quiesce("force-update");
@@ -47591,7 +47882,7 @@ async function main() {
47591
47882
  const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
47592
47883
  if (!configPath || !roomId)
47593
47884
  throw new Error("corner-read-token requires --config and --room");
47594
- const activated = await activateDaemonTransport(resolve41(configPath));
47885
+ const activated = await activateDaemonTransport(resolve42(configPath));
47595
47886
  if (!activated)
47596
47887
  throw new Error("corner-read-token requires monolith transport");
47597
47888
  const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
@@ -47646,7 +47937,7 @@ async function main() {
47646
47937
  }
47647
47938
  if (!configPath && !args.includes("--machine"))
47648
47939
  throw new Error("daemon requires --machine, or --config <runtime.json> for one agent");
47649
- const { reason, lifecycle } = await runMachineHelper(configPath ? { configPaths: [resolve41(configPath)] } : {});
47940
+ const { reason, lifecycle } = await runMachineHelper(configPath ? { configPaths: [resolve42(configPath)] } : {});
47650
47941
  daemonLifecycle = lifecycle;
47651
47942
  lifecycle.exit(reason);
47652
47943
  return;