@coreplane/switchboard 1.260.6 → 1.261.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -195,7 +195,9 @@ async function ensureWorkArea(at, version2, workers, deps) {
195
195
  deps.log(`[deploy] materialised the shipped tree (version ${version2}) under ${at.workArea}`);
196
196
  }
197
197
  if (plan3.install.length > 0) {
198
- deps.log(`[deploy] npm ci --workspace ${plan3.install.join(" --workspace ")} under ${at.workArea}`);
198
+ deps.log(
199
+ `[deploy] npm ci --include-workspace-root --workspace ${plan3.install.join(" --workspace ")} under ${at.workArea}`
200
+ );
199
201
  const r = await deps.install(at.workArea, plan3.install);
200
202
  if (r.code !== 0)
201
203
  return {
@@ -241,10 +243,22 @@ function packageSourceOnHost(at = OPERATOR_ROOT) {
241
243
  function cliVersionOnHost(at = OPERATOR_ROOT) {
242
244
  return at.mode === "package" ? packageSourceOnHost(at).version : packageVersion();
243
245
  }
246
+ function workAreaNpmCiArgs(workspaces) {
247
+ return [
248
+ "ci",
249
+ "--include-workspace-root",
250
+ "--no-audit",
251
+ "--no-fund",
252
+ ...workspaces.flatMap((w) => ["--workspace", w])
253
+ ];
254
+ }
244
255
  function npmCi(cwd, workspaces) {
245
256
  return new Promise((resolve13) => {
246
- const args = ["ci", "--no-audit", "--no-fund", ...workspaces.flatMap((w) => ["--workspace", w])];
247
- const child = spawn("npm", args, { cwd, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
257
+ const child = spawn("npm", workAreaNpmCiArgs(workspaces), {
258
+ cwd,
259
+ env: process.env,
260
+ stdio: ["ignore", "pipe", "pipe"]
261
+ });
248
262
  let output = "";
249
263
  child.stdout.on("data", (c) => output += c.toString());
250
264
  child.stderr.on("data", (c) => output += c.toString());
@@ -3264,7 +3278,7 @@ function renewalDecision(input) {
3264
3278
  };
3265
3279
  }
3266
3280
  function renderRenewal(decision, grant2, options = {}) {
3267
- const recourse = options.idle ? "the next reply in this thread continues the unit" : "the next reply in this thread continues the original task";
3281
+ const recourse = options.idle ? "the next reply in this thread continues the unit" : "start ship again with the original task";
3268
3282
  const senders = options.senders?.length ? `, with ${options.senders.length} message${options.senders.length === 1 ? "" : "s"} from ${options.senders.join(", ")}` : "";
3269
3283
  if (decision.renew)
3270
3284
  return `budget renewed, ${decision.segment - 1} of ${grant2.renewals}, continues ${decision.from !== void 0 ? decision.from.slice(0, 7) : "the branch's head"}${senders}`;
@@ -3564,11 +3578,11 @@ function buildReviewPostBody(answer, verdict, target) {
3564
3578
  ...target ? [`head \`${target.head.slice(0, 7)}\``] : [],
3565
3579
  verdict ? findingCounts(verdict.findings) : "the run ended without a submit_verdict call"
3566
3580
  ];
3567
- const parts = [verdictLine(verdict), `> [!${calloutKind(verdict)}]
3581
+ const fixedParts = [verdictLine(verdict), `> [!${calloutKind(verdict)}]
3568
3582
  > ${facts2.join(" \xB7 ")}`];
3569
3583
  const findings = verdict?.findings ?? [];
3570
3584
  if (findings.length > 0) {
3571
- parts.push(
3585
+ fixedParts.push(
3572
3586
  [
3573
3587
  "| Severity | Finding | Where |",
3574
3588
  "| --- | --- | --- |",
@@ -3578,15 +3592,25 @@ function buildReviewPostBody(answer, verdict, target) {
3578
3592
  ].join("\n")
3579
3593
  );
3580
3594
  }
3595
+ const marker = verdictMarker(verdict, target);
3596
+ const fixedBody = [...fixedParts, marker].join("\n\n");
3581
3597
  const prose2 = answer.trim();
3582
- if (prose2) parts.push(`<details>
3598
+ if (!prose2) return fixedBody;
3599
+ const details2 = (text, truncated) => `<details>
3583
3600
  <summary>Full review</summary>
3584
3601
 
3585
- ${prose2}
3602
+ ${text}${truncated ? `${text ? "\n\n" : ""}${REVIEW_TRUNCATION_NOTE}` : ""}
3586
3603
 
3587
- </details>`);
3588
- parts.push(verdictMarker(verdict, target));
3589
- return parts.join("\n\n");
3604
+ </details>`;
3605
+ const render3 = (text, truncated) => [...fixedParts, details2(text, truncated), marker].join("\n\n");
3606
+ const full = render3(prose2, false);
3607
+ if ([...full].length <= MAX_REVIEW_POST_CODE_POINTS) return full;
3608
+ const clippedShell = render3("", true);
3609
+ const proseSeparatorCodePoints = 2;
3610
+ const proseBudget = MAX_REVIEW_POST_CODE_POINTS - [...clippedShell].length - proseSeparatorCodePoints;
3611
+ if (proseBudget > 0) return render3([...prose2].slice(0, proseBudget).join(""), true);
3612
+ if ([...clippedShell].length <= MAX_REVIEW_POST_CODE_POINTS) return clippedShell;
3613
+ return full;
3590
3614
  }
3591
3615
  function buildReviewChannelReply(input) {
3592
3616
  const { answer, verdict, posted, liveUrl } = input;
@@ -3675,7 +3699,7 @@ function redactVerdict(v, redact = redactSecrets) {
3675
3699
  function redactDispositions(dispositions, redact = redactSecrets) {
3676
3700
  return dispositions.map((d) => ({ findingId: d.findingId, disposition: d.disposition, note: redact(d.note) }));
3677
3701
  }
3678
- var FINDING_SEVERITIES, ADDRESS_SEVERITIES, DEFAULT_ADDRESS_SEVERITY, isAddressSeverity, severityRank, LGTM_TOKEN, CHANGES_TOKEN, NO_VERDICT_LINE, escapeMarkdownTableCell, unescapeMarkdownTableCell, isRepoPath, VERDICT_KINDS, DISPOSITION_KINDS, isRecordLike, REVIEW_POST_HEAD;
3702
+ var FINDING_SEVERITIES, ADDRESS_SEVERITIES, DEFAULT_ADDRESS_SEVERITY, isAddressSeverity, severityRank, LGTM_TOKEN, CHANGES_TOKEN, NO_VERDICT_LINE, MAX_REVIEW_POST_CODE_POINTS, REVIEW_TRUNCATION_NOTE, escapeMarkdownTableCell, unescapeMarkdownTableCell, isRepoPath, VERDICT_KINDS, DISPOSITION_KINDS, isRecordLike, REVIEW_POST_HEAD;
3679
3703
  var init_reviewVerdict = __esm({
3680
3704
  "../../src/core/reviewVerdict.ts"() {
3681
3705
  "use strict";
@@ -3690,6 +3714,8 @@ var init_reviewVerdict = __esm({
3690
3714
  LGTM_TOKEN = "LGTM:";
3691
3715
  CHANGES_TOKEN = "Changes requested:";
3692
3716
  NO_VERDICT_LINE = "No verdict submitted \u2014 not approving.";
3717
+ MAX_REVIEW_POST_CODE_POINTS = 65e3;
3718
+ REVIEW_TRUNCATION_NOTE = "_(review truncated to fit GitHub's review size limit)_";
3693
3719
  escapeMarkdownTableCell = (text) => oneLine2(text).replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
3694
3720
  unescapeMarkdownTableCell = (text) => text.replace(/\\([\\|])/g, "$1");
3695
3721
  isRepoPath = (file) => /^[^\s`]+$/.test(file) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(file);
@@ -3711,10 +3737,10 @@ function interruptionCauseOfWords(words) {
3711
3737
  return void 0;
3712
3738
  }
3713
3739
  function shipInterruptedNote(prUrl3, cause, idle = false) {
3714
- const stands = prUrl3 ? `Its work stands on GitHub: ${prUrl3}.` : "Whatever it pushed stands on its pipeline branch; no PR was opened yet.";
3715
- const continuation = idle ? "The next reply in this thread continues the unit." : prUrl3 ? `The pipeline kept its task, branch and pull request; the next reply in this thread continues the review loop at ${prUrl3}.` : "The pipeline kept its task and branch; the next reply in this thread starts round 0 again on that branch.";
3716
- const opening = cause === "container_replaced" ? "\u26A0\uFE0F The resident container running this pipeline's child was replaced (a deploy's image swap) and the child could not resume, so the pipeline stopped." : cause === "sandbox_fault" ? "\u26A0\uFE0F The sandbox running this pipeline's child failed and the child could not resume, so the pipeline stopped." : cause === "bot_restart" ? "\u26A0\uFE0F The bot restarted while this ship pipeline was running, so the pipeline stopped." : "\u26A0\uFE0F This ship pipeline's child was interrupted and could not resume, so the pipeline stopped.";
3717
- return `${opening} ${stands} ${continuation}`;
3740
+ const opening = cause === "container_replaced" ? "\u26A0\uFE0F Work stopped before the next review because the repository container was replaced and the run could not continue." : cause === "sandbox_fault" ? "\u26A0\uFE0F Work stopped before the next review because the sandbox failed and the run could not continue." : cause === "bot_restart" ? "\u26A0\uFE0F Work stopped before the next review because Switchboard restarted and the run could not continue." : "\u26A0\uFE0F Work stopped before the next review because the run could not continue.";
3741
+ const saved = prUrl3 ? `The existing pull request is ${prUrl3}; Switchboard did not verify the stopped run's latest commit there.` : "No open pull request was verified for the stopped run's latest commit.";
3742
+ const next = idle ? "The unit remains live; a reply in this thread is recorded as its continuation action." : "No review was started. Next action: start ship again after the remote branch and pull request state can be verified.";
3743
+ return `${opening} ${saved} ${next}`;
3718
3744
  }
3719
3745
  function planIdOf(planPath) {
3720
3746
  const base = planPath.split("/").pop() ?? "";
@@ -4078,6 +4104,7 @@ function isHeadMaterial(event) {
4078
4104
  case "context":
4079
4105
  case "reference":
4080
4106
  case "run_meta":
4107
+ case "run_state":
4081
4108
  case "route":
4082
4109
  return true;
4083
4110
  case "run_note":
@@ -4847,7 +4874,7 @@ function analyzeRunFriction(events, opts = {}) {
4847
4874
  return;
4848
4875
  }
4849
4876
  if (ev.type === "artifact" && ev.direction === "out") outboundArtifacts++;
4850
- if (ev.type === "skill_use" || ev.type === "artifact" || ev.type === "review_artifact" || ev.type === "pr_description" || ev.type === "pr_opened" || ev.type === "coordinator_tag" || ev.type === "ship_handoff" || ev.type === "child_interrupted" || ev.type === "child_resumed" || ev.type === "review_posted" || ev.type === "ship_round" || ev.type === "ship_unit" || ev.type === "route" || ev.type === "operator" || ev.type === "refusal" || ev.type === "reference" || ev.type === "lease" || ev.type === "pushed_head") {
4877
+ if (ev.type === "skill_use" || ev.type === "artifact" || ev.type === "review_artifact" || ev.type === "pr_description" || ev.type === "pr_opened" || ev.type === "coordinator_tag" || ev.type === "ship_handoff" || ev.type === "child_interrupted" || ev.type === "child_resumed" || ev.type === "review_posted" || ev.type === "ship_round" || ev.type === "ship_unit" || ev.type === "route" || ev.type === "operator" || ev.type === "refusal" || ev.type === "reference" || ev.type === "run_state" || ev.type === "lease" || ev.type === "pushed_head") {
4851
4878
  sideFactEvents++;
4852
4879
  return;
4853
4880
  }
@@ -4929,6 +4956,7 @@ function analyzeRunFriction(events, opts = {}) {
4929
4956
  }
4930
4957
  return;
4931
4958
  }
4959
+ if (ev.type !== "run_note") return;
4932
4960
  switch (ev.kind) {
4933
4961
  case "wrap_up":
4934
4962
  wrapUp = { index, at: ev.at };
@@ -5882,6 +5910,26 @@ var init_contract2 = __esm({
5882
5910
  });
5883
5911
 
5884
5912
  // ../../src/core/pipelineStanding.ts
5913
+ function hostedStageDetail(stage) {
5914
+ switch (stage) {
5915
+ case "coding":
5916
+ return "coding a unit";
5917
+ case "review":
5918
+ return "reviewing a unit";
5919
+ case "fix":
5920
+ return "fixing review findings";
5921
+ case "approved":
5922
+ return "waiting for checks and merge";
5923
+ case "merge-ready":
5924
+ return "ready for a person to merge";
5925
+ case "merged":
5926
+ return "a unit merged";
5927
+ case "idle":
5928
+ return "waiting for the next pipeline step";
5929
+ case "ended":
5930
+ return "finishing the pipeline";
5931
+ }
5932
+ }
5885
5933
  function endingWordOf(kind) {
5886
5934
  return ENDING_WORDS[kind] ?? kind;
5887
5935
  }
@@ -6008,10 +6056,11 @@ function isPipelineSummaryShape(v) {
6008
6056
  }
6009
6057
  return true;
6010
6058
  }
6011
- var STAGES, OPEN_STAGES, ROUND_STAGE, ENDING_STAGE, ENDING_WORDS, ROUND_OUTCOME_WORDS, isFinite_;
6059
+ var HOSTED_PIPELINE_STARTING_DETAIL, STAGES, OPEN_STAGES, ROUND_STAGE, ENDING_STAGE, ENDING_WORDS, ROUND_OUTCOME_WORDS, isFinite_;
6012
6060
  var init_pipelineStanding = __esm({
6013
6061
  "../../src/core/pipelineStanding.ts"() {
6014
6062
  "use strict";
6063
+ HOSTED_PIPELINE_STARTING_DETAIL = "pipeline starting";
6015
6064
  STAGES = [
6016
6065
  "coding",
6017
6066
  "review",
@@ -6530,15 +6579,15 @@ function capEvent(event, maxBytes) {
6530
6579
  }
6531
6580
  return { event: capped4, bytes: size };
6532
6581
  }
6533
- function fitRecordToBudget(record2, maxBytes = MAX_RECORD_BYTES) {
6534
- const cappedAll = record2.events.map((e) => capEvent(e, MAX_EVENT_BYTES));
6535
- const measure = (evs) => utf8ByteLength(JSON.stringify({ ...record2, events: evs, storedEventCount: evs.length, truncated: true }));
6536
- const whole = { ...record2, events: cappedAll.map((c) => c.event), storedEventCount: cappedAll.length };
6582
+ function fitRecordToBudget(record3, maxBytes = MAX_RECORD_BYTES) {
6583
+ const cappedAll = record3.events.map((e) => capEvent(e, MAX_EVENT_BYTES));
6584
+ const measure = (evs) => utf8ByteLength(JSON.stringify({ ...record3, events: evs, storedEventCount: evs.length, truncated: true }));
6585
+ const whole = { ...record3, events: cappedAll.map((c) => c.event), storedEventCount: cappedAll.length };
6537
6586
  if (utf8ByteLength(JSON.stringify(whole)) <= maxBytes) return whole;
6538
6587
  const baseline = measure([]);
6539
6588
  const capped4 = dropSpanPairsFromTheMiddle(cappedAll, maxBytes - baseline);
6540
6589
  const events = capped4.map((c) => c.event);
6541
- if (measure(events) <= maxBytes) return { ...record2, events, storedEventCount: events.length, truncated: true };
6590
+ if (measure(events) <= maxBytes) return { ...record3, events, storedEventCount: events.length, truncated: true };
6542
6591
  const sizes = capped4.map((c) => c.bytes + 1);
6543
6592
  let remaining = maxBytes - measure([]);
6544
6593
  let head = 0;
@@ -6555,7 +6604,7 @@ function fitRecordToBudget(record2, maxBytes = MAX_RECORD_BYTES) {
6555
6604
  while (kept.length > 0 && measure(kept) > maxBytes) {
6556
6605
  kept.splice(Math.floor(kept.length / 2), 1);
6557
6606
  }
6558
- return { ...record2, events: kept, storedEventCount: kept.length, truncated: true };
6607
+ return { ...record3, events: kept, storedEventCount: kept.length, truncated: true };
6559
6608
  }
6560
6609
  function headLength(events) {
6561
6610
  let n2 = 0;
@@ -6673,9 +6722,9 @@ function dominantFriction(byCategory) {
6673
6722
  }
6674
6723
  return best;
6675
6724
  }
6676
- function pointOf(record2, prices) {
6677
- if (record2.provisional === true) return void 0;
6678
- const usage = record2.usage ?? emptyUsage();
6725
+ function pointOf(record3, prices) {
6726
+ if (record3.provisional === true) return void 0;
6727
+ const usage = record3.usage ?? emptyUsage();
6679
6728
  const priced = llmUsdOfUsage(usage, prices);
6680
6729
  let inputTokens = 0;
6681
6730
  let outputTokens = 0;
@@ -6687,27 +6736,27 @@ function pointOf(record2, prices) {
6687
6736
  cacheReadTokens += m.cacheReadTokens;
6688
6737
  cacheWriteTokens += m.cacheWriteTokens;
6689
6738
  }
6690
- const shape = record2.diagnosis.shape;
6739
+ const shape = record3.diagnosis.shape;
6691
6740
  const blobs = {
6692
6741
  schema: POINT_SCHEMA,
6693
- agent: record2.agent ?? "",
6694
- preset: record2.profile?.preset ?? "",
6695
- model: record2.model ?? "",
6696
- status: record2.status,
6697
- "failure kind": record2.failure?.kind ?? "",
6698
- "dominant friction": dominantFriction(record2.diagnosis.byCategory),
6699
- channel: record2.channelId,
6700
- repository: record2.repo ?? "",
6701
- "machine class": record2.profile?.machine ?? "",
6702
- "route class": record2.route !== void 0 ? "routed" : "chosen",
6703
- reply: record2.replyOk === true ? "ok" : record2.replyOk === false ? "failed" : "none",
6704
- lineage: record2.parentRunId !== void 0 ? "child" : "root",
6705
- requester: record2.userId,
6706
- identity: record2.profile?.identity ?? "",
6707
- "run id": record2.id
6742
+ agent: record3.agent ?? "",
6743
+ preset: record3.profile?.preset ?? "",
6744
+ model: record3.model ?? "",
6745
+ status: record3.status,
6746
+ "failure kind": record3.failure?.kind ?? "",
6747
+ "dominant friction": dominantFriction(record3.diagnosis.byCategory),
6748
+ channel: record3.channelId,
6749
+ repository: record3.repo ?? "",
6750
+ "machine class": record3.profile?.machine ?? "",
6751
+ "route class": record3.route !== void 0 ? "routed" : "chosen",
6752
+ reply: record3.replyOk === true ? "ok" : record3.replyOk === false ? "failed" : "none",
6753
+ lineage: record3.parentRunId !== void 0 ? "child" : "root",
6754
+ requester: record3.userId,
6755
+ identity: record3.profile?.identity ?? "",
6756
+ "run id": record3.id
6708
6757
  };
6709
6758
  const doubles = {
6710
- wall: record2.finishedAt - (record2.receivedAt ?? record2.startedAt),
6759
+ wall: record3.finishedAt - (record3.receivedAt ?? record3.startedAt),
6711
6760
  "getting ready": shape?.gettingReadyMs ?? 0,
6712
6761
  thinking: shape?.thinkingMs ?? 0,
6713
6762
  tools: shape?.toolsMs ?? 0,
@@ -6721,15 +6770,15 @@ function pointOf(record2, prices) {
6721
6770
  "cache read tokens": cacheReadTokens,
6722
6771
  "cache write tokens": cacheWriteTokens,
6723
6772
  dollars: priced.usd,
6724
- steps: record2.stepCount ?? 0,
6725
- "tool calls": isFinite_2(record2.diagnosis.toolCalls) ? record2.diagnosis.toolCalls : 0,
6726
- events: record2.eventCount,
6773
+ steps: record3.stepCount ?? 0,
6774
+ "tool calls": isFinite_2(record3.diagnosis.toolCalls) ? record3.diagnosis.toolCalls : 0,
6775
+ events: record3.eventCount,
6727
6776
  "unpriced tokens": priced.unpricedTokens,
6728
- minutes: record2.profile?.minutes ?? 0,
6729
- "finished at": record2.finishedAt
6777
+ minutes: record3.profile?.minutes ?? 0,
6778
+ "finished at": record3.finishedAt
6730
6779
  };
6731
6780
  return {
6732
- indexes: [capBlob(record2.agent ?? UNKNOWN_AGENT)],
6781
+ indexes: [capBlob(record3.agent ?? UNKNOWN_AGENT)],
6733
6782
  blobs: POINT_COLUMNS.blobs.map((n2) => capBlob(blobs[n2])),
6734
6783
  doubles: POINT_COLUMNS.doubles.map((n2) => doubles[n2])
6735
6784
  };
@@ -10367,12 +10416,8 @@ function containerApplicationName(script, className) {
10367
10416
  return `${script}-${className.toLowerCase()}`;
10368
10417
  }
10369
10418
  function bindLiveGate(gate, script, healthUrl) {
10370
- return gate.kind === "health" ? { kind: "health", healthUrl } : {
10371
- kind: "sandbox",
10372
- healthUrl,
10373
- bearerEnv: gate.bearerEnv,
10374
- containerApp: containerApplicationName(script, gate.containerClass)
10375
- };
10419
+ const containerApp = containerApplicationName(script, gate.containerClass);
10420
+ return gate.kind === "bot" ? { kind: "bot", healthUrl, containerApp } : { kind: "sandbox", healthUrl, bearerEnv: gate.bearerEnv, containerApp };
10376
10421
  }
10377
10422
  function workersFor(profile) {
10378
10423
  return WORKER_SPECS.flatMap(({ preflight, liveGate, ...spec }) => {
@@ -10387,8 +10432,8 @@ function workersFor(profile) {
10387
10432
  script,
10388
10433
  baseUrl,
10389
10434
  healthUrl,
10390
- // A preflighted step with a health gate reads the same URL for its wait heartbeat.
10391
- ...preflight ? { preflight: liveGate?.kind === "health" ? { ...preflight, healthUrl } : preflight } : {},
10435
+ // The bot preflight reads the same URL as its application + health live gate.
10436
+ ...preflight ? { preflight: liveGate?.kind === "bot" ? { ...preflight, healthUrl } : preflight } : {},
10392
10437
  ...liveGate ? { liveGate: bindLiveGate(liveGate, script, healthUrl) } : {}
10393
10438
  }
10394
10439
  ];
@@ -10499,7 +10544,7 @@ function formatPlan(plan3) {
10499
10544
  plan3.steps.forEach((s, i) => {
10500
10545
  const pf = s.retryOnPreflightRefusal ? ` \u2014 preflight (retry every ${plan3.pollMs / 1e3}s up to ${(s.waitMaxMs ?? plan3.waitMaxMs) / 6e4} min)` : s.forcedBy ? ` \u2014 preflight FORCED (${s.forcedBy}=1)` : "";
10501
10546
  const env = s.requiredEnv.length > 0 ? ` \u2014 needs ${s.requiredEnv.map((r) => r.anyOf.length === 1 ? r.anyOf[0] : `one of ${r.anyOf.join(" / ")}`).join(", ")}` : "";
10502
- const live = s.liveGate ? s.liveGate.kind === "health" ? ` \u2014 then wait until live (${s.liveGate.healthUrl} not draining + build.commit == HEAD)` : ` \u2014 then wait until live (${s.liveGate.healthUrl} build.commit == HEAD + every running ${s.liveGate.containerApp} instance on the app version + an /exec probe answers ok from one)` : "";
10547
+ const live = s.liveGate ? s.liveGate.kind === "bot" ? ` \u2014 then wait until live (${s.liveGate.containerApp} advances to the deployed image + ${s.liveGate.healthUrl} build.commit exactly == HEAD)` : ` \u2014 then wait until live (${s.liveGate.healthUrl} build.commit == HEAD + every running ${s.liveGate.containerApp} instance on the app version + an /exec probe answers ok from one)` : "";
10503
10548
  const cap = s.capabilities.length > 0 ? ` \u2014 credential must pass ${s.capabilities.map((c) => `\`${c.command.join(" ")}\` (${c.needs})`).join(" and ")}` : "";
10504
10549
  lines.push(
10505
10550
  ` ${i + 1}. ${s.name} (${s.script}) \u2014 ${s.dir}: ${s.command.join(" ")}${pf}${env}${cap}${live}
@@ -10570,7 +10615,7 @@ function classifyDeployOutput(exitCode, output) {
10570
10615
  const firstError = lines.find((l) => isErrorish(l) && !/^npm ERR!/i.test(l)) ?? lines.find(isErrorish);
10571
10616
  return { kind: "failed", reason: firstError ?? `exit ${exitCode}` };
10572
10617
  }
10573
- var UNSET_ENV, DEPLOY_FORCE_ENV, SANDBOX_CONTAINER_CLASS, SANDBOX_BEARER_ENV, R2_CAPABILITY, RESIDENT_BEARER_ENVS, RESIDENT_WAIT_MAX_MS, CONTAINERS_CAPABILITY, WORKER_DIRS, WORKER_SPECS, DEPLOY_ORDER, CONFIG_DOCUMENT_KEY, hasImage, ANSI_SEQUENCE;
10618
+ var UNSET_ENV, DEPLOY_FORCE_ENV, BOT_CONTAINER_CLASS, SANDBOX_CONTAINER_CLASS, SANDBOX_BEARER_ENV, R2_CAPABILITY, RESIDENT_BEARER_ENVS, RESIDENT_WAIT_MAX_MS, CONTAINERS_CAPABILITY, WORKER_DIRS, WORKER_SPECS, DEPLOY_ORDER, CONFIG_DOCUMENT_KEY, hasImage, ANSI_SEQUENCE;
10574
10619
  var init_plan = __esm({
10575
10620
  "../../src/deploy/plan.ts"() {
10576
10621
  "use strict";
@@ -10582,6 +10627,7 @@ var init_plan = __esm({
10582
10627
  init_profile2();
10583
10628
  UNSET_ENV = ["CLOUDFLARE_ACCOUNT_ID"];
10584
10629
  DEPLOY_FORCE_ENV = "SWITCHBOARD_DEPLOY_FORCE";
10630
+ BOT_CONTAINER_CLASS = "SwitchboardServer";
10585
10631
  SANDBOX_CONTAINER_CLASS = "SwitchboardSandbox";
10586
10632
  SANDBOX_BEARER_ENV = "SANDBOX_TOKEN";
10587
10633
  R2_CAPABILITY = {
@@ -10642,10 +10688,10 @@ var init_plan = __esm({
10642
10688
  ]
10643
10689
  },
10644
10690
  preflight: { forceEnv: DEPLOY_FORCE_ENV, baseUrlEnv: "SWITCHBOARD_BASE_URL" },
10645
- liveGate: { kind: "health" },
10691
+ liveGate: { kind: "bot", containerClass: BOT_CONTAINER_CLASS },
10646
10692
  // The preflight reads the container application and the deploy pushes the image: both need Containers.
10647
10693
  capabilities: [CONTAINERS_CAPABILITY],
10648
- why: "container shim \u2014 preflight refuses only over a rollout in progress (runs in flight hand off); done only when the new container is live. A rotated bot secret needs no build: `wrangler secret put` alone leaves the running container on its old env \u2014 `deploy restart` restarts it on the current env"
10694
+ why: "container shim \u2014 preflight refuses only over a rollout in progress (runs in flight hand off); done only when the application advances to the deployed image and /healthz serves the exact commit. A rotated bot secret needs no build: `wrangler secret put` alone leaves the running container on its old env \u2014 `deploy restart` restarts it on the current application target"
10649
10695
  },
10650
10696
  {
10651
10697
  name: "resident",
@@ -11765,11 +11811,11 @@ var init_runStoreWorker = __esm({
11765
11811
  fetchImpl;
11766
11812
  abandoned() {
11767
11813
  }
11768
- async put(record2, trace) {
11769
- if (!RUN_ID_PATTERN.test(record2.id))
11770
- throw new PermanentStoreError(`run store: refusing to put malformed id ${JSON.stringify(record2.id)}`);
11771
- const body = { storeKey: this.opts.storeKey, record: record2 };
11772
- const point = pointOf(record2, this.opts.prices);
11814
+ async put(record3, trace) {
11815
+ if (!RUN_ID_PATTERN.test(record3.id))
11816
+ throw new PermanentStoreError(`run store: refusing to put malformed id ${JSON.stringify(record3.id)}`);
11817
+ const body = { storeKey: this.opts.storeKey, record: record3 };
11818
+ const point = pointOf(record3, this.opts.prices);
11773
11819
  if (point !== void 0) body.point = point;
11774
11820
  if (this.opts.policy) {
11775
11821
  body.policy = this.opts.policy;
@@ -11939,6 +11985,66 @@ var init_supersede = __esm({
11939
11985
  }
11940
11986
  });
11941
11987
 
11988
+ // ../../src/deploy/botLiveGate.ts
11989
+ function judge(input) {
11990
+ if ("error" in input.app)
11991
+ return waiting(`container application ${input.containerApp} unreadable: ${input.app.error}`);
11992
+ const app = input.app.value;
11993
+ let rollout;
11994
+ if (input.target) {
11995
+ if (input.target.image !== null && app.image !== input.target.image)
11996
+ return waiting(
11997
+ `container application ${input.containerApp} still targets ${app.image ?? "an unknown image"}; expected ${input.target.image}`
11998
+ );
11999
+ if ("error" in input.before)
12000
+ return waiting(
12001
+ `container application ${input.containerApp} pre-deploy version unreadable (${input.before.error}); cannot prove the application version advanced`
12002
+ );
12003
+ if (app.version <= input.before.value.version)
12004
+ return waiting(
12005
+ `container application ${input.containerApp} is still at pre-deploy version ${input.before.value.version}; expected a newer application version`
12006
+ );
12007
+ rollout = ` at version ${app.version} (up from ${input.before.value.version})`;
12008
+ } else {
12009
+ rollout = ` at version ${app.version} (Worker-only deploy; no application change printed)`;
12010
+ }
12011
+ if ("error" in input.health) return waiting(`health: GET /healthz failed: ${input.health.error}`);
12012
+ if (input.health.status !== 200)
12013
+ return waiting(
12014
+ `health: GET /healthz \u2192 HTTP ${input.health.status}; expected the deployed container's exact build identity`
12015
+ );
12016
+ if (!input.health.body || input.health.body.ok !== true)
12017
+ return waiting("health: /healthz is not ready or did not answer with JSON");
12018
+ const commit = servedCommit(input.health.body);
12019
+ if (!commit) return waiting("health: /healthz carries no build identity");
12020
+ if (commit !== input.expectedCommit)
12021
+ return waiting(`health: serving commit ${commit}, expected exact ${input.expectedCommit}`);
12022
+ if (input.health.body.draining === true)
12023
+ return waiting("health: the exact deployed commit answers but its container is draining");
12024
+ const target = input.target?.image ?? app.image ?? "an unchanged image";
12025
+ return {
12026
+ kind: "live",
12027
+ summary: `container application ${input.containerApp} targets ${target}${rollout}; /healthz serves exact ${commit}`
12028
+ };
12029
+ }
12030
+ function decideBotLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
12031
+ const decision = judge(input);
12032
+ if (decision.kind !== "waiting" || input.elapsedMs < deadlineMs) return decision;
12033
+ return {
12034
+ kind: "failed",
12035
+ reason: `${decision.reason} \u2014 still not live after ${Math.round(input.elapsedMs / MINUTE_MS)} min (deadline ${deadlineMs / MINUTE_MS} min)`
12036
+ };
12037
+ }
12038
+ var waiting;
12039
+ var init_botLiveGate = __esm({
12040
+ "../../src/deploy/botLiveGate.ts"() {
12041
+ "use strict";
12042
+ init_budgets();
12043
+ init_liveGate();
12044
+ waiting = (reason2) => ({ kind: "waiting", reason: reason2 });
12045
+ }
12046
+ });
12047
+
11942
12048
  // ../../src/execution/sandboxErrors.ts
11943
12049
  function fleetBusyRunEndedLine(run2, thread, err2) {
11944
12050
  if (typeof err2 !== "object" || err2 === null) return null;
@@ -12025,51 +12131,51 @@ function rolloutAdvanced(app, before, target) {
12025
12131
  reason: `rollout: pre-deploy version unreadable (${before.error}); application image ${app.image === null ? "?" : shortImage(app.image)} is not the deploy's ${shortImage(target.image)}`
12026
12132
  };
12027
12133
  }
12028
- function judge(input) {
12134
+ function judge2(input) {
12029
12135
  const worker = decideWorker(input.health, input.deployedCommit);
12030
- if (!worker.ok) return worker.fatal ? { kind: "failed", reason: worker.reason } : waiting(worker.reason);
12031
- if (input.app === null) return waiting("rollout: not read yet");
12032
- if ("error" in input.app) return waiting(`rollout: ${input.app.error}`);
12136
+ if (!worker.ok) return worker.fatal ? { kind: "failed", reason: worker.reason } : waiting2(worker.reason);
12137
+ if (input.app === null) return waiting2("rollout: not read yet");
12138
+ if ("error" in input.app) return waiting2(`rollout: ${input.app.error}`);
12033
12139
  const app = input.app.value;
12034
12140
  const appVersion = app.version;
12035
12141
  let advancedFrom = null;
12036
12142
  if (input.target) {
12037
12143
  const advanced = rolloutAdvanced(app, input.before, input.target);
12038
- if (!advanced.ok) return waiting(advanced.reason);
12144
+ if (!advanced.ok) return waiting2(advanced.reason);
12039
12145
  advancedFrom = advanced.from;
12040
12146
  }
12041
- if (input.instances === null) return waiting("rollout: instances not read yet");
12042
- if ("error" in input.instances) return waiting(`rollout: ${input.instances.error}`);
12147
+ if (input.instances === null) return waiting2("rollout: instances not read yet");
12148
+ if ("error" in input.instances) return waiting2(`rollout: ${input.instances.error}`);
12043
12149
  const running = input.instances.value.filter((i) => i.state === "running");
12044
12150
  const rolloutSummary = advancedFrom === null ? `Worker-only deploy \u2014 no container change (${running.length} running instance(s) on version ${appVersion})` : `rollout complete (${running.length} running instance(s) on version ${appVersion}, ${advancedFrom})`;
12045
12151
  const stale = running.filter((i) => i.version !== appVersion);
12046
12152
  if (stale.length > 0) {
12047
12153
  const versions = [...new Set(stale.map((i) => i.version === null ? "?" : String(i.version)))].join("/");
12048
- return waiting(
12154
+ return waiting2(
12049
12155
  `rollout in progress \u2014 ${stale.length} of ${running.length} running instance(s) still on version ${versions}, app version ${appVersion}`
12050
12156
  );
12051
12157
  }
12052
- if (input.probe === null) return waiting("probe: not sent yet");
12053
- if ("error" in input.probe) return waiting(`probe: ${input.probe.error}`);
12158
+ if (input.probe === null) return waiting2("probe: not sent yet");
12159
+ if ("error" in input.probe) return waiting2(`probe: ${input.probe.error}`);
12054
12160
  const body = input.probe.body;
12055
12161
  if (body.reason === FLEET_BUSY_REASON)
12056
- return waiting("probe: fleet busy \u2014 no free instance for the probe thread (max_instances reached)");
12162
+ return waiting2("probe: fleet busy \u2014 no free instance for the probe thread (max_instances reached)");
12057
12163
  if (typeof body.error === "string" && CONTAINER_STARTING.test(body.error))
12058
- return waiting("probe: container starting");
12164
+ return waiting2("probe: container starting");
12059
12165
  if ("error" in body) {
12060
12166
  const failed = body.error === "" ? "/exec failed with an EMPTY error" : `/exec failed \u2014 ${typeof body.error === "string" ? body.error : JSON.stringify(body.error)}`;
12061
- return waiting(`probe: ${failed} \u2014 the probe's container may still run the previous image`);
12167
+ return waiting2(`probe: ${failed} \u2014 the probe's container may still run the previous image`);
12062
12168
  }
12063
12169
  if (body.exitCode !== 0) {
12064
12170
  const stderr = typeof body.stderr === "string" ? body.stderr.trim() : "";
12065
- return waiting(`probe: \`${PROBE_COMMAND}\` exited ${String(body.exitCode)}${stderr ? `: ${stderr}` : ""}`);
12171
+ return waiting2(`probe: \`${PROBE_COMMAND}\` exited ${String(body.exitCode)}${stderr ? `: ${stderr}` : ""}`);
12066
12172
  }
12067
12173
  const stdout = typeof body.stdout === "string" ? body.stdout.trim() : "";
12068
- if (stdout !== "ok") return waiting(`probe: \`${PROBE_COMMAND}\` printed ${JSON.stringify(stdout)}`);
12174
+ if (stdout !== "ok") return waiting2(`probe: \`${PROBE_COMMAND}\` printed ${JSON.stringify(stdout)}`);
12069
12175
  const mine = input.instances.value.find((i) => i.name === input.probeThreadKey);
12070
- if (!mine) return waiting(`probe instance ${input.probeThreadKey} not listed yet`);
12176
+ if (!mine) return waiting2(`probe instance ${input.probeThreadKey} not listed yet`);
12071
12177
  if (mine.version !== appVersion)
12072
- return waiting(
12178
+ return waiting2(
12073
12179
  `probe instance ${input.probeThreadKey} is on version ${mine.version === null ? "?" : mine.version}, app version ${appVersion} \u2014 the probe landed on a previous image`
12074
12180
  );
12075
12181
  return {
@@ -12078,7 +12184,7 @@ function judge(input) {
12078
12184
  };
12079
12185
  }
12080
12186
  function decideSandboxLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
12081
- const d = judge(input);
12187
+ const d = judge2(input);
12082
12188
  if (d.kind !== "waiting" || input.elapsedMs < deadlineMs) return d;
12083
12189
  return {
12084
12190
  kind: "failed",
@@ -12152,7 +12258,7 @@ function parseExecStream(text) {
12152
12258
  return { error: `/exec answered no JSON document: ${JSON.stringify(text.trim().slice(0, 120))}` };
12153
12259
  }
12154
12260
  }
12155
- var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING, waiting, isRecord2, ANSI_SEQUENCE2, SECTION_GUTTER;
12261
+ var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING, waiting2, isRecord2, ANSI_SEQUENCE2, SECTION_GUTTER;
12156
12262
  var init_sandboxLiveGate = __esm({
12157
12263
  "../../src/deploy/sandboxLiveGate.ts"() {
12158
12264
  "use strict";
@@ -12161,7 +12267,7 @@ var init_sandboxLiveGate = __esm({
12161
12267
  PROBE_COMMAND = "echo ok";
12162
12268
  PROBE_TIMEOUT_MS = 6e4;
12163
12269
  CONTAINER_STARTING = /Container is starting/i;
12164
- waiting = (reason2) => ({ kind: "waiting", reason: reason2 });
12270
+ waiting2 = (reason2) => ({ kind: "waiting", reason: reason2 });
12165
12271
  isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12166
12272
  ANSI_SEQUENCE2 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
12167
12273
  SECTION_GUTTER = /^[\s│├╭╰|]+/;
@@ -12487,26 +12593,6 @@ async function fetchHealthz(url) {
12487
12593
  const r = await readHealthz(url);
12488
12594
  return "body" in r ? r.body : void 0;
12489
12595
  }
12490
- async function waitUntilLive(step, healthUrl, expectedCommit, io, clock4, previousStartedAt) {
12491
- const started = clock4();
12492
- for (; ; ) {
12493
- const elapsed2 = clock4() - started;
12494
- const body = await fetchHealthz(healthUrl);
12495
- const d = decideLive(body, expectedCommit, elapsed2, LIVE_GATE_DEADLINE_MS, {
12496
- ...previousStartedAt !== void 0 ? { previousStartedAt } : {}
12497
- });
12498
- if (d.kind === "live") return { live: true, detail: `commit ${d.commit.slice(0, 7)}`, waitedMs: elapsed2 };
12499
- if (d.kind === "timeout")
12500
- return {
12501
- live: false,
12502
- reason: `${d.reason} \u2014 gave up after ${Math.round(elapsed2 / 6e4)} min (drain deadline ${LIVE_GATE_DEADLINE_MS / 6e4} min)`
12503
- };
12504
- io.log(
12505
- `[deploy:all] ${step.name}: deployed, not live yet \u2014 ${d.reason} (${Math.floor(elapsed2 / 6e4)}m ${Math.floor(elapsed2 % 6e4 / 1e3)}s)`
12506
- );
12507
- await sleep(LIVE_GATE_POLL_MS);
12508
- }
12509
- }
12510
12596
  async function wranglerJson(dir, args) {
12511
12597
  const r = await run("npx", ["wrangler", ...args], { cwd: workerDir(dir), unset: UNSET_ENV });
12512
12598
  if (r.code !== 0)
@@ -12527,6 +12613,29 @@ async function resolveContainerAppId(dir, containerApp) {
12527
12613
  containerAppIds.set(containerApp, id);
12528
12614
  return { value: id };
12529
12615
  }
12616
+ async function waitUntilBotLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
12617
+ const started = deps.now();
12618
+ for (; ; ) {
12619
+ const elapsed2 = deps.now() - started;
12620
+ const app = await deps.readAppState(step.dir, gate.containerApp);
12621
+ const health = await deps.readHealth(gate.healthUrl);
12622
+ const decision = decideBotLive({
12623
+ containerApp: gate.containerApp,
12624
+ health,
12625
+ app,
12626
+ before: rollout.before,
12627
+ target: rollout.target,
12628
+ expectedCommit,
12629
+ elapsedMs: elapsed2
12630
+ });
12631
+ if (decision.kind === "live") return { live: true, detail: decision.summary, waitedMs: deps.now() - started };
12632
+ if (decision.kind === "failed") return { live: false, reason: decision.reason };
12633
+ io.log(
12634
+ `[deploy:all] ${step.name}: deployed, not live yet \u2014 ${decision.reason} (${Math.floor(elapsed2 / 6e4)}m ${Math.floor(elapsed2 % 6e4 / 1e3)}s)`
12635
+ );
12636
+ await deps.sleep(LIVE_GATE_POLL_MS);
12637
+ }
12638
+ }
12530
12639
  async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
12531
12640
  const bearer = deps.env[gate.bearerEnv];
12532
12641
  if (!bearer)
@@ -12567,7 +12676,7 @@ async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, dep
12567
12676
  async function readAppBeforeUpload(step, gate, io, deps) {
12568
12677
  const before = await deps.readAppState(step.dir, gate.containerApp);
12569
12678
  io.log(
12570
- "value" in before ? `[deploy:all] ${step.name}: container application at version ${before.value.version}${before.value.image ? ` (image ${shortImage(before.value.image)})` : ""} before the upload` : `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate will need the deploy's image to show`
12679
+ "value" in before ? `[deploy:all] ${step.name}: container application at version ${before.value.version}${before.value.image ? ` (image ${shortImage(before.value.image)})` : ""} before the upload` : gate.kind === "sandbox" ? `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate will need the deploy's image to show` : `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate requires a readable pre-deploy version to prove advancement`
12571
12680
  );
12572
12681
  return before;
12573
12682
  }
@@ -12643,8 +12752,7 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
12643
12752
  for (; ; ) {
12644
12753
  io.log(`
12645
12754
  [deploy:all] \u25B6 ${step.name} (${step.script}) \u2014 ${step.dir}: ${step.command.join(" ")}`);
12646
- const sandbox = step.liveGate?.kind === "sandbox" ? { gate: step.liveGate, before: await readAppBeforeUpload(step, step.liveGate, io, deps) } : void 0;
12647
- const previousStartedAt = step.liveGate && step.liveGate.kind !== "sandbox" ? await fetchHealthz(step.liveGate.healthUrl).then((b) => b ? servedStartedAt(b) : void 0) : void 0;
12755
+ const application = step.liveGate ? { before: await readAppBeforeUpload(step, step.liveGate, io, deps) } : void 0;
12648
12756
  const r = await exec(step, io);
12649
12757
  const outcome = classifyDeployOutput(r.code, r.output);
12650
12758
  if (outcome.kind === "deployed") {
@@ -12657,15 +12765,20 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
12657
12765
  );
12658
12766
  const liveGate = step.liveGate;
12659
12767
  const gate = await root.span("deploy.wait_live", async (wait2) => {
12660
- let g;
12661
- if (sandbox) {
12662
- const target = rolloutTargetFromDeployOutput(r.output);
12663
- const from = "value" in sandbox.before ? `version ${sandbox.before.value.version}` : "its pre-deploy version";
12664
- io.log(
12665
- target ? `[deploy:all] ${step.name}: wrangler printed a container change \u2014 ${target.image ? `image ${shortImage(target.image)}` : "configuration only, image unchanged"}; the application must leave ${from}` : `[deploy:all] ${step.name}: wrangler printed no container change \u2014 Worker-only deploy, no rollout expected`
12666
- );
12667
- g = await waitUntilSandboxLive(step, sandbox.gate, expectedCommit, { ...sandbox, target }, io, deps);
12668
- } else g = await waitUntilLive(step, liveGate.healthUrl, expectedCommit, io, deps.now, previousStartedAt);
12768
+ if (!application) throw new Error(`${step.name}: live gate has no application state`);
12769
+ const target = rolloutTargetFromDeployOutput(r.output);
12770
+ const from = "value" in application.before ? `version ${application.before.value.version}` : "its pre-deploy version";
12771
+ io.log(
12772
+ target ? `[deploy:all] ${step.name}: wrangler printed a container change \u2014 ${target.image ? `image ${shortImage(target.image)}` : "configuration only, image unchanged"}; the application must leave ${from}` : `[deploy:all] ${step.name}: wrangler printed no container change \u2014 Worker-only deploy, no rollout expected`
12773
+ );
12774
+ const g = liveGate.kind === "sandbox" ? await waitUntilSandboxLive(
12775
+ step,
12776
+ liveGate,
12777
+ expectedCommit,
12778
+ { before: application.before, target },
12779
+ io,
12780
+ deps
12781
+ ) : await waitUntilBotLive(step, liveGate, expectedCommit, { before: application.before, target }, io, deps);
12669
12782
  wait2.setAttrs(g.live ? { outcome: "live", waitedMs: g.waitedMs } : { outcome: "not_live" });
12670
12783
  return g;
12671
12784
  });
@@ -13082,6 +13195,7 @@ var init_run = __esm({
13082
13195
  init_planeDeploy();
13083
13196
  init_wranglerTemplate();
13084
13197
  init_supersede();
13198
+ init_botLiveGate();
13085
13199
  init_sandboxLiveGate();
13086
13200
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
13087
13201
  workerDir = (dir) => workPath(OPERATOR_ROOT, dir);
@@ -16134,6 +16248,7 @@ var init_resident = __esm({
16134
16248
  * here: its `runtime-replaced` is the typed restart (`opWithReattach`). */
16135
16249
  runtimeReplacedStreak = 0;
16136
16250
  lastBinding;
16251
+ attachAttempt = 0;
16137
16252
  /** The thread's binding as the resident answered it on the most recent
16138
16253
  * successful attach — including a mid-run re-attach after an eviction, which
16139
16254
  * may land on a newer sha than the one the run started on. Undefined until
@@ -16300,6 +16415,7 @@ var init_resident = __esm({
16300
16415
  * an operation's wall clock is the command's, and a recovery attach is not
16301
16416
  * the command. */
16302
16417
  async attach(span, opts = {}) {
16418
+ this.attachAttempt++;
16303
16419
  const answer = await this.attachOnce(span, this.attachBoundMs("/attach"), opts.signal);
16304
16420
  if (answer.ok) return answer.binding;
16305
16421
  if (isDrainingRefusal(answer)) {
@@ -16348,14 +16464,16 @@ var init_resident = __esm({
16348
16464
  t0 + (left === void 0 ? DRAIN_WAIT_MAX_MS : Math.max(0, left - DRAIN_LEASE_RESERVE_MS))
16349
16465
  );
16350
16466
  let answer = first;
16351
- const note = (now) => this.opts.onSetupNote?.(
16352
- `waiting for the deploy to finish \xB7 ${Math.max(1, Math.round((now - t0) / MINUTE_MS))} min`
16353
- );
16467
+ await this.opts.onLiveStateObservation?.({
16468
+ state: "waiting_deploy",
16469
+ bound: deadline,
16470
+ reason: "deploy",
16471
+ attempt: this.attachAttempt
16472
+ });
16354
16473
  const waitSpan = span?.start("dispatch.workspace.attach.drain-wait");
16355
16474
  try {
16356
16475
  for (; ; ) {
16357
16476
  const now = systemClock();
16358
- note(now);
16359
16477
  if (now >= deadline)
16360
16478
  throw new ResidentDrainingError(this.opts.resource, now - t0, drainingUntil(answer), refusalWords(answer));
16361
16479
  await wakePause(Math.min(DRAIN_POLL_MS, deadline - now), opts.signal, "/attach");
@@ -16393,7 +16511,6 @@ var init_resident = __esm({
16393
16511
  }
16394
16512
  throw err2;
16395
16513
  } finally {
16396
- this.opts.onSetupNote?.(void 0);
16397
16514
  }
16398
16515
  }
16399
16516
  /** One `/attach`, answered rather than thrown: the binding on a 200, else
@@ -16700,6 +16817,13 @@ var init_resident = __esm({
16700
16817
  }
16701
16818
  };
16702
16819
  const t0 = systemClock();
16820
+ const wakeBound = t0 + wakeWaitBudget(opts.budgetMs);
16821
+ await this.opts.onLiveStateObservation?.({
16822
+ state: "waiting_repository",
16823
+ bound: wakeBound,
16824
+ reason: "repository_container",
16825
+ attempt: this.attachAttempt
16826
+ });
16703
16827
  let transient = opts.origin === "transient-refusal";
16704
16828
  const definite = (why) => residentWakeStrike(route2, refusal, why, "refused", opts.origin);
16705
16829
  const decide = (view) => transient && isUnansweredProbe(view) ? {
@@ -17331,7 +17455,7 @@ async function branchHeadSubject(repo, branch2) {
17331
17455
  if (typeof message4 !== "string" || message4.trim() === "") return void 0;
17332
17456
  return message4.split("\n", 1)[0].trim();
17333
17457
  }
17334
- async function headRefTipSha(repo, branch2, headers) {
17458
+ async function readHeadRef(repo, branch2, headers) {
17335
17459
  let res;
17336
17460
  try {
17337
17461
  res = await fetch(`https://api.github.com/repos/${repo}/git/ref/heads/${encodeGithubRef(branch2)}`, {
@@ -17339,13 +17463,17 @@ async function headRefTipSha(repo, branch2, headers) {
17339
17463
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
17340
17464
  });
17341
17465
  } catch {
17342
- return void 0;
17466
+ return { kind: "unverified" };
17343
17467
  }
17344
- if (!res.ok) return void 0;
17468
+ if (res.status === 404) return { kind: "missing" };
17469
+ if (!res.ok) return { kind: "unverified" };
17345
17470
  const data = await res.json().catch(() => null);
17346
- if (data?.object?.type !== "commit") return void 0;
17347
- const sha = data.object.sha;
17348
- return typeof sha === "string" && /^[0-9a-f]{40}$/.test(sha) ? sha : void 0;
17471
+ const sha = data?.object?.sha;
17472
+ return data?.object?.type === "commit" && typeof sha === "string" && /^[0-9a-f]{40}$/.test(sha) ? { kind: "verified", sha } : { kind: "unverified" };
17473
+ }
17474
+ async function headRefTipSha(repo, branch2, headers) {
17475
+ const read = await readHeadRef(repo, branch2, headers);
17476
+ return read.kind === "verified" ? read.sha : void 0;
17349
17477
  }
17350
17478
  function preferRefTip(where, prSha, refTip, branch2) {
17351
17479
  if (refTip === void 0) return prSha;
@@ -17395,8 +17523,11 @@ async function fetchPullRequestFacts(pr) {
17395
17523
  const prSha = typeof data.head?.sha === "string" && /^[0-9a-f]{40}$/.test(data.head.sha) ? data.head.sha : void 0;
17396
17524
  const sameRepoHead = headRepo === pr.repo.toLowerCase();
17397
17525
  const headRef = typeof data.head?.ref === "string" && data.head.ref ? data.head.ref : void 0;
17398
- const headBranchExists = sameRepoHead && headRef !== void 0 ? await fetchRefExists(pr.repo, headRef) : void 0;
17399
- const sha = sameRepoHead && headRef !== void 0 && headBranchExists !== false ? preferRefTip(`${pr.repo}#${pr.number}`, prSha, await headRefTipSha(pr.repo, headRef, headers), headRef) : prSha;
17526
+ const headRead = sameRepoHead && headRef !== void 0 ? await readHeadRef(pr.repo, headRef, headers) : void 0;
17527
+ const headBranchExists = headRead?.kind === "verified" ? true : headRead?.kind === "missing" ? false : void 0;
17528
+ const refTip = headRead?.kind === "verified" ? headRead.sha : void 0;
17529
+ const sha = sameRepoHead && headRef !== void 0 ? preferRefTip(`${pr.repo}#${pr.number}`, prSha, refTip, headRef) : prSha;
17530
+ const verifiedHead = sameRepoHead && headRef !== void 0 && prSha !== void 0 && refTip === prSha ? { repo: pr.repo, ref: headRef, sha: refTip } : void 0;
17400
17531
  let closedBy = typeof data.closed_by?.login === "string" ? data.closed_by.login : void 0;
17401
17532
  if (data.state === "closed" && !(typeof data.merged_at === "string" && data.merged_at) && closedBy === void 0) {
17402
17533
  try {
@@ -17421,6 +17552,7 @@ async function fetchPullRequestFacts(pr) {
17421
17552
  } : {},
17422
17553
  ...headRef !== void 0 ? { headRef } : {},
17423
17554
  ...sha ? { headSha: sha } : {},
17555
+ ...verifiedHead !== void 0 ? { verifiedHead } : {},
17424
17556
  ...headBranchExists !== void 0 ? { headBranchExists } : {},
17425
17557
  sameRepoHead,
17426
17558
  ...typeof data.base?.ref === "string" && data.base.ref ? { baseRef: data.base.ref } : {},
@@ -18413,7 +18545,7 @@ async function makeExecutor(opts, ctx, span) {
18413
18545
  resolveEnvs: () => gitIdentityEnvs(ctx.profile.identity, authorSourceOf(opts, ctx)),
18414
18546
  ...ctx.ownPr !== void 0 ? { ownPr: ctx.ownPr } : {},
18415
18547
  ...ctx.remainingMs !== void 0 ? { remainingMs: ctx.remainingMs } : {},
18416
- ...ctx.onSetupNote !== void 0 ? { onSetupNote: ctx.onSetupNote } : {}
18548
+ ...ctx.onLiveStateObservation !== void 0 ? { onLiveStateObservation: ctx.onLiveStateObservation } : {}
18417
18549
  },
18418
18550
  nonWarm,
18419
18551
  span,
@@ -18595,7 +18727,7 @@ async function reattachWorkspace(opts, ctx, recorded, span) {
18595
18727
  // resident holds its own credential, so the pairs alone ride.
18596
18728
  resolveEnvs: () => gitIdentityEnvs(ctx.profile.identity, authorSourceOf(opts, ctx)),
18597
18729
  ...ctx.remainingMs !== void 0 ? { remainingMs: ctx.remainingMs } : {},
18598
- ...ctx.onSetupNote !== void 0 ? { onSetupNote: ctx.onSetupNote } : {}
18730
+ ...ctx.onLiveStateObservation !== void 0 ? { onLiveStateObservation: ctx.onLiveStateObservation } : {}
18599
18731
  });
18600
18732
  let binding;
18601
18733
  try {
@@ -21048,8 +21180,8 @@ function pageEvents(events, opts) {
21048
21180
  if (page2.length < rest.length && page2.length > 0) out.nextAfterSeq = page2[page2.length - 1].seq;
21049
21181
  return out;
21050
21182
  }
21051
- function toListItem(record2, bytes) {
21052
- const { events: _events, ...rest } = record2;
21183
+ function toListItem(record3, bytes) {
21184
+ const { events: _events, ...rest } = record3;
21053
21185
  return { ...rest, bytes };
21054
21186
  }
21055
21187
  function retentionPolicyOf(cfg) {
@@ -21200,19 +21332,19 @@ var init_runStore = __esm({
21200
21332
  }
21201
21333
  abandoned() {
21202
21334
  }
21203
- async put(record2) {
21204
- if (!isValidRunId(record2.id))
21335
+ async put(record3) {
21336
+ if (!isValidRunId(record3.id))
21205
21337
  return { ok: true, retained: this.readIndex().length, stored: false, rewritten: false };
21206
21338
  this.ensureDir();
21207
- const content = JSON.stringify(record2);
21339
+ const content = JSON.stringify(record3);
21208
21340
  const bytes = utf8ByteLength(content);
21209
21341
  const index = this.readIndex();
21210
- const prev = index.find((r) => r.id === record2.id);
21211
- const rewritten = prev !== void 0 && !sameStoredVersion(prev, { ...record2, bytes });
21212
- this.writeAtomic(this.recordPath(record2.id), content);
21213
- const item = toListItem(record2, bytes);
21214
- const kept = this.compact([...index.filter((r) => r.id !== record2.id), item]);
21215
- return { ok: true, retained: kept.length, stored: kept.some((r) => r.id === record2.id), rewritten };
21342
+ const prev = index.find((r) => r.id === record3.id);
21343
+ const rewritten = prev !== void 0 && !sameStoredVersion(prev, { ...record3, bytes });
21344
+ this.writeAtomic(this.recordPath(record3.id), content);
21345
+ const item = toListItem(record3, bytes);
21346
+ const kept = this.compact([...index.filter((r) => r.id !== record3.id), item]);
21347
+ return { ok: true, retained: kept.length, stored: kept.some((r) => r.id === record3.id), rewritten };
21216
21348
  }
21217
21349
  /** Retention is decided from the index alone (no per-file stat); the one
21218
21350
  * file that matters is then read directly — a torn or missing file lands
@@ -21239,16 +21371,16 @@ var init_runStore = __esm({
21239
21371
  return selectListItems(this.retainedIntact(), opts);
21240
21372
  }
21241
21373
  async events(id, opts) {
21242
- const record2 = await this.get(id);
21243
- return record2 ? pageEvents(record2.events, opts) : null;
21374
+ const record3 = await this.get(id);
21375
+ return record3 ? pageEvents(record3.events, opts) : null;
21244
21376
  }
21245
21377
  async usage(query) {
21246
21378
  const items = this.retainedIntact();
21247
21379
  const records = [];
21248
21380
  for (const item of items) {
21249
21381
  if (item.finishedAt < query.sinceMs || item.finishedAt >= query.untilMs) continue;
21250
- const record2 = await this.get(item.id);
21251
- if (record2) records.push(record2);
21382
+ const record3 = await this.get(item.id);
21383
+ if (record3) records.push(record3);
21252
21384
  }
21253
21385
  const parent = (id) => {
21254
21386
  const p = items.find((i) => i.id === id);
@@ -21966,24 +22098,24 @@ function predicateWith(rules, actor, action, resourceType, kind) {
21966
22098
  function predicateFor(actor, action, resourceType, kind) {
21967
22099
  return predicateWith(POLICY, actor, action, resourceType, kind);
21968
22100
  }
21969
- function matchesPredicate(predicate, record2) {
22101
+ function matchesPredicate(predicate, record3) {
21970
22102
  switch (predicate.kind) {
21971
22103
  case "none":
21972
22104
  return false;
21973
22105
  case "all":
21974
22106
  return true;
21975
22107
  case "channels-in":
21976
- return record2.channelId !== void 0 && holds(predicate.channelIds, record2.channelId);
22108
+ return record3.channelId !== void 0 && holds(predicate.channelIds, record3.channelId);
21977
22109
  case "user-is":
21978
- return record2.userId !== void 0 && record2.userId === predicate.userId;
22110
+ return record3.userId !== void 0 && record3.userId === predicate.userId;
21979
22111
  case "repos-in":
21980
- return record2.repo !== void 0 && holds(predicate.repos, record2.repo);
22112
+ return record3.repo !== void 0 && holds(predicate.repos, record3.repo);
21981
22113
  case "visibility-in":
21982
- return predicate.visibilities.has(record2.channelVisibility ?? "unknown");
22114
+ return predicate.visibilities.has(record3.channelVisibility ?? "unknown");
21983
22115
  case "or":
21984
- return predicate.of.some((p) => matchesPredicate(p, record2));
22116
+ return predicate.of.some((p) => matchesPredicate(p, record3));
21985
22117
  case "and":
21986
- return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p, record2));
22118
+ return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p, record3));
21987
22119
  }
21988
22120
  return false;
21989
22121
  }
@@ -25400,6 +25532,199 @@ var init_frictionProposals = __esm({
25400
25532
  }
25401
25533
  });
25402
25534
 
25535
+ // ../../src/core/runLiveState.ts
25536
+ function safeLiveStateDetail(detail) {
25537
+ if (detail === void 0) return void 0;
25538
+ const line4 = oneLine(detail);
25539
+ if (line4 === "" || UNSAFE_DETAIL.test(line4)) return void 0;
25540
+ return redactAndCap(line4, LIVE_STATE_DETAIL_MAX - 1);
25541
+ }
25542
+ function assignRunLiveState(current, currentSeq, input) {
25543
+ if (input.expectedSeq !== currentSeq) return { ok: false, reason: "stale-sequence" };
25544
+ if (current === void 0 && input.state !== "admitted") return { ok: false, reason: "invalid-transition" };
25545
+ if (current?.state === "ended") return { ok: false, reason: "terminal" };
25546
+ if (input.state !== "ended") {
25547
+ if (input.bound === void 0) return { ok: false, reason: "bound-required" };
25548
+ if (!Number.isFinite(input.bound) || input.bound < input.at) return { ok: false, reason: "invalid-bound" };
25549
+ } else if (input.cause === void 0) return { ok: false, reason: "cause-required" };
25550
+ if (current !== void 0 && current.state !== input.state && !TRANSITIONS[current.state].includes(input.state))
25551
+ return { ok: false, reason: "invalid-transition" };
25552
+ const detail = safeLiveStateDetail(input.detail);
25553
+ const liveState = {
25554
+ state: input.state,
25555
+ since: current?.state === input.state ? current.since : input.at,
25556
+ ...input.state !== "ended" ? { bound: input.bound } : {},
25557
+ ...detail !== void 0 ? { detail } : {}
25558
+ };
25559
+ if (current?.state === input.state) return { ok: true, liveState };
25560
+ return {
25561
+ ok: true,
25562
+ liveState,
25563
+ event: {
25564
+ type: "run_state",
25565
+ ...liveState,
25566
+ ...input.state === "ended" ? { cause: input.cause } : {},
25567
+ at: input.at
25568
+ }
25569
+ };
25570
+ }
25571
+ function foldRunLiveState(events, materialized = {}) {
25572
+ let liveState = materialized.liveState;
25573
+ let liveStateSeq = materialized.liveStateSeq ?? 0;
25574
+ let leaseEndsAt;
25575
+ for (const event of [...events].sort((left, right) => (left.seq ?? 0) - (right.seq ?? 0))) {
25576
+ if (event.type === "lease" && event.endsAt !== void 0) leaseEndsAt = event.endsAt;
25577
+ const seq = event.seq ?? 0;
25578
+ if (seq <= liveStateSeq) continue;
25579
+ if (event.type === "run_state" && RUN_LIVE_STATE_NAMES.includes(event.state) && event.since !== void 0) {
25580
+ liveState = {
25581
+ state: event.state,
25582
+ since: event.since,
25583
+ ...event.bound !== void 0 ? { bound: event.bound } : {},
25584
+ ...event.detail !== void 0 ? { detail: event.detail } : {}
25585
+ };
25586
+ liveStateSeq = seq;
25587
+ continue;
25588
+ }
25589
+ if (event.type === "tool_call" && liveState?.state === "working" && event.at !== void 0 && event.boundMs !== void 0) {
25590
+ liveState = { ...liveState, bound: event.at + event.boundMs, detail: "running a tool" };
25591
+ liveStateSeq = seq;
25592
+ continue;
25593
+ }
25594
+ if (event.type === "tool_result" && liveState?.state === "working" && leaseEndsAt !== void 0) {
25595
+ liveState = { ...liveState, bound: leaseEndsAt, detail: "model turn" };
25596
+ liveStateSeq = seq;
25597
+ }
25598
+ }
25599
+ return {
25600
+ ...liveState !== void 0 ? { liveState } : {},
25601
+ ...liveStateSeq > 0 ? { liveStateSeq } : {}
25602
+ };
25603
+ }
25604
+ var RUN_LIVE_STATE_NAMES, LIVE_STATE_DETAIL_MAX, TRANSITIONS, UNSAFE_DETAIL;
25605
+ var init_runLiveState = __esm({
25606
+ "../../src/core/runLiveState.ts"() {
25607
+ "use strict";
25608
+ init_redact();
25609
+ RUN_LIVE_STATE_NAMES = [
25610
+ "admitted",
25611
+ "waiting_deploy",
25612
+ "waiting_repository",
25613
+ "falling_back",
25614
+ "preparing",
25615
+ "working",
25616
+ "waiting_provider",
25617
+ "wrapping_up",
25618
+ "ended"
25619
+ ];
25620
+ LIVE_STATE_DETAIL_MAX = 160;
25621
+ TRANSITIONS = {
25622
+ admitted: ["waiting_deploy", "waiting_repository", "falling_back", "preparing", "working", "wrapping_up", "ended"],
25623
+ waiting_deploy: ["waiting_repository", "falling_back", "preparing", "working", "wrapping_up", "ended"],
25624
+ waiting_repository: ["waiting_deploy", "falling_back", "preparing", "working", "wrapping_up", "ended"],
25625
+ falling_back: ["preparing", "working", "wrapping_up", "ended"],
25626
+ preparing: ["waiting_deploy", "waiting_repository", "falling_back", "working", "wrapping_up", "ended"],
25627
+ working: [
25628
+ "waiting_deploy",
25629
+ "waiting_repository",
25630
+ "falling_back",
25631
+ "preparing",
25632
+ "waiting_provider",
25633
+ "wrapping_up",
25634
+ "ended"
25635
+ ],
25636
+ waiting_provider: ["working", "wrapping_up", "ended"],
25637
+ wrapping_up: ["ended"],
25638
+ ended: []
25639
+ };
25640
+ UNSAFE_DETAIL = /(?:\bimage-stale\b|\battach-failed\b|\bparked-provider\b|<\/?(?:html|body|head)\b|\bbad gateway\b)/i;
25641
+ }
25642
+ });
25643
+
25644
+ // ../../src/core/plane/decide.ts
25645
+ function liveStateWords(state) {
25646
+ switch (state) {
25647
+ case "admitted":
25648
+ return "admitted";
25649
+ case "waiting_deploy":
25650
+ return "waiting for the current deploy";
25651
+ case "waiting_repository":
25652
+ return "waiting for the repository container";
25653
+ case "falling_back":
25654
+ return "switching to a fallback workspace";
25655
+ case "preparing":
25656
+ return "preparing the workspace";
25657
+ case "working":
25658
+ return "working";
25659
+ case "waiting_provider":
25660
+ return "waiting for the model provider";
25661
+ case "wrapping_up":
25662
+ return "wrapping up";
25663
+ case "ended":
25664
+ return "ended";
25665
+ }
25666
+ }
25667
+ function causeOfClose(status3, restarting) {
25668
+ switch (status3) {
25669
+ case "completed":
25670
+ return "completed";
25671
+ case "failed":
25672
+ return "failed";
25673
+ case "stopped_soft":
25674
+ case "stopped_hard":
25675
+ case "stopped":
25676
+ return "stopped";
25677
+ default:
25678
+ return restarting === true ? "resident_replaced" : "lease_lapsed";
25679
+ }
25680
+ }
25681
+ function endingCauseWords(cause) {
25682
+ switch (cause) {
25683
+ case "completed":
25684
+ return "it completed";
25685
+ case "failed":
25686
+ return "it failed";
25687
+ case "stopped":
25688
+ return "it was stopped";
25689
+ case "withdrawn":
25690
+ return "its wait was withdrawn";
25691
+ case "refused":
25692
+ return "its admission was refused";
25693
+ case "lease_lapsed":
25694
+ return "its lease lapsed with no heartbeat";
25695
+ case "resident_replaced":
25696
+ return "the resident container running it was replaced";
25697
+ case "runner_gone":
25698
+ return "the runner instance driving it is gone";
25699
+ }
25700
+ }
25701
+ function isReissueSteerText(text) {
25702
+ return /^the model provider .+ is answering again — re-issue the held turn and continue$/.test(text.trim());
25703
+ }
25704
+ function waitingWords(waiting3) {
25705
+ if (waiting3.length === 0) return "its turn";
25706
+ return waiting3.map(
25707
+ (c) => c.kind === "thread_free" ? "the thread's live run" : c.kind === "deploy_settled" ? "the pending deploy" : c.kind === "seat" ? `a seat on ${c.resident}` : c.kind === "memory" ? `memory on ${c.resident}` : c.kind === "provider_up" ? `the ${c.provider} provider` : `the ${c.window} window`
25708
+ ).join(", then ");
25709
+ }
25710
+ var PLANE_ENDING_CAUSES;
25711
+ var init_decide = __esm({
25712
+ "../../src/core/plane/decide.ts"() {
25713
+ "use strict";
25714
+ init_budgets();
25715
+ PLANE_ENDING_CAUSES = [
25716
+ "completed",
25717
+ "failed",
25718
+ "stopped",
25719
+ "withdrawn",
25720
+ "refused",
25721
+ "lease_lapsed",
25722
+ "resident_replaced",
25723
+ "runner_gone"
25724
+ ];
25725
+ }
25726
+ });
25727
+
25403
25728
  // ../../src/core/runEventLines.ts
25404
25729
  function parseRunEventLines(text) {
25405
25730
  const events = [];
@@ -25465,6 +25790,8 @@ function isRunEvent(v) {
25465
25790
  return typeof o.url === "string" && typeof o.number === "number" && typeof o.created === "boolean";
25466
25791
  case "pushed_head":
25467
25792
  return typeof o.ref === "string" && typeof o.sha === "string" && (o.by === "push" || o.by === "salvage");
25793
+ case "run_state":
25794
+ return typeof o.state === "string" && LIVE_STATES.has(o.state) && typeof o.since === "number" && (o.state === "ended" ? typeof o.cause === "string" && ENDING_CAUSES.has(o.cause) : typeof o.bound === "number");
25468
25795
  case "review_posted":
25469
25796
  return typeof o.repo === "string" && typeof o.number === "number" && typeof o.head === "string";
25470
25797
  case "ship_round":
@@ -25487,12 +25814,16 @@ function isRunEvent(v) {
25487
25814
  return false;
25488
25815
  }
25489
25816
  }
25490
- var NOTE_KINDS;
25817
+ var NOTE_KINDS, LIVE_STATES, ENDING_CAUSES;
25491
25818
  var init_runEventLines = __esm({
25492
25819
  "../../src/core/runEventLines.ts"() {
25493
25820
  "use strict";
25494
25821
  init_runEvents();
25822
+ init_runLiveState();
25823
+ init_decide();
25495
25824
  NOTE_KINDS = new Set(RUN_NOTE_KINDS);
25825
+ LIVE_STATES = new Set(RUN_LIVE_STATE_NAMES);
25826
+ ENDING_CAUSES = new Set(PLANE_ENDING_CAUSES);
25496
25827
  }
25497
25828
  });
25498
25829
 
@@ -28644,23 +28975,23 @@ var init_turnEffort = __esm({
28644
28975
  function tokenize2(text) {
28645
28976
  return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
28646
28977
  }
28647
- function keywordMatch(record2, query) {
28978
+ function keywordMatch(record3, query) {
28648
28979
  const queryTokens = typeof query === "string" ? tokenize2(query) : query;
28649
28980
  if (queryTokens.length === 0) return 0;
28650
- const recordTokens = /* @__PURE__ */ new Set([...tokenize2(record2.keywords.join(" ")), ...tokenize2(record2.text)]);
28981
+ const recordTokens = /* @__PURE__ */ new Set([...tokenize2(record3.keywords.join(" ")), ...tokenize2(record3.text)]);
28651
28982
  let hits = 0;
28652
28983
  for (const token2 of queryTokens) {
28653
28984
  if (recordTokens.has(token2)) hits++;
28654
28985
  }
28655
28986
  return hits / queryTokens.length;
28656
28987
  }
28657
- function recencyScore(record2, now, tau = RECENCY_TAU_MS) {
28658
- const ts = record2.lastUsedAt ?? record2.createdAt;
28988
+ function recencyScore(record3, now, tau = RECENCY_TAU_MS) {
28989
+ const ts = record3.lastUsedAt ?? record3.createdAt;
28659
28990
  const age = Math.max(0, now - ts);
28660
28991
  return Math.exp(-age / tau);
28661
28992
  }
28662
- function scoreRecord(record2, query, now, weights = DEFAULT_WEIGHTS, tau = RECENCY_TAU_MS) {
28663
- return weights.keyword * keywordMatch(record2, query) + weights.recency * recencyScore(record2, now, tau);
28993
+ function scoreRecord(record3, query, now, weights = DEFAULT_WEIGHTS, tau = RECENCY_TAU_MS) {
28994
+ return weights.keyword * keywordMatch(record3, query) + weights.recency * recencyScore(record3, now, tau);
28664
28995
  }
28665
28996
  function estimateTokens(text) {
28666
28997
  return Math.ceil(text.length / 4);
@@ -28668,11 +28999,11 @@ function estimateTokens(text) {
28668
28999
  function applyBudget(records, budget) {
28669
29000
  const out = [];
28670
29001
  let tokens = 0;
28671
- for (const record2 of records) {
29002
+ for (const record3 of records) {
28672
29003
  if (out.length >= budget.maxRecords) break;
28673
- const cost = estimateTokens(renderMemoryBullet(record2));
29004
+ const cost = estimateTokens(renderMemoryBullet(record3));
28674
29005
  if (out.length > 0 && tokens + cost > budget.maxTokens) break;
28675
- out.push(record2);
29006
+ out.push(record3);
28676
29007
  tokens += cost;
28677
29008
  }
28678
29009
  return out;
@@ -28683,8 +29014,8 @@ function memoryBlockPrefix(resource) {
28683
29014
  function sanitizeMemoryField(s) {
28684
29015
  return s.replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029]/g, " ").replace(/\s+/g, " ").trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
28685
29016
  }
28686
- function renderMemoryBullet(record2) {
28687
- return `- ${sanitizeMemoryField(record2.text)} (source: ${sanitizeMemoryField(record2.sourceThreadKey)})`;
29017
+ function renderMemoryBullet(record3) {
29018
+ return `- ${sanitizeMemoryField(record3.text)} (source: ${sanitizeMemoryField(record3.sourceThreadKey)})`;
28688
29019
  }
28689
29020
  function renderMemoryBlock(resource, records) {
28690
29021
  const bullets = records.map(renderMemoryBullet);
@@ -29270,7 +29601,7 @@ function parseConfirmation(v) {
29270
29601
  function isRefusal(v) {
29271
29602
  return typeof v === "string" && CONFIRMATION_REFUSALS.includes(v);
29272
29603
  }
29273
- function judge2(rows, id, actorIds, now, kind) {
29604
+ function judge3(rows, id, actorIds, now, kind) {
29274
29605
  const row2 = rows.get(id);
29275
29606
  if (!row2) return { ok: false, refused: "used" };
29276
29607
  if (kind === "consume" && row2.expiresAt <= now) {
@@ -29340,13 +29671,13 @@ var init_confirmations = __esm({
29340
29671
  }
29341
29672
  async consume(id, actorIds) {
29342
29673
  const rows = this.read();
29343
- const out = judge2(rows, id, actorIds, this.clock(), "consume");
29674
+ const out = judge3(rows, id, actorIds, this.clock(), "consume");
29344
29675
  this.write(rows);
29345
29676
  return out;
29346
29677
  }
29347
29678
  async cancel(id, actorIds) {
29348
29679
  const rows = this.read();
29349
- const out = judge2(rows, id, actorIds, this.clock(), "cancel");
29680
+ const out = judge3(rows, id, actorIds, this.clock(), "cancel");
29350
29681
  this.write(rows);
29351
29682
  return out;
29352
29683
  }
@@ -29354,7 +29685,7 @@ var init_confirmations = __esm({
29354
29685
  const rows = this.read();
29355
29686
  const id = threadRowId(rows, threadKey);
29356
29687
  if (id === void 0) return { ok: false, refused: "used" };
29357
- const out = judge2(rows, id, actorIds, this.clock(), "cancel");
29688
+ const out = judge3(rows, id, actorIds, this.clock(), "cancel");
29358
29689
  this.write(rows);
29359
29690
  return out;
29360
29691
  }
@@ -29835,16 +30166,16 @@ async function reflect(deps) {
29835
30166
  dropped.push(`${placement.from} (${placement.reason})`);
29836
30167
  continue;
29837
30168
  }
29838
- let record2 = plain7;
30169
+ let record3 = plain7;
29839
30170
  if (placement.narrowed) {
29840
- const { supersedes: _supersedes, restates: _restates, ...withoutPointers } = record2;
29841
- record2 = withoutPointers;
30171
+ const { supersedes: _supersedes, restates: _restates, ...withoutPointers } = record3;
30172
+ record3 = withoutPointers;
29842
30173
  const line4 = `${placement.narrowed.from} \u2192 ${placement.target.kind} (${placement.narrowed.reason})`;
29843
30174
  narrowed.set(line4, (narrowed.get(line4) ?? 0) + 1);
29844
30175
  }
29845
30176
  let batch = byScope.get(placement.target.key);
29846
30177
  if (!batch) byScope.set(placement.target.key, batch = []);
29847
- batch.push(record2);
30178
+ batch.push(record3);
29848
30179
  }
29849
30180
  if (narrowed.size > 0)
29850
30181
  warn(`write narrowed by policy: ${[...narrowed].map(([line4, n2]) => `${n2}\xD7 ${line4}`).join(", ")}`);
@@ -31675,8 +32006,8 @@ ${line4}`
31675
32006
  const list = await call(() => api.residents());
31676
32007
  if (list.status !== 200) throw residentFailure(list);
31677
32008
  const residents = list.data.residents ?? [];
31678
- const record2 = residents.find((rec2) => rec2.resource === repoResourceId(args.slug));
31679
- if (!record2) {
32009
+ const record3 = residents.find((rec2) => rec2.resource === repoResourceId(args.slug));
32010
+ if (!record3) {
31680
32011
  const slugs = residents.map(
31681
32012
  (rec2) => String(rec2.resource ?? "").replace(/^repo:/, "").toLowerCase()
31682
32013
  ).filter(Boolean);
@@ -31696,7 +32027,7 @@ ${line4}`
31696
32027
  ${line4}` : ""}`
31697
32028
  );
31698
32029
  }
31699
- body.commands = { ...obj(record2.commands), ...commands };
32030
+ body.commands = { ...obj(record3.commands), ...commands };
31700
32031
  }
31701
32032
  if (options.ref !== void 0) body.defaultRef = options.ref;
31702
32033
  const r = await call(() => api.reconfigure(body));
@@ -31721,11 +32052,11 @@ ${line4}` : ""}`
31721
32052
 
31722
32053
  // ../../src/core/reviewAbridge.ts
31723
32054
  import { resolve as resolve10 } from "node:path";
31724
- function meatArtifactOf(record2) {
31725
- return record2.events.find((e) => isReadingDiff(e) && e.poweredBy === "meat");
32055
+ function meatArtifactOf(record3) {
32056
+ return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "meat");
31726
32057
  }
31727
- function gitArtifactOf(record2) {
31728
- return record2.events.find((e) => isReadingDiff(e) && e.poweredBy === "git");
32058
+ function gitArtifactOf(record3) {
32059
+ return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "git");
31729
32060
  }
31730
32061
  function summarize(a) {
31731
32062
  return {
@@ -31739,16 +32070,16 @@ function summarize(a) {
31739
32070
  };
31740
32071
  }
31741
32072
  async function appendReviewArtifact(store, runId3, artifact, at) {
31742
- const record2 = await store.get(runId3);
31743
- if (!record2) throw new Error("the run's record is gone \u2014 nothing to append to");
31744
- const kept = record2.events.filter((e) => !(isReadingDiff(e) && e.poweredBy === "meat"));
31745
- const replaced = kept.length !== record2.events.length;
31746
- const seq = Math.max(0, ...storedEventSeqs(record2.events), ...record2.events.map((e) => e.seq ?? 0)) + 1;
32073
+ const record3 = await store.get(runId3);
32074
+ if (!record3) throw new Error("the run's record is gone \u2014 nothing to append to");
32075
+ const kept = record3.events.filter((e) => !(isReadingDiff(e) && e.poweredBy === "meat"));
32076
+ const replaced = kept.length !== record3.events.length;
32077
+ const seq = Math.max(0, ...storedEventSeqs(record3.events), ...record3.events.map((e) => e.seq ?? 0)) + 1;
31747
32078
  const event = { type: "review_artifact", artifact: "reading_diff", ...artifact, at, seq };
31748
32079
  const next = fitRecordToBudget({
31749
- ...record2,
32080
+ ...record3,
31750
32081
  events: [...kept, event],
31751
- eventCount: record2.eventCount + (replaced ? 0 : 1),
32082
+ eventCount: record3.eventCount + (replaced ? 0 : 1),
31752
32083
  storedEventCount: kept.length + 1
31753
32084
  });
31754
32085
  if (!next.events.includes(event))
@@ -31837,19 +32168,19 @@ var init_reviewAbridge = __esm({
31837
32168
  async decide(req) {
31838
32169
  const job = this.jobs.get(req.runId);
31839
32170
  if (job) return { state: "running", startedAt: job.startedAt };
31840
- const record2 = await this.deps.store.get(req.runId);
31841
- if (!record2) throw new AbridgeRefusal("not_found", "run not found");
31842
- const existing = meatArtifactOf(record2);
32171
+ const record3 = await this.deps.store.get(req.runId);
32172
+ if (!record3) throw new AbridgeRefusal("not_found", "run not found");
32173
+ const existing = meatArtifactOf(record3);
31843
32174
  if (existing && !req.force) return { state: "done", reused: true, artifact: summarize(existing) };
31844
32175
  const failed = this.failures.get(req.runId);
31845
32176
  if (failed && !req.force) return { state: "failed", ...failed };
31846
- const git2 = gitArtifactOf(record2);
32177
+ const git2 = gitArtifactOf(record3);
31847
32178
  if (!git2)
31848
32179
  throw new AbridgeRefusal("conflict", `run ${req.runId} carries no reading diff \u2014 only PR review runs record one`);
31849
32180
  const startedAt = this.deps.clock();
31850
32181
  const model = req.model ?? this.deps.defaultModel();
31851
32182
  this.failures.delete(req.runId);
31852
- const done = this.produce(record2, git2, model).finally(() => this.jobs.delete(req.runId));
32183
+ const done = this.produce(record3, git2, model).finally(() => this.jobs.delete(req.runId));
31853
32184
  this.jobs.set(req.runId, { startedAt, done });
31854
32185
  return { state: "running", startedAt };
31855
32186
  }
@@ -31859,8 +32190,8 @@ var init_reviewAbridge = __esm({
31859
32190
  if (job) return { state: "running", startedAt: job.startedAt };
31860
32191
  const failed = this.failures.get(runId3);
31861
32192
  if (failed) return { state: "failed", ...failed };
31862
- const record2 = await this.deps.store.get(runId3);
31863
- const existing = record2 ? meatArtifactOf(record2) : void 0;
32193
+ const record3 = await this.deps.store.get(runId3);
32194
+ const existing = record3 ? meatArtifactOf(record3) : void 0;
31864
32195
  return existing ? { state: "done", reused: true, artifact: summarize(existing) } : { state: "absent" };
31865
32196
  }
31866
32197
  /** The running job's final state, or the current state when none runs. */
@@ -31891,10 +32222,10 @@ var init_reviewAbridge = __esm({
31891
32222
  if (!(err2 instanceof AbridgeRefusal)) this.deps.warn(`[reading-diff] ${runId3} abridge failed: ${describe(err2)}`);
31892
32223
  }
31893
32224
  }
31894
- async produce(record2, git2, model) {
32225
+ async produce(record3, git2, model) {
31895
32226
  const at = () => this.deps.clock();
31896
32227
  try {
31897
- const input = await this.input(record2, git2);
32228
+ const input = await this.input(record3, git2);
31898
32229
  const meat = await this.deps.meat({ diff: input.diff, model, timeoutMs: this.deps.timeoutMs() });
31899
32230
  if (!meat.ok) throw new Error(meat.reason);
31900
32231
  const capped4 = capDiff(sanitizeArtifactText(meat.result.diff));
@@ -31910,12 +32241,12 @@ var init_reviewAbridge = __esm({
31910
32241
  inputBytes: input.bytes,
31911
32242
  model
31912
32243
  };
31913
- const stored = await appendReviewArtifact(this.deps.store, record2.id, artifact, at());
31914
- return this.conclude(record2.id, { state: "done", reused: false, artifact: summarize(stored) });
32244
+ const stored = await appendReviewArtifact(this.deps.store, record3.id, artifact, at());
32245
+ return this.conclude(record3.id, { state: "done", reused: false, artifact: summarize(stored) });
31915
32246
  } catch (err2) {
31916
32247
  const failed = { reason: redactSecrets(describe(err2)), at: at() };
31917
- this.failures.set(record2.id, failed);
31918
- return this.conclude(record2.id, { state: "failed", ...failed });
32248
+ this.failures.set(record3.id, failed);
32249
+ return this.conclude(record3.id, { state: "failed", ...failed });
31919
32250
  }
31920
32251
  }
31921
32252
  conclude(runId3, state) {
@@ -31924,8 +32255,8 @@ var init_reviewAbridge = __esm({
31924
32255
  }
31925
32256
  /** The complete diff: GitHub's compare of base...head first; the recorded
31926
32257
  * git diff only when that fails AND it is whole. */
31927
- async input(record2, git2) {
31928
- const meta = record2.events.find((e) => e.type === "run_meta");
32258
+ async input(record3, git2) {
32259
+ const meta = record3.events.find((e) => e.type === "run_meta");
31929
32260
  let compareFailure;
31930
32261
  if (!meta?.repo || !meta.headSha) compareFailure = "the run records no repo and head to compare";
31931
32262
  else {
@@ -31944,7 +32275,7 @@ var init_reviewAbridge = __esm({
31944
32275
  }
31945
32276
  }
31946
32277
  if (git2.truncated) throw new Error(`diff unavailable: ${compareFailure} and the recorded diff is truncated`);
31947
- this.deps.warn(`[reading-diff] ${record2.id} using the recorded diff: ${compareFailure}`);
32278
+ this.deps.warn(`[reading-diff] ${record3.id} using the recorded diff: ${compareFailure}`);
31948
32279
  return { diff: git2.diff, source: "recorded", bytes: Buffer.byteLength(git2.diff) };
31949
32280
  }
31950
32281
  };
@@ -32237,6 +32568,8 @@ function assembleRunRecord(input) {
32237
32568
  ...seal?.replyNote !== void 0 ? { replyNote: seal.replyNote } : {},
32238
32569
  ...snap !== null ? { stepCount: snap.stepCount } : {},
32239
32570
  status: input.status,
32571
+ ...snap?.liveState !== void 0 ? { liveState: snap.liveState } : {},
32572
+ ...snap?.liveStateSeq !== void 0 ? { liveStateSeq: snap.liveStateSeq } : {},
32240
32573
  eventCount: Math.max(snap?.eventCount ?? atFinish.length, seal?.eventCount ?? 0),
32241
32574
  storedEventCount: events.length,
32242
32575
  truncated: false,
@@ -32420,43 +32753,6 @@ var init_record = __esm({
32420
32753
  }
32421
32754
  });
32422
32755
 
32423
- // ../../src/core/plane/decide.ts
32424
- function endingCauseWords(cause) {
32425
- switch (cause) {
32426
- case "completed":
32427
- return "it completed";
32428
- case "failed":
32429
- return "it failed";
32430
- case "stopped":
32431
- return "it was stopped";
32432
- case "withdrawn":
32433
- return "its wait was withdrawn";
32434
- case "refused":
32435
- return "its admission was refused";
32436
- case "lease_lapsed":
32437
- return "its lease lapsed with no heartbeat";
32438
- case "resident_replaced":
32439
- return "the resident container running it was replaced";
32440
- case "runner_gone":
32441
- return "the runner instance driving it is gone";
32442
- }
32443
- }
32444
- function isReissueSteerText(text) {
32445
- return /^the model provider .+ is answering again — re-issue the held turn and continue$/.test(text.trim());
32446
- }
32447
- function waitingWords(waiting2) {
32448
- if (waiting2.length === 0) return "its turn";
32449
- return waiting2.map(
32450
- (c) => c.kind === "thread_free" ? "the thread's live run" : c.kind === "deploy_settled" ? "the pending deploy" : c.kind === "seat" ? `a seat on ${c.resident}` : c.kind === "memory" ? `memory on ${c.resident}` : c.kind === "provider_up" ? `the ${c.provider} provider` : `the ${c.window} window`
32451
- ).join(", then ");
32452
- }
32453
- var init_decide = __esm({
32454
- "../../src/core/plane/decide.ts"() {
32455
- "use strict";
32456
- init_budgets();
32457
- }
32458
- });
32459
-
32460
32756
  // ../../src/core/runsService.ts
32461
32757
  function runResource(view) {
32462
32758
  return {
@@ -32493,6 +32789,8 @@ function ledgerView(row2, events) {
32493
32789
  ...m.repo !== void 0 ? { repo: m.repo } : {},
32494
32790
  startedAt: row2.startedAt,
32495
32791
  finished: false,
32792
+ ...row2.liveState !== void 0 ? { liveState: row2.liveState } : {},
32793
+ ...row2.liveStateSeq !== void 0 ? { liveStateSeq: row2.liveStateSeq } : {},
32496
32794
  eventCount: events.length,
32497
32795
  ...activity !== void 0 ? { activity } : {},
32498
32796
  ...m.sourceUrl !== void 0 ? { sourceUrl: m.sourceUrl } : {},
@@ -32531,6 +32829,8 @@ function liveView(s) {
32531
32829
  ...s.stepCount !== void 0 ? { stepCount: s.stepCount } : {},
32532
32830
  ...s.schema !== void 0 ? { schema: s.schema } : {},
32533
32831
  ...s.status !== void 0 ? { status: s.status } : {},
32832
+ ...s.liveState !== void 0 ? { liveState: s.liveState } : {},
32833
+ ...s.liveStateSeq !== void 0 ? { liveStateSeq: s.liveStateSeq } : {},
32534
32834
  eventCount: s.eventCount,
32535
32835
  ...s.stop ? { stop: s.stop } : {},
32536
32836
  ...s.activity !== void 0 ? { activity: s.activity } : {},
@@ -32628,7 +32928,7 @@ function createRunsService(deps) {
32628
32928
  const prices = deps.prices ?? NO_PRICES;
32629
32929
  const clock4 = deps.clock ?? systemClock;
32630
32930
  const warn = deps.warn ?? ((m) => console.warn(m));
32631
- const describe12 = (err2) => err2 instanceof Error ? err2.message : String(err2);
32931
+ const describe13 = (err2) => err2 instanceof Error ? err2.message : String(err2);
32632
32932
  let listing;
32633
32933
  const liveRows = () => {
32634
32934
  if (!ledger) return null;
@@ -32648,7 +32948,7 @@ function createRunsService(deps) {
32648
32948
  try {
32649
32949
  rows = await pending2;
32650
32950
  } catch (err2) {
32651
- warn(`[runs] run ledger list failed \u2014 showing this process's runs only: ${describe12(err2)}`);
32951
+ warn(`[runs] run ledger list failed \u2014 showing this process's runs only: ${describe13(err2)}`);
32652
32952
  return [];
32653
32953
  }
32654
32954
  const foreign = rows.filter((row2) => !registry.getById(row2.runId));
@@ -32658,7 +32958,7 @@ function createRunsService(deps) {
32658
32958
  try {
32659
32959
  events = await ledger.readEvents(row2.runId);
32660
32960
  } catch (err2) {
32661
- warn(`[runs] run ledger events read failed for ${row2.runId}: ${describe12(err2)}`);
32961
+ warn(`[runs] run ledger events read failed for ${row2.runId}: ${describe13(err2)}`);
32662
32962
  }
32663
32963
  return ledgerView(row2, events);
32664
32964
  })
@@ -32672,7 +32972,7 @@ function createRunsService(deps) {
32672
32972
  if (!row2) return null;
32673
32973
  return { row: row2, events: await ledger.readEvents(id) };
32674
32974
  } catch (err2) {
32675
- warn(`[runs] run ledger read failed for ${id}: ${describe12(err2)}`);
32975
+ warn(`[runs] run ledger read failed for ${id}: ${describe13(err2)}`);
32676
32976
  return null;
32677
32977
  }
32678
32978
  };
@@ -32681,21 +32981,23 @@ function createRunsService(deps) {
32681
32981
  try {
32682
32982
  const row2 = await ledger.planeQueued(id);
32683
32983
  if (!row2) return null;
32984
+ const stampedChannel = row2.request.channelId;
32684
32985
  const channelAt = row2.threadKey.lastIndexOf(":");
32685
- const waiting2 = waitingWords(row2.conditions);
32986
+ const channelId = typeof stampedChannel === "string" ? stampedChannel : channelAt > 0 ? row2.threadKey.slice(0, channelAt) : void 0;
32987
+ const waiting3 = waitingWords(row2.conditions);
32686
32988
  return {
32687
32989
  id: row2.runId,
32688
- ...channelAt > 0 ? { channelId: row2.threadKey.slice(0, channelAt) } : {},
32990
+ ...channelId !== void 0 ? { channelId } : {},
32689
32991
  userId: row2.requester,
32690
32992
  threadKey: row2.threadKey,
32691
32993
  startedAt: row2.queuedAt,
32692
32994
  finished: false,
32693
32995
  eventCount: 0,
32694
- queued: { state: row2.state, position: row2.position, waiting: waiting2 },
32695
- activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${waiting2}` : row2.state === "admitted" ? "admitted \u2014 starting" : "withdrawn"
32996
+ queued: { state: row2.state, position: row2.position, waiting: waiting3 },
32997
+ activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${waiting3}` : row2.state === "admitted" ? "admitted \u2014 starting" : "withdrawn"
32696
32998
  };
32697
32999
  } catch (err2) {
32698
- warn(`[runs] plane queued read failed for ${id}: ${describe12(err2)}`);
33000
+ warn(`[runs] plane queued read failed for ${id}: ${describe13(err2)}`);
32699
33001
  return null;
32700
33002
  }
32701
33003
  };
@@ -32713,7 +33015,7 @@ function createRunsService(deps) {
32713
33015
  row2 = await storeSummary(id);
32714
33016
  } catch (err2) {
32715
33017
  warn(
32716
- `[runs] history store read failed for ${id} \u2014 serving the registry row without its record: ${describe12(err2)}`
33018
+ `[runs] history store read failed for ${id} \u2014 serving the registry row without its record: ${describe13(err2)}`
32717
33019
  );
32718
33020
  return {};
32719
33021
  }
@@ -32758,12 +33060,12 @@ function createRunsService(deps) {
32758
33060
  if (marked.ok) runnerStopped = true;
32759
33061
  else warn(`[runs] the runner's stop mark was refused for ${id} (${marked.reason})`);
32760
33062
  } catch (err2) {
32761
- warn(`[runs] the runner's stop mark failed for ${id}: ${describe12(err2)}`);
33063
+ warn(`[runs] the runner's stop mark failed for ${id}: ${describe13(err2)}`);
32762
33064
  }
32763
33065
  try {
32764
33066
  unitRows = await units.listUnits(instanceId);
32765
33067
  } catch (err2) {
32766
- warn(`[runs] unit rows unavailable for the hard stop of ${id}: ${describe12(err2)}`);
33068
+ warn(`[runs] unit rows unavailable for the hard stop of ${id}: ${describe13(err2)}`);
32767
33069
  }
32768
33070
  }
32769
33071
  registry.publish(id, { type: "answer", text: hostedSealAnswer(unitRows, runnerStopped), at: clock4() });
@@ -32777,7 +33079,7 @@ function createRunsService(deps) {
32777
33079
  if (row2) {
32778
33080
  const finishedAt = snap?.finishedAt ?? at;
32779
33081
  const m = row2.meta;
32780
- const record2 = assembleRunRecord({
33082
+ const record3 = assembleRunRecord({
32781
33083
  run: { id, ...summary?.label !== void 0 ? { label: summary.label } : {} },
32782
33084
  snap,
32783
33085
  agent: m.agent ?? "ship",
@@ -32805,11 +33107,11 @@ function createRunsService(deps) {
32805
33107
  }),
32806
33108
  seal
32807
33109
  });
32808
- const done = await ledger.finish(id, row2.ownerGen, record2);
33110
+ const done = await ledger.finish(id, row2.ownerGen, record3);
32809
33111
  if (!done.ok) warn(`[runs] run ledger finish refused for ${id} (${done.reason ?? "unknown"})`);
32810
33112
  }
32811
33113
  } catch (err2) {
32812
- warn(`[runs] run ledger finish failed for ${id}: ${describe12(err2)}`);
33114
+ warn(`[runs] run ledger finish failed for ${id}: ${describe13(err2)}`);
32813
33115
  }
32814
33116
  }
32815
33117
  return { ok: true, value: { id, mode: "hard", state: "stopping" } };
@@ -32903,12 +33205,12 @@ function createRunsService(deps) {
32903
33205
  const queued = await queuedRun(id);
32904
33206
  return queued ? { ok: true, value: queued } : notFound;
32905
33207
  }
32906
- const record2 = await storeGet(id);
32907
- if (!record2) {
33208
+ const record3 = await storeGet(id);
33209
+ if (!record3) {
32908
33210
  const queued = await queuedRun(id);
32909
33211
  return queued ? { ok: true, value: queued } : notFound;
32910
33212
  }
32911
- const { events, ...rest } = record2;
33213
+ const { events, ...rest } = record3;
32912
33214
  const view = persistedView(rest, prices);
32913
33215
  view.events = events;
32914
33216
  return { ok: true, value: view };
@@ -32988,7 +33290,7 @@ function createRunsService(deps) {
32988
33290
  const r = await ledger.requestStop(id, mode);
32989
33291
  if (r.ok) return { ok: true, value: { id, mode, state: "stopping" } };
32990
33292
  } catch (err2) {
32991
- warn(`[runs] run ledger stop failed for ${id}: ${describe12(err2)}`);
33293
+ warn(`[runs] run ledger stop failed for ${id}: ${describe13(err2)}`);
32992
33294
  }
32993
33295
  }
32994
33296
  if (await storeSummary(id)) return conflict;
@@ -32998,7 +33300,7 @@ function createRunsService(deps) {
32998
33300
  if (r.withdrawn) return { ok: true, value: { id, mode, state: "withdrawn" } };
32999
33301
  if (await ledger.planeQueued(id) !== null) return conflict;
33000
33302
  } catch (err2) {
33001
- warn(`[runs] plane withdraw failed for ${id}: ${describe12(err2)}`);
33303
+ warn(`[runs] plane withdraw failed for ${id}: ${describe13(err2)}`);
33002
33304
  }
33003
33305
  }
33004
33306
  return notFound;
@@ -34789,11 +35091,11 @@ var init_runLedgerWorker = __esm({
34789
35091
  this.checkIds(runId3, gen);
34790
35092
  return this.writeTurns(runId3, gen, turns, session);
34791
35093
  }
34792
- async step(runId3, gen, record2, turns, session) {
35094
+ async step(runId3, gen, record3, turns, session) {
34793
35095
  this.checkIds(runId3, gen);
34794
35096
  const written = await this.writeTurns(runId3, gen, turns, session);
34795
35097
  if (!written.ok) return written;
34796
- return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record: record2 }));
35098
+ return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record: record3 }));
34797
35099
  }
34798
35100
  checkSessionKey(key) {
34799
35101
  if (!SESSION_KEY_PATTERN.test(key))
@@ -34929,6 +35231,11 @@ var init_runLedgerWorker = __esm({
34929
35231
  if (events.length === 0) return { ok: true };
34930
35232
  return this.fenceResult(await this.post("/runs/append", { storeKey: this.opts.storeKey, runId: runId3, gen, events }));
34931
35233
  }
35234
+ async assignLiveState(runId3, gen, assignment) {
35235
+ this.checkIds(runId3, gen);
35236
+ const r = await this.post("/runs/live-state", { storeKey: this.opts.storeKey, runId: runId3, gen, assignment });
35237
+ return r.data;
35238
+ }
34932
35239
  async setState(runId3, gen, state) {
34933
35240
  this.checkIds(runId3, gen);
34934
35241
  return this.fenceResult(await this.post("/runs/state", { storeKey: this.opts.storeKey, runId: runId3, gen, state }));
@@ -34959,20 +35266,20 @@ var init_runLedgerWorker = __esm({
34959
35266
  this.checkIds(runId3, gen);
34960
35267
  return this.fenceResult(await this.post("/runs/finishing", { storeKey: this.opts.storeKey, runId: runId3, gen }));
34961
35268
  }
34962
- async finish(runId3, gen, record2) {
35269
+ async finish(runId3, gen, record3) {
34963
35270
  this.checkIds(runId3, gen);
34964
- const point = pointOf(record2, this.opts.prices);
35271
+ const point = pointOf(record3, this.opts.prices);
34965
35272
  const r = await this.post("/runs/finish", {
34966
35273
  storeKey: this.opts.storeKey,
34967
35274
  runId: runId3,
34968
35275
  gen,
34969
- record: record2,
35276
+ record: record3,
34970
35277
  ...point !== void 0 ? { point } : {}
34971
35278
  });
34972
35279
  const f = this.fenceResult(r);
34973
35280
  if (!f.ok) return f;
34974
- if (record2.session)
34975
- await this.post("/runs/session/clear-owner", { key: record2.session.key, runId: runId3, gen }).catch(() => {
35281
+ if (record3.session)
35282
+ await this.post("/runs/session/clear-owner", { key: record3.session.key, runId: runId3, gen }).catch(() => {
34976
35283
  });
34977
35284
  return { ok: true, stored: r.data.stored === true };
34978
35285
  }
@@ -35671,6 +35978,19 @@ function runPageLink(id) {
35671
35978
  if (!base) return void 0;
35672
35979
  return `${base.replace(/\/+$/, "")}/runs/${encodeURIComponent(id)}`;
35673
35980
  }
35981
+ function threadPageLink(threadKey, publicBaseUrl = process.env.PUBLIC_BASE_URL) {
35982
+ if (!publicBaseUrl?.trim()) return void 0;
35983
+ try {
35984
+ const base = new URL(publicBaseUrl.trim());
35985
+ if (!["https:", "http:"].includes(base.protocol) || base.username || base.password) return void 0;
35986
+ base.search = "";
35987
+ base.hash = "";
35988
+ base.pathname = `${base.pathname.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadKey).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`;
35989
+ return base.href;
35990
+ } catch {
35991
+ return void 0;
35992
+ }
35993
+ }
35674
35994
  function artifactLink(id, key, token2) {
35675
35995
  const page2 = runPageLink(id);
35676
35996
  if (!page2) return void 0;
@@ -35757,6 +36077,9 @@ function activityLine(e) {
35757
36077
  case "lease":
35758
36078
  return "lease started";
35759
36079
  // the harness's clocks: head material the run loop keeps off the card — never arrives here
36080
+ case "run_state":
36081
+ return "";
36082
+ // Existing renderers stay until the surface cutover to the shared wording table.
35760
36083
  case "skill_use":
35761
36084
  return `\u{1F4DA} skill ${e.skill} loaded`;
35762
36085
  case "artifact":
@@ -36345,9 +36668,9 @@ function recordPrOf(ctx) {
36345
36668
  }
36346
36669
  function inheritedPr(thread, records, repo) {
36347
36670
  const turn = thread.pr && thread.pr.repo === repo ? { repo, number: thread.pr.number, source: "turn", at: thread.prAt } : void 0;
36348
- const record2 = records?.pr && records.pr.repo === repo ? { repo, number: records.pr.number, source: "record", at: records.pr.at } : void 0;
36349
- if (!turn || !record2) return turn ?? record2;
36350
- return turn.at !== void 0 && record2.at !== void 0 && turn.at > record2.at ? turn : record2;
36671
+ const record3 = records?.pr && records.pr.repo === repo ? { repo, number: records.pr.number, source: "record", at: records.pr.at } : void 0;
36672
+ if (!turn || !record3) return turn ?? record3;
36673
+ return turn.at !== void 0 && record3.at !== void 0 && turn.at > record3.at ? turn : record3;
36351
36674
  }
36352
36675
  async function openPrHeadSha(pr) {
36353
36676
  const head = await prHead(pr).catch(() => void 0);
@@ -36700,7 +37023,7 @@ async function attachRoundWorkspace(input) {
36700
37023
  ...input.round.stopSignal !== void 0 ? { stopSignal: input.round.stopSignal } : {},
36701
37024
  ...input.round.remainingMs !== void 0 ? { remainingMs: input.round.remainingMs } : {},
36702
37025
  ...input.round.requester !== void 0 ? { requester: input.round.requester } : {},
36703
- ...input.round.onSetupNote !== void 0 ? { onSetupNote: input.round.onSetupNote } : {}
37026
+ ...input.round.onLiveStateObservation !== void 0 ? { onLiveStateObservation: input.round.onLiveStateObservation } : {}
36704
37027
  },
36705
37028
  input.span
36706
37029
  );
@@ -36955,7 +37278,7 @@ async function classifyMove(fetchPrCommits, q) {
36955
37278
  async function runReviewPostStep(input) {
36956
37279
  const { agent, repoCtx, verdict, carried, logKey } = input;
36957
37280
  const { reviewHead, observedHead } = input.heads;
36958
- const record2 = (outcome) => {
37281
+ const record3 = (outcome) => {
36959
37282
  if (input.publish && agent.name === "review" && !input.hardStopped) {
36960
37283
  const where = outcome.posted ? void 0 : repoCtx.repo && repoCtx.pr ? `${repoCtx.repo}#${repoCtx.pr}` : void 0;
36961
37284
  input.publish(
@@ -37054,7 +37377,7 @@ ${carriedFooter(carried)}` : rendered;
37054
37377
  console.error(`[review-post] ${logKey} failed for ${where}: ${reason2}`);
37055
37378
  await input.reply(`\u2139\uFE0F Review not posted to ${where}: ${reason2} \u2014 this verdict is Slack-only.`).catch(() => {
37056
37379
  });
37057
- return record2({ posted: false, reason: reason2 });
37380
+ return record3({ posted: false, reason: reason2 });
37058
37381
  }
37059
37382
  console.log(
37060
37383
  `[review-post] ${logKey} \u2192 ${where} (${verdict?.verdict ?? "no verdict"})${carried ? ` carried ${carried.reviewed.slice(0, 7)} \u2192 ${pinned.slice(0, 7)}` : ""}`
@@ -37070,14 +37393,14 @@ ${carriedFooter(carried)}` : rendered;
37070
37393
  await input.reply(moved).catch(() => {
37071
37394
  });
37072
37395
  }
37073
- return record2({
37396
+ return record3({
37074
37397
  posted: true,
37075
37398
  target: { repo: postTarget.repo, number: postTarget.number },
37076
37399
  head: pinned,
37077
37400
  ...verdict !== void 0 ? { verdict: verdict.verdict } : {}
37078
37401
  });
37079
37402
  }
37080
- return record2({ posted: false, reason: skipReason });
37403
+ return record3({ posted: false, reason: skipReason });
37081
37404
  }
37082
37405
  var init_reviewRound = __esm({
37083
37406
  "../../src/core/reviewRound.ts"() {
@@ -37434,11 +37757,11 @@ var init_authorize2 = __esm({
37434
37757
  });
37435
37758
 
37436
37759
  // ../../src/core/runHistoryWriter.ts
37437
- async function putOnce(sink, record2) {
37760
+ async function putOnce(sink, record3) {
37438
37761
  try {
37439
- return await sink.put(record2);
37762
+ return await sink.put(record3);
37440
37763
  } catch (err2) {
37441
- sink.abandoned(record2, describe2(err2));
37764
+ sink.abandoned(record3, describe2(err2));
37442
37765
  throw err2;
37443
37766
  }
37444
37767
  }
@@ -37462,28 +37785,28 @@ function createRunHistoryWriter(opts) {
37462
37785
  opts.warn(`[run-history] onPersisted hook failed for ${id}: ${describe2(err2)}`);
37463
37786
  }
37464
37787
  };
37465
- const attemptAll = async (record2, flag2, sink, span) => {
37788
+ const attemptAll = async (record3, flag2, sink, span) => {
37466
37789
  const attempts = RUN_HISTORY_RETRY_DELAYS_MS.length + 1;
37467
37790
  const giveUp = async (why) => {
37468
37791
  failures++;
37469
37792
  try {
37470
- sink.abandoned(record2, why);
37793
+ sink.abandoned(record3, why);
37471
37794
  } catch (err2) {
37472
- opts.warn(`[run-history] abandoned hook failed for ${record2.id}: ${describe2(err2)}`);
37795
+ opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(err2)}`);
37473
37796
  }
37474
37797
  if (sink === opts.store || flag2?.superseded) return;
37475
37798
  try {
37476
- await opts.store.put(record2, span ? { span } : void 0);
37477
- if (!flag2) persisted(record2.id);
37799
+ await opts.store.put(record3, span ? { span } : void 0);
37800
+ if (!flag2) persisted(record3.id);
37478
37801
  } catch (err2) {
37479
- opts.warn(`[run-history] ${record2.id} store fallback after give-up failed: ${describe2(err2)}`);
37802
+ opts.warn(`[run-history] ${record3.id} store fallback after give-up failed: ${describe2(err2)}`);
37480
37803
  }
37481
37804
  };
37482
37805
  for (let attempt = 1; ; attempt++) {
37483
37806
  if (flag2?.superseded) return;
37484
37807
  try {
37485
- await sink.put(record2, span ? { span } : void 0);
37486
- if (!flag2) persisted(record2.id);
37808
+ await sink.put(record3, span ? { span } : void 0);
37809
+ if (!flag2) persisted(record3.id);
37487
37810
  return;
37488
37811
  } catch (err2) {
37489
37812
  if (err2 instanceof RouteMissingError) {
@@ -37496,12 +37819,12 @@ function createRunHistoryWriter(opts) {
37496
37819
  return;
37497
37820
  }
37498
37821
  if (err2 instanceof PermanentStoreError) {
37499
- opts.warn(`[run-history] ${record2.id} not persisted (permanent, not retried): ${describe2(err2)}`);
37822
+ opts.warn(`[run-history] ${record3.id} not persisted (permanent, not retried): ${describe2(err2)}`);
37500
37823
  await giveUp(describe2(err2));
37501
37824
  return;
37502
37825
  }
37503
37826
  if (attempt >= attempts) {
37504
- opts.warn(`[run-history] ${record2.id} not persisted after ${attempts} attempts: ${describe2(err2)}`);
37827
+ opts.warn(`[run-history] ${record3.id} not persisted after ${attempts} attempts: ${describe2(err2)}`);
37505
37828
  await giveUp(`not persisted after ${attempts} attempts: ${describe2(err2)}`);
37506
37829
  return;
37507
37830
  }
@@ -37510,24 +37833,24 @@ function createRunHistoryWriter(opts) {
37510
37833
  }
37511
37834
  };
37512
37835
  return {
37513
- write(record2, writeOpts) {
37836
+ write(record3, writeOpts) {
37514
37837
  let flag2;
37515
37838
  if (writeOpts?.provisional === true) {
37516
- if (finals.has(record2.id)) return;
37517
- flag2 = { id: record2.id, superseded: false };
37839
+ if (finals.has(record3.id)) return;
37840
+ flag2 = { id: record3.id, superseded: false };
37518
37841
  provisionalFlags.add(flag2);
37519
37842
  } else {
37520
- finals.add(record2.id);
37521
- for (const f of provisionalFlags) if (f.id === record2.id) f.superseded = true;
37843
+ finals.add(record3.id);
37844
+ for (const f of provisionalFlags) if (f.id === record3.id) f.superseded = true;
37522
37845
  }
37523
37846
  const sink = writeOpts?.via ?? opts.store;
37524
- const p = attemptAll(record2, flag2, sink, writeOpts?.span).catch((err2) => {
37847
+ const p = attemptAll(record3, flag2, sink, writeOpts?.span).catch((err2) => {
37525
37848
  failures++;
37526
- opts.warn(`[run-history] ${record2.id} writer failed unexpectedly: ${describe2(err2)}`);
37849
+ opts.warn(`[run-history] ${record3.id} writer failed unexpectedly: ${describe2(err2)}`);
37527
37850
  try {
37528
- sink.abandoned(record2, `writer failed unexpectedly: ${describe2(err2)}`);
37851
+ sink.abandoned(record3, `writer failed unexpectedly: ${describe2(err2)}`);
37529
37852
  } catch (hookErr) {
37530
- opts.warn(`[run-history] abandoned hook failed for ${record2.id}: ${describe2(hookErr)}`);
37853
+ opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(hookErr)}`);
37531
37854
  }
37532
37855
  }).finally(() => {
37533
37856
  if (flag2) provisionalFlags.delete(flag2);
@@ -37890,12 +38213,12 @@ async function closeRestartRow(adopted, restart, why) {
37890
38213
  async function closeResumedRow(adopted, resume, why, status3 = "interrupted", opts) {
37891
38214
  try {
37892
38215
  const finishedAt = (opts?.clock ?? systemClock)();
37893
- const record2 = {
38216
+ const record3 = {
37894
38217
  ...reclaimedRunRecord({ row: resume.row, events: resume.events, status: status3, finishedAt }),
37895
38218
  ...opts?.restarting === true ? { restarting: true, restartUntil: finishedAt + RESTART_CLAIM_GRACE_MS } : {}
37896
38219
  };
37897
- await putOnce(adopted.sink, record2);
37898
- return record2;
38220
+ await putOnce(adopted.sink, record3);
38221
+ return record3;
37899
38222
  } catch (err2) {
37900
38223
  console.warn(
37901
38224
  `[resume] ${resume.row.runId} could not be closed (${why}): ${err2 instanceof Error ? err2.message : String(err2)}`
@@ -38586,6 +38909,8 @@ function summaryOf(run2, now) {
38586
38909
  ...m?.hosted ? { hosted: true } : {},
38587
38910
  ...run2.instanceId !== void 0 ? { instanceId: run2.instanceId } : {},
38588
38911
  ...run2.pipeline !== void 0 ? { pipeline: run2.pipeline } : {},
38912
+ ...run2.liveState !== void 0 ? { liveState: run2.liveState } : {},
38913
+ ...run2.liveStateSeq !== void 0 ? { liveStateSeq: run2.liveStateSeq } : {},
38589
38914
  finished: run2.finished,
38590
38915
  startedAt: run2.startedAt,
38591
38916
  ...run2.finishedAt !== void 0 ? { finishedAt: run2.finishedAt } : {},
@@ -38621,7 +38946,9 @@ function snapshotOf(run2) {
38621
38946
  ...run2.meta?.receivedAt !== void 0 ? { receivedAt: run2.meta.receivedAt } : {},
38622
38947
  eventCount: run2.eventCount,
38623
38948
  stepCount: run2.stepCount,
38624
- truncated: run2.eventCount > run2.backlog.length
38949
+ truncated: run2.eventCount > run2.backlog.length,
38950
+ ...run2.liveState !== void 0 ? { liveState: run2.liveState } : {},
38951
+ ...run2.liveStateSeq !== void 0 ? { liveStateSeq: run2.liveStateSeq } : {}
38625
38952
  };
38626
38953
  }
38627
38954
  function sealResultOf(run2) {
@@ -38705,6 +39032,7 @@ var init_runRegistry = __esm({
38705
39032
  init_projections();
38706
39033
  init_indexFeed();
38707
39034
  init_runPace();
39035
+ init_runLiveState();
38708
39036
  PACE_RING_CAP = 1200;
38709
39037
  UNSEALED_HOLD_MS = 15 * 6e4;
38710
39038
  RUN_LABEL_MAX2 = 200;
@@ -38778,6 +39106,10 @@ var init_runRegistry = __esm({
38778
39106
  if (!isSpanRecord(event)) run2.stepCount++;
38779
39107
  appendToBacklog(run2, this.bounds, { ...event, seq });
38780
39108
  }
39109
+ const replayedLiveState = foldRunLiveState(opts.replay ?? [], opts.liveState);
39110
+ const folded = opts.resetLiveState ? { liveStateSeq: replayedLiveState.liveStateSeq } : replayedLiveState;
39111
+ run2.liveState = folded.liveState;
39112
+ run2.liveStateSeq = folded.liveStateSeq;
38781
39113
  this.index.notify({ type: "upsert", run: summaryOf(run2, this.now()) });
38782
39114
  return { id, token: token2, control: run2.control, ...stored !== void 0 ? { label: stored } : {} };
38783
39115
  }
@@ -38866,6 +39198,32 @@ var init_runRegistry = __esm({
38866
39198
  }
38867
39199
  if (!span) this.index.notify({ type: "upsert", run: summaryOf(run2, this.now()) });
38868
39200
  }
39201
+ /** Apply an assignment only after its durable transaction acknowledged. The
39202
+ * boundary keeps the ledger-assigned stream sequence; subscribers cannot
39203
+ * observe either half before this call. */
39204
+ commitLiveState(id, committed) {
39205
+ const run2 = this.runs.get(id);
39206
+ if (!run2 || run2.finished || run2.sealedAt !== void 0) return false;
39207
+ const event = committed.event;
39208
+ if (event !== void 0) {
39209
+ const seq = event.seq;
39210
+ if (seq === void 0 || seq !== run2.eventCount + 1) return false;
39211
+ run2.eventCount = seq;
39212
+ run2.stepCount++;
39213
+ const stamped = { ...event, seq };
39214
+ appendToBacklog(run2, this.bounds, stamped);
39215
+ for (const sub of run2.subscribers) {
39216
+ try {
39217
+ sub.onEvent(stamped, seq);
39218
+ } catch {
39219
+ }
39220
+ }
39221
+ }
39222
+ run2.liveState = committed.liveState;
39223
+ run2.liveStateSeq = committed.liveStateSeq;
39224
+ this.index.notify({ type: "upsert", run: summaryOf(run2, this.now()) });
39225
+ return true;
39226
+ }
38869
39227
  /** Mark a run finished — the agent stopped: stamp `finishedAt`, send every
38870
39228
  * attached subscriber the `finished` frame WITHOUT detaching it (span records
38871
39229
  * still flow until the seal), and upsert the index. `status` is the terminal
@@ -40277,11 +40635,13 @@ async function ownerOf2(runs, unitsOf, threadKey) {
40277
40635
  const units = await unitsOf(instanceId).catch(() => []);
40278
40636
  const unit = units.find((u) => u.threadKey === threadKey && u.ending === void 0);
40279
40637
  if (unit !== void 0) return { kind: "unit", instanceId, unit };
40280
- const ended = units.find(
40638
+ const ended = units.filter(
40281
40639
  (u) => u.threadKey === threadKey && u.ending !== void 0 && u.ending.kind !== "merged" && u.ending.kind !== "already_landed"
40282
40640
  );
40283
40641
  const ship = runs.find((r) => r.instanceId === instanceId && r.finished);
40284
- if (ended !== void 0 && ship !== void 0) return { kind: "pipeline", instanceId, run: ship, unit: ended };
40642
+ if (ended.length === 1 && ship !== void 0) return { kind: "pipeline", instanceId, run: ship, unit: ended[0] };
40643
+ if (ended.length > 1 && ship !== void 0)
40644
+ return { kind: "pipeline_ambiguous", instanceId, run: ship, units: ended };
40285
40645
  }
40286
40646
  const agent = stickyAgentOf(runs);
40287
40647
  if (agent !== void 0) return { kind: "session", agent };
@@ -40492,8 +40852,9 @@ function ownedProjection(p) {
40492
40852
  function ownerNote(owner) {
40493
40853
  const who2 = owner.kind === "live" ? `a live run${owner.runId !== void 0 ? ` (\`${owner.runId}\`)` : ""}` : owner.kind === "unit" ? `the unfinished plan unit ${owner.unit}` : `the ended pipeline for unmerged plan unit ${owner.unit}`;
40494
40854
  const steer = owner.kind === "live" && owner.runId !== void 0 ? ` bind \`steer run ${owner.runId} <words>\` to deliver it,` : "";
40495
- const fold = owner.kind === "pipeline" ? " Any steer decision also folds, so the pipeline's original task is re-issued instead of targeting an ended transcript run." : "";
40496
- return `This thread is owned by ${who2}: the request below is a follow-up for that owner. To act on it,${steer} bind a read command, or ask a question. Any other decision \u2014 a refusal, a preset, a write \u2014 folds the whole message into the owner unchanged and posts no answer.${fold}`;
40855
+ if (owner.kind === "pipeline")
40856
+ return `This thread is owned by ${who2}: the request below is a continuation of that durable task. Read tools may ground the decision, but every action folds into the owner so the current pull request is re-read and the pipeline resumes; an informational command or question is not fulfillment.`;
40857
+ return `This thread is owned by ${who2}: the request below is a follow-up for that owner. To act on it,${steer} bind a read command, or ask a question. Any other decision \u2014 a refusal, a preset, a write \u2014 folds the whole message into the owner unchanged and posts no answer.`;
40497
40858
  }
40498
40859
  function operatorProjection(input) {
40499
40860
  return {
@@ -40878,14 +41239,13 @@ function runnableProposal(proposal, ctx) {
40878
41239
  return (ctx.repositories?.length ?? 0) > 0 || /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+|(?:^|\s)[\w.-]+\/[\w.-]+(?:\s|[:#]|$)/i.test(request2);
40879
41240
  }
40880
41241
  function ownedDecisionRuns(event, owner, commands) {
40881
- if (event.outcome !== "binds") return false;
41242
+ if (owner.kind === "pipeline" || event.outcome !== "binds") return false;
40882
41243
  return (event.binds ?? []).every((bind) => {
40883
41244
  const parsed = commands ? parseChatCommand(bind.line, commands) : null;
40884
41245
  if (parsed?.kind !== "invoke") return false;
40885
41246
  const def = commands.list().find((c) => c.id === parsed.id);
40886
41247
  if (!def) return false;
40887
- if (def.id === "steer.run")
40888
- return owner.kind !== "pipeline" && !(owner.kind === "live" && owner.runId === void 0);
41248
+ if (def.id === "steer.run") return !(owner.kind === "live" && owner.runId === void 0);
40889
41249
  return boundBlastRadius(def, parsed.input) === "read";
40890
41250
  });
40891
41251
  }
@@ -41208,6 +41568,7 @@ async function executeOperatorDecision(deps, ctx) {
41208
41568
  "verbose"
41209
41569
  );
41210
41570
  const answered = { kind: "answered" };
41571
+ if (ctx.owner?.kind === "pipeline") return { kind: "fold" };
41211
41572
  if (event.outcome === "question") {
41212
41573
  await io.reply(event.question ?? "");
41213
41574
  await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
@@ -41567,21 +41928,21 @@ function parseSkillMarkdown(raw) {
41567
41928
  if (!fm || typeof fm !== "object" || Array.isArray(fm)) {
41568
41929
  throw new Error("skill frontmatter is not a YAML mapping");
41569
41930
  }
41570
- const record2 = fm;
41571
- const name = record2.name;
41931
+ const record3 = fm;
41932
+ const name = record3.name;
41572
41933
  if (typeof name !== "string" || name.trim() === "") {
41573
41934
  throw new Error("skill frontmatter is missing a non-empty string `name`");
41574
41935
  }
41575
- const description = record2.description;
41936
+ const description = record3.description;
41576
41937
  if (typeof description !== "string" || description.trim() === "") {
41577
41938
  throw new Error(`skill "${name}" frontmatter is missing a non-empty string \`description\``);
41578
41939
  }
41579
- const agents = record2.agents;
41940
+ const agents = record3.agents;
41580
41941
  if (!Array.isArray(agents) || agents.length === 0 || !agents.every((a) => typeof a === "string" && a.trim() !== "")) {
41581
41942
  throw new Error(`skill "${name}" frontmatter needs a non-empty \`agents\` array of strings (e.g. [review])`);
41582
41943
  }
41583
- const source2 = typeof record2.source === "string" ? record2.source.trim() : void 0;
41584
- const upstream = parseUpstream(record2.upstream, name);
41944
+ const source2 = typeof record3.source === "string" ? record3.source.trim() : void 0;
41945
+ const upstream = parseUpstream(record3.upstream, name);
41585
41946
  return {
41586
41947
  name: name.trim(),
41587
41948
  description: description.trim(),
@@ -41867,6 +42228,47 @@ var init_customInstructions = __esm({
41867
42228
  }
41868
42229
  });
41869
42230
 
42231
+ // ../../src/core/runEventLane.ts
42232
+ var RunEventLane;
42233
+ var init_runEventLane = __esm({
42234
+ "../../src/core/runEventLane.ts"() {
42235
+ "use strict";
42236
+ RunEventLane = class {
42237
+ constructor(publishNow) {
42238
+ this.publishNow = publishNow;
42239
+ }
42240
+ publishNow;
42241
+ tail;
42242
+ /** Publish synchronously while idle; otherwise preserve order behind the active write. */
42243
+ publish(event) {
42244
+ if (!this.tail) {
42245
+ this.publishNow(event);
42246
+ return;
42247
+ }
42248
+ void this.write(() => this.publishNow(event));
42249
+ }
42250
+ /** Queue one operation after every event or operation already accepted. */
42251
+ write(work) {
42252
+ const previous = this.tail ?? Promise.resolve();
42253
+ const next = previous.then(work);
42254
+ const settled = next.then(
42255
+ () => void 0,
42256
+ () => void 0
42257
+ );
42258
+ this.tail = settled;
42259
+ void settled.then(() => {
42260
+ if (this.tail === settled) this.tail = void 0;
42261
+ });
42262
+ return next;
42263
+ }
42264
+ /** Wait until every operation accepted so far has settled. */
42265
+ async drain() {
42266
+ await this.tail;
42267
+ }
42268
+ };
42269
+ }
42270
+ });
42271
+
41870
42272
  // ../../src/core/statusCoalescer.ts
41871
42273
  function coalesceStatus(inner, minIntervalMs, now = Date.now, schedule = setTimeout) {
41872
42274
  let lastSentAt = -Infinity;
@@ -42039,22 +42441,24 @@ async function registerRun(deps, ctx) {
42039
42441
  {
42040
42442
  id: runId3,
42041
42443
  startedAt,
42042
- ...resume ? { replay: resume.events } : ctx.restartCarried ? { replay: ctx.restartCarried.events, token: ctx.restartCarried.token } : {}
42444
+ ...carriedRow?.liveState !== void 0 ? { liveState: { liveState: carriedRow.liveState, liveStateSeq: carriedRow.liveStateSeq } } : {},
42445
+ ...resume ? { replay: resume.events } : ctx.restartCarried ? { replay: ctx.restartCarried.events, token: ctx.restartCarried.token, resetLiveState: true } : {}
42043
42446
  }
42044
42447
  );
42448
+ const events = new RunEventLane((event) => registry.publish(run2.id, event));
42045
42449
  if (ctx.restartCarried)
42046
- registry.publish(run2.id, { type: "run_note", kind: "resumed", summary: ctx.restartCarried.note, at: clock4() });
42450
+ events.publish({ type: "run_note", kind: "resumed", summary: ctx.restartCarried.note, at: clock4() });
42047
42451
  const liveUrl = liveViewLink(run2.id, run2.token);
42048
42452
  shell.setLink(liveUrl ? { url: liveUrl, label: "Live run" } : void 0);
42049
42453
  if (liveUrl) admitted.runLink = liveUrl;
42050
42454
  io.runStarted?.({ id: run2.id });
42051
- trace.bindRun(run2.id, (e) => registry.publish(run2.id, e));
42455
+ trace.bindRun(run2.id, (event) => events.publish(event));
42052
42456
  const publishText = (type, text, source3, raw) => {
42053
42457
  const redacted = redactSecrets(text);
42054
42458
  const body = { text: redacted, ...source3 ? { source: source3 } : {}, at: clock4() };
42055
42459
  const event = type === "input" ? { type, messageId: messageIdOf(msg, run2.id), ...body } : { type, ...body };
42056
42460
  const withRaw = raw !== void 0 ? { ...event, raw: redactSecrets(raw) } : event;
42057
- registry.publish(run2.id, utf8ByteLength(JSON.stringify(withRaw)) <= MAX_EVENT_BYTES ? withRaw : event);
42461
+ events.publish(utf8ByteLength(JSON.stringify(withRaw)) <= MAX_EVENT_BYTES ? withRaw : event);
42058
42462
  console.log(`[event] ${msg.threadKey} type=${type} bytes=${utf8ByteLength(redacted)}`);
42059
42463
  };
42060
42464
  const humanize = isMrkdwnChannel(msg.channelId);
@@ -42074,7 +42478,7 @@ async function registerRun(deps, ctx) {
42074
42478
  if (!resume && ctx.references) {
42075
42479
  const { conversations, blocks } = ctx.references;
42076
42480
  conversations.forEach((rc, i) => {
42077
- registry.publish(run2.id, {
42481
+ events.publish({
42078
42482
  type: "reference",
42079
42483
  url: rc.permalink,
42080
42484
  channelId: rc.ref.channelId,
@@ -42086,7 +42490,7 @@ async function registerRun(deps, ctx) {
42086
42490
  console.log(`[event] ${msg.threadKey} type=reference channel=${rc.ref.channelId} messages=${rc.messages.length}`);
42087
42491
  });
42088
42492
  }
42089
- const publishMeta = (repoCtx2) => registry.publish(run2.id, {
42493
+ const publishMeta = (repoCtx2) => events.publish({
42090
42494
  type: "run_meta",
42091
42495
  agent: agent.name,
42092
42496
  agentSource,
@@ -42118,7 +42522,7 @@ async function registerRun(deps, ctx) {
42118
42522
  if (!resume)
42119
42523
  for (const d of cardDecisions ?? []) {
42120
42524
  if (d.outcome !== "degraded") continue;
42121
- registry.publish(run2.id, {
42525
+ events.publish({
42122
42526
  type: "run_note",
42123
42527
  kind: "control_degraded",
42124
42528
  summary: oneLine(
@@ -42132,12 +42536,12 @@ async function registerRun(deps, ctx) {
42132
42536
  at: clock4()
42133
42537
  });
42134
42538
  }
42135
- if (!resume && route2) registry.publish(run2.id, { type: "route", ...route2, at: clock4() });
42136
- if (!resume && ctx.operator) registry.publish(run2.id, { type: "operator", ...ctx.operator, at: clock4() });
42539
+ if (!resume && route2) events.publish({ type: "route", ...route2, at: clock4() });
42540
+ if (!resume && ctx.operator) events.publish({ type: "operator", ...ctx.operator, at: clock4() });
42137
42541
  if (!resume && deps.config.config.runHistory?.includeContext !== false) {
42138
42542
  for (const text of contextMessageTexts(seedTurns ?? history, humanize)) publishText("context", text);
42139
42543
  }
42140
- return { run: run2, runId: runId3, channelVisibility, liveUrl, publishText, publishMeta };
42544
+ return { run: run2, runId: runId3, channelVisibility, liveUrl, events, publishText, publishMeta };
42141
42545
  }
42142
42546
  async function reserveRun(deps, ctx) {
42143
42547
  const {
@@ -42213,7 +42617,7 @@ async function reserveRun(deps, ctx) {
42213
42617
  }
42214
42618
  async function attachRound(deps, ctx) {
42215
42619
  const { threadKey, agent, profile, repoCtx, root, clock: clock4, reattach, stopSignal, remainingMs, requester } = ctx;
42216
- const { onSetupNote } = ctx;
42620
+ const { onLiveStateObservation } = ctx;
42217
42621
  const ownPr = agent.name === "review" ? void 0 : ownPrOf(repoCtx);
42218
42622
  return root.span("dispatch.workspace.attach", async (span) => {
42219
42623
  const graft = (steps, residentTotalMs) => graftResidentSteps(steps, {
@@ -42249,7 +42653,7 @@ async function attachRound(deps, ctx) {
42249
42653
  ...stopSignal !== void 0 ? { stopSignal } : {},
42250
42654
  ...remainingMs !== void 0 ? { remainingMs } : {},
42251
42655
  ...requester !== void 0 ? { requester } : {},
42252
- ...onSetupNote !== void 0 ? { onSetupNote } : {}
42656
+ ...onLiveStateObservation !== void 0 ? { onLiveStateObservation } : {}
42253
42657
  },
42254
42658
  logKey: threadKey,
42255
42659
  span
@@ -42306,10 +42710,10 @@ async function attachWorkspace(deps, ctx) {
42306
42710
  clock: clock4,
42307
42711
  // The requester whose binding names the author pair (record 0062).
42308
42712
  requester: msg.userId,
42309
- // A run admitted onto a drained fleet says so (issue 2044): the drain
42310
- // wait paints its one line here, and its end restores the attach label
42311
- // the card sink painted at the span's start.
42312
- onSetupNote: (note) => shell.setSetupLabel(note ?? `${displayNameOf("dispatch.workspace.attach")}\u2026`),
42713
+ onLiveStateObservation: async (observation) => {
42714
+ await ctx.onLiveStateObservation?.(observation);
42715
+ shell.setSetupLabel(`${liveStateWords(observation.state)}\u2026`);
42716
+ },
42313
42717
  ...reattach !== void 0 ? { reattach } : {},
42314
42718
  ...stopSignal !== void 0 ? { stopSignal } : {},
42315
42719
  ...remainingMs !== void 0 ? { remainingMs } : {}
@@ -42476,7 +42880,6 @@ var init_provision = __esm({
42476
42880
  init_factory();
42477
42881
  init_executor();
42478
42882
  init_residentTrace();
42479
- init_displayNames();
42480
42883
  init_resident();
42481
42884
  init_memory();
42482
42885
  init_budgets();
@@ -42489,12 +42892,14 @@ var init_provision = __esm({
42489
42892
  init_reviewRound();
42490
42893
  init_repoContext();
42491
42894
  init_runEvents();
42895
+ init_runEventLane();
42492
42896
  init_redact();
42493
42897
  init_runRecord();
42494
42898
  init_inboxMessage();
42495
42899
  init_statusCardFrame();
42496
42900
  init_statusCoalescer();
42497
42901
  init_types5();
42902
+ init_decide();
42498
42903
  init_record();
42499
42904
  init_reply();
42500
42905
  init_refusal();
@@ -44750,9 +45155,13 @@ function mapStringLeaves(value, fn) {
44750
45155
  }
44751
45156
  return value;
44752
45157
  }
45158
+ function requesterText(text) {
45159
+ return text.trim().replace(/\s+/g, " ").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/[\\`*_{}[\]()#!|~]/g, "\\$&").replace(/@/g, "&#64;");
45160
+ }
44753
45161
  function requestedByLine(by) {
44754
- const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.join(", ")}` : "";
44755
- return `Requested by @${by.login} in ${by.surface}${steered}`;
45162
+ const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.map(requesterText).join(", ")}` : "";
45163
+ const thread = by.threadUrl ? ` \xB7 [Thread](${by.threadUrl})` : "";
45164
+ return `Requested by **${requesterText(by.name) || "Unknown requester"}**${thread}${steered}`;
44756
45165
  }
44757
45166
  function encodeGithubPathSegments(path) {
44758
45167
  return path.split("/").map(encodeURIComponent).join("/");
@@ -44775,6 +45184,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
44775
45184
  if (!SHA_RE2.test(ctx.headSha))
44776
45185
  throw new Error(`renderPrDescriptionMarkdown: headSha must be a full 40-char lowercase sha, got "${ctx.headSha}"`);
44777
45186
  const out = [];
45187
+ if (ctx.requestedBy) out.push(requestedByLine(ctx.requestedBy), "");
44778
45188
  out.push(desc.tldr, "");
44779
45189
  out.push(`${MAP_LABELS.why} ${desc.why}`, "");
44780
45190
  out.push(MAP_LABELS.whereToLook, "");
@@ -44801,10 +45211,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
44801
45211
  ...desc.validation.criteria.map((c) => `| ${cell2(c.criterion)} | ${cell2(c.proof)} |`)
44802
45212
  ])
44803
45213
  );
44804
- const agentLines = [
44805
- ...desc.agentNotes ? [desc.agentNotes] : [],
44806
- ...ctx.requestedBy ? [requestedByLine(ctx.requestedBy)] : []
44807
- ];
45214
+ const agentLines = desc.agentNotes ? [desc.agentNotes] : [];
44808
45215
  if (agentLines.length > 0) out.push(...details(FOLD_SUMMARIES.agents, agentLines));
44809
45216
  out.push(GENERATED_FOOTER, "");
44810
45217
  return out.join("\n");
@@ -44812,6 +45219,8 @@ function renderPrDescriptionMarkdown(desc, ctx) {
44812
45219
  function parsePrDescriptionMarkdown(body) {
44813
45220
  const problems = [];
44814
45221
  const lines = body.replace(/\r\n?/g, "\n").split("\n").filter((l) => l.trim() !== GENERATED_FOOTER);
45222
+ if (/^Requested by \*\*(?:\\.|[^*\\])+\*\*(?: · \[Thread\]\([^\s)]+\))?(?:; steered by .*)?$/.test(lines[0] ?? ""))
45223
+ lines.shift();
44815
45224
  const fenced = fenceMask(lines);
44816
45225
  const description = { pointers: [] };
44817
45226
  if (lines.some((l, i) => !fenced[i] && /^##\s+Tour\s*$/i.test(l))) {
@@ -46012,12 +46421,12 @@ var init_contract4 = __esm({
46012
46421
  };
46013
46422
  RELAUNCH_CEILING = 2;
46014
46423
  HarnessContainerReplacedError = class extends HarnessInterruptedError {
46015
- constructor(message4, said2, was, now, record2, condition = "word") {
46424
+ constructor(message4, said2, was, now, record3, condition = "word") {
46016
46425
  super(message4, "container replaced under the run", "container_replaced");
46017
46426
  this.said = said2;
46018
46427
  this.was = was;
46019
46428
  this.now = now;
46020
- this.record = record2;
46429
+ this.record = record3;
46021
46430
  this.condition = condition;
46022
46431
  this.name = "HarnessContainerReplacedError";
46023
46432
  if (condition !== "identity" !== (said2 !== void 0))
@@ -46104,11 +46513,11 @@ async function prepareRelaunch(deps, ctx) {
46104
46513
  } else {
46105
46514
  ctx.saveFacts(written);
46106
46515
  }
46107
- const { deadline, ...record2 } = replaced.record;
46516
+ const { deadline, ...record3 } = replaced.record;
46108
46517
  return {
46109
46518
  kind: "relaunch",
46110
46519
  resume: {
46111
- ...record2,
46520
+ ...record3,
46112
46521
  remainingMs: Math.max(0, deadline - ctx.clock()),
46113
46522
  facts: written,
46114
46523
  relaunch: {
@@ -47271,7 +47680,7 @@ var init_runBearers = __esm({
47271
47680
  * after `record` returned would carry the old hash ahead of the new one.
47272
47681
  * Refuses by name the runs `issue` mints nothing for, and hands `record`
47273
47682
  * nothing then. */
47274
- rotate(runId3, record2) {
47683
+ rotate(runId3, record3) {
47275
47684
  const entry = this.entries.get(runId3);
47276
47685
  if (!entry) return { ok: false, reason: "unknown_run" };
47277
47686
  if (entry.revoked) return { ok: false, reason: "revoked" };
@@ -47279,7 +47688,7 @@ var init_runBearers = __esm({
47279
47688
  const secret2 = randomBytes4(SECRET_BYTES);
47280
47689
  const hash = hashOf(secret2);
47281
47690
  entry.hashes.push(hash);
47282
- record2(hash.toString("hex"));
47691
+ record3(hash.toString("hex"));
47283
47692
  entry.hashes = [hash];
47284
47693
  return { ok: true, token: token(runId3, secret2), expiresAt: entry.grant.expiresAt };
47285
47694
  }
@@ -48715,6 +49124,11 @@ var init_mirror = __esm({
48715
49124
  seedEchoPending = false;
48716
49125
  mirroredTail;
48717
49126
  iteration = 0;
49127
+ /** The loop counters on the last transcript-bearing step. A cursor-only
49128
+ * checkpoint repeats them so a post-loop turn cannot become part of the
49129
+ * resumable model loop merely by consuming a control row. */
49130
+ lastTurn = 0;
49131
+ lastIteration = 0;
48718
49132
  /** The rows written since the mirror started, and the compaction entries
48719
49133
  * among them by position — kept whether or not the ledger is wired, so
48720
49134
  * the harness holds this generation's copy of the record it wrote
@@ -48824,6 +49238,8 @@ var init_mirror = __esm({
48824
49238
  inboxConsumedSeq: this.inboxConsumedSeq,
48825
49239
  ...this.inboxDeferred()
48826
49240
  };
49241
+ this.lastTurn = report2.turn;
49242
+ this.lastIteration = report2.iteration;
48827
49243
  this.idx += turns.length;
48828
49244
  this.lastAssistantIdx = this.idx - 1;
48829
49245
  this.rows.push(...turns);
@@ -48852,6 +49268,8 @@ var init_mirror = __esm({
48852
49268
  inboxConsumedSeq: this.inboxConsumedSeq,
48853
49269
  ...this.inboxDeferred()
48854
49270
  };
49271
+ this.lastTurn = report2.turn;
49272
+ this.lastIteration = report2.iteration;
48855
49273
  this.idx += turns.length + 1;
48856
49274
  this.rows.push(...turns);
48857
49275
  this.compactionRows.push({ before: this.rows.length, entry });
@@ -48859,6 +49277,22 @@ var init_mirror = __esm({
48859
49277
  await this.deps.onStep(report2);
48860
49278
  return true;
48861
49279
  }
49280
+ /** Persist only the inbox cursor and its deferred exceptions. Post-loop
49281
+ * turns deliberately stay out of the resumable transcript, but a control
49282
+ * row they consume must survive a bot restart just as a loop step does. */
49283
+ async checkpointInbox() {
49284
+ if (!this.deps.onStep) return;
49285
+ await this.deps.onStep({
49286
+ turns: [],
49287
+ firstIdx: this.idx,
49288
+ inFlight: [],
49289
+ turn: this.lastTurn,
49290
+ iteration: this.lastIteration,
49291
+ remainingMs: Math.max(0, this.deps.remainingMs()),
49292
+ inboxConsumedSeq: this.inboxConsumedSeq,
49293
+ ...this.inboxDeferred()
49294
+ });
49295
+ }
48862
49296
  };
48863
49297
  }
48864
49298
  });
@@ -49057,6 +49491,82 @@ var init_process = __esm({
49057
49491
  }
49058
49492
  });
49059
49493
 
49494
+ // ../../src/core/providerToolSchemas.ts
49495
+ function responsesRejectsPattern(pattern) {
49496
+ return /\(\?(?:[=!]|<[=!])/.test(pattern);
49497
+ }
49498
+ function shapeResponsesSchema(value, tool, degradations) {
49499
+ const schema = record(value);
49500
+ if (!schema) {
49501
+ if (!Array.isArray(value)) return { value, changed: false };
49502
+ let changed2 = false;
49503
+ const items = value.map((item) => {
49504
+ const shaped = shapeResponsesSchema(item, tool, degradations);
49505
+ changed2 ||= shaped.changed;
49506
+ return shaped.value;
49507
+ });
49508
+ return { value: changed2 ? items : value, changed: changed2 };
49509
+ }
49510
+ let changed = false;
49511
+ const out = {};
49512
+ for (const [keyword, item] of Object.entries(schema)) {
49513
+ if (keyword === "pattern" && typeof item === "string" && responsesRejectsPattern(item)) {
49514
+ degradations.push({
49515
+ tool,
49516
+ keyword,
49517
+ why: "the Responses wire does not accept regular-expression lookaround"
49518
+ });
49519
+ changed = true;
49520
+ continue;
49521
+ }
49522
+ if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
49523
+ let mapChanged = false;
49524
+ const mapped = {};
49525
+ for (const [name, child] of Object.entries(record(item))) {
49526
+ const shaped = shapeResponsesSchema(child, tool, degradations);
49527
+ mapChanged ||= shaped.changed;
49528
+ mapped[name] = shaped.value;
49529
+ }
49530
+ out[keyword] = mapChanged ? mapped : item;
49531
+ changed ||= mapChanged;
49532
+ continue;
49533
+ }
49534
+ if (["additionalProperties", "allOf", "anyOf", "if", "items", "not", "oneOf", "then", "else"].includes(keyword) && typeof item === "object" && item !== null) {
49535
+ const shaped = shapeResponsesSchema(item, tool, degradations);
49536
+ out[keyword] = shaped.value;
49537
+ changed ||= shaped.changed;
49538
+ continue;
49539
+ }
49540
+ out[keyword] = item;
49541
+ }
49542
+ return { value: changed ? out : value, changed };
49543
+ }
49544
+ function shapeToolSchemasForWire(shape, body) {
49545
+ if (shape !== "openai-responses" || !Array.isArray(body.tools)) return { body, degradations: [] };
49546
+ const degradations = [];
49547
+ let changed = false;
49548
+ const tools = body.tools.map((item) => {
49549
+ const tool = record(item);
49550
+ if (!tool) return item;
49551
+ const name = typeof tool.name === "string" ? tool.name : "unnamed";
49552
+ const shaped = shapeResponsesSchema(tool.parameters, name, degradations);
49553
+ if (!shaped.changed) return item;
49554
+ changed = true;
49555
+ return { ...tool, parameters: shaped.value };
49556
+ });
49557
+ const unique = degradations.filter(
49558
+ (candidate, index, all) => all.findIndex((other) => other.tool === candidate.tool && other.keyword === candidate.keyword) === index
49559
+ );
49560
+ return { body: changed ? { ...body, tools } : body, degradations: unique };
49561
+ }
49562
+ var record;
49563
+ var init_providerToolSchemas = __esm({
49564
+ "../../src/core/providerToolSchemas.ts"() {
49565
+ "use strict";
49566
+ record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
49567
+ }
49568
+ });
49569
+
49060
49570
  // ../../src/core/harness/piAi.ts
49061
49571
  import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
49062
49572
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
@@ -49224,6 +49734,7 @@ var init_piAi = __esm({
49224
49734
  "../../src/core/harness/piAi.ts"() {
49225
49735
  "use strict";
49226
49736
  init_provider();
49737
+ init_providerToolSchemas();
49227
49738
  init_secrets2();
49228
49739
  init_clock();
49229
49740
  ANTHROPIC_BASE_URL = "https://api.anthropic.com";
@@ -49311,7 +49822,10 @@ var init_piAi = __esm({
49311
49822
  ...this.fetchImpl ? { fetch: this.fetchImpl } : {}
49312
49823
  };
49313
49824
  try {
49314
- const message4 = await this.api$().stream(model, toPiContext(req, model, this.clock), options).result();
49825
+ const context = toPiContext(req, model, this.clock);
49826
+ const wire = this.api === "openai-completions" ? "openai-chat" : this.api;
49827
+ const shaped = shapeToolSchemasForWire(wire, context);
49828
+ const message4 = await this.api$().stream(model, shaped.body, options).result();
49315
49829
  return fromPiMessage(message4, this.name, req.tools);
49316
49830
  } catch (err2) {
49317
49831
  const failure2 = providerFailureOf(err2);
@@ -50388,6 +50902,7 @@ async function runPiHarnessOpen(deps, run2) {
50388
50902
  let parkSettled = false;
50389
50903
  let pendingReissue = [];
50390
50904
  let reissueInFlight;
50905
+ let reissueInFlightRows = [];
50391
50906
  let reissues = 0;
50392
50907
  let providerRetryReady = false;
50393
50908
  let providerRetryWait;
@@ -50400,20 +50915,21 @@ async function runPiHarnessOpen(deps, run2) {
50400
50915
  providerRetryWait = void 0;
50401
50916
  providerRetryGeneration += 1;
50402
50917
  pendingReissue = [];
50918
+ reissueInFlightRows = [];
50403
50919
  };
50404
50920
  const maybeReissue = () => {
50405
50921
  if (heldTurn === void 0 || !parkSettled || reissueInFlight !== void 0 || retryInFlight !== void 0)
50406
50922
  return;
50407
50923
  if (pendingReissue.length === 0) return;
50408
50924
  if (loopEnded || hardStopped || writeUp !== void 0 || finaleAborted) return;
50409
- const batch = pendingReissue.splice(0);
50925
+ reissueInFlightRows = pendingReissue.splice(0);
50410
50926
  reissues += 1;
50411
50927
  const id = reissues === 1 ? `${ids.prompt}:reissue` : `${ids.prompt}:reissue:${reissues}`;
50412
50928
  reissueInFlight = id;
50413
50929
  unechoed.push({
50414
50930
  message: REISSUE_PROMPT,
50415
- seq: Math.max(0, ...batch.map((i) => i.ledgerSeq ?? 0)),
50416
- inputs: batch
50931
+ seq: Math.max(0, ...reissueInFlightRows.map((i) => i.ledgerSeq ?? 0)),
50932
+ inputs: reissueInFlightRows
50417
50933
  });
50418
50934
  sends.send({ id, type: "prompt", message: REISSUE_PROMPT });
50419
50935
  };
@@ -50674,16 +51190,29 @@ ${stagedLine}` : prompt;
50674
51190
  save();
50675
51191
  }
50676
51192
  if (reissueInFlight !== void 0 && r.id === reissueInFlight) {
50677
- reissueInFlight = void 0;
50678
51193
  if (r.success === false) {
51194
+ reissueInFlight = void 0;
50679
51195
  parkSettled = false;
50680
51196
  const at = unechoed.findIndex((u) => u.message === REISSUE_PROMPT);
50681
- if (at >= 0) pendingReissue.push(...unechoed.splice(at, 1)[0].inputs);
51197
+ if (at >= 0) unechoed.splice(at, 1);
51198
+ pendingReissue.push(...reissueInFlightRows);
51199
+ reissueInFlightRows = [];
50682
51200
  note(
50683
51201
  "harness_error",
50684
51202
  `the reissue prompt was refused (${String(r.error ?? "no reason")}); the turn stays held for the next settle`
50685
51203
  );
50686
51204
  } else {
51205
+ drainFollowUps2();
51206
+ await steers;
51207
+ const at = unechoed.findIndex((u) => u.message === REISSUE_PROMPT);
51208
+ if (at >= 0) unechoed.splice(at, 1);
51209
+ mirror.inboxConsumedSeq = Math.max(
51210
+ mirror.inboxConsumedSeq,
51211
+ ...reissueInFlightRows.map((item) => item.ledgerSeq ?? 0),
51212
+ ...pendingReissue.map((item) => item.ledgerSeq ?? 0)
51213
+ );
51214
+ await mirror.checkpointInbox();
51215
+ reissueInFlight = void 0;
50687
51216
  clearProviderHold();
50688
51217
  }
50689
51218
  }
@@ -50692,7 +51221,16 @@ ${stagedLine}` : prompt;
50692
51221
  if (r.success === false) {
50693
51222
  parkSettled = false;
50694
51223
  note("harness_error", "the model retry prompt was refused; the transport failure remains held");
50695
- } else clearProviderHold();
51224
+ } else {
51225
+ drainFollowUps2();
51226
+ await steers;
51227
+ mirror.inboxConsumedSeq = Math.max(
51228
+ mirror.inboxConsumedSeq,
51229
+ ...pendingReissue.map((item) => item.ledgerSeq ?? 0)
51230
+ );
51231
+ await mirror.checkpointInbox();
51232
+ clearProviderHold();
51233
+ }
50696
51234
  }
50697
51235
  if (r.id === ids.prompt) {
50698
51236
  catchingUp = false;
@@ -50926,8 +51464,8 @@ ${stagedLine}` : prompt;
50926
51464
  return;
50927
51465
  }
50928
51466
  const reissued = inputs.filter((item) => item.userId === PLANE_ACTOR_ID && isReissueSteerText(item.text));
50929
- const waiting2 = reissued.length === 0 ? inputs : inputs.filter((item) => !reissued.includes(item));
50930
- if (waiting2.length > 0) run2.inbox?.requeue(waiting2);
51467
+ const waiting3 = reissued.length === 0 ? inputs : inputs.filter((item) => !reissued.includes(item));
51468
+ if (waiting3.length > 0) run2.inbox?.requeue(waiting3);
50931
51469
  for (const item of reissued) {
50932
51470
  emit({
50933
51471
  type: "input",
@@ -51049,10 +51587,13 @@ ${stagedLine}` : prompt;
51049
51587
  `the follow-up turn's reissue prompt was refused (${String(obs.response.error ?? "no reason")}); the turn stays held for the next settle`
51050
51588
  );
51051
51589
  } else {
51590
+ drainTurnReissues();
51052
51591
  mirror.inboxConsumedSeq = Math.max(
51053
51592
  mirror.inboxConsumedSeq,
51054
- ...turnReissueInFlightRows.map((item) => item.ledgerSeq ?? 0)
51593
+ ...turnReissueInFlightRows.map((item) => item.ledgerSeq ?? 0),
51594
+ ...turnReissueRows.map((item) => item.ledgerSeq ?? 0)
51055
51595
  );
51596
+ await mirror.checkpointInbox();
51056
51597
  clearTurnProviderHold();
51057
51598
  }
51058
51599
  }
@@ -51067,6 +51608,7 @@ ${stagedLine}` : prompt;
51067
51608
  mirror.inboxConsumedSeq,
51068
51609
  ...turnReissueRows.map((item) => item.ledgerSeq ?? 0)
51069
51610
  );
51611
+ await mirror.checkpointInbox();
51070
51612
  clearTurnProviderHold();
51071
51613
  }
51072
51614
  }
@@ -51234,13 +51776,13 @@ var init_harness = __esm({
51234
51776
  }
51235
51777
  };
51236
51778
  PiContainerReplacedError = class extends HarnessContainerReplacedError {
51237
- constructor(said2, was, now, record2, condition = "word") {
51779
+ constructor(said2, was, now, record3, condition = "word") {
51238
51780
  super(
51239
51781
  condition === "transport" ? `the container running pi stopped answering (${was ?? "unknown"}; ${replacedBecause(condition, said2)})` : `the container running pi was replaced (${was ?? "unknown"} \u2192 ${now ?? "unknown"}; ${replacedBecause(condition, said2)})`,
51240
51782
  said2,
51241
51783
  was,
51242
51784
  now,
51243
- record2,
51785
+ record3,
51244
51786
  condition
51245
51787
  );
51246
51788
  this.name = "PiContainerReplacedError";
@@ -51451,14 +51993,17 @@ var init_resume = __esm({
51451
51993
 
51452
51994
  // ../../src/execution/githubComments.ts
51453
51995
  async function postReviewComment(target, body) {
51996
+ const codePoints = [...body].length;
51997
+ if (codePoints > MAX_REVIEW_POST_CODE_POINTS) {
51998
+ throw new Error(
51999
+ `PR review body is ${codePoints} Unicode code points, over the ${MAX_REVIEW_POST_CODE_POINTS} limit; refusing to clip the structured verdict`
52000
+ );
52001
+ }
51454
52002
  const token2 = await resolveGithubToken();
51455
52003
  if (!token2) {
51456
52004
  throw new Error("no GitHub credential available to post the PR review comment");
51457
52005
  }
51458
- const clipped = body.length > MAX_COMMENT_CHARS ? `${body.slice(0, MAX_COMMENT_CHARS)}
51459
-
51460
- _(review truncated to fit GitHub's comment size limit)_` : body;
51461
- const payload2 = { event: "COMMENT", body: clipped };
52006
+ const payload2 = { event: "COMMENT", body };
51462
52007
  if (target.commitId) payload2.commit_id = target.commitId;
51463
52008
  const res = await fetch(`https://api.github.com/repos/${target.repo}/pulls/${target.number}/reviews`, {
51464
52009
  method: "POST",
@@ -51476,13 +52021,12 @@ _(review truncated to fit GitHub's comment size limit)_` : body;
51476
52021
  throw new Error(`PR comment post failed: HTTP ${res.status} ${redactAndCap(text, 300)}`);
51477
52022
  }
51478
52023
  }
51479
- var MAX_COMMENT_CHARS;
51480
52024
  var init_githubComments = __esm({
51481
52025
  "../../src/execution/githubComments.ts"() {
51482
52026
  "use strict";
51483
52027
  init_githubApp();
51484
52028
  init_redact();
51485
- MAX_COMMENT_CHARS = 65e3;
52029
+ init_reviewVerdict();
51486
52030
  }
51487
52031
  });
51488
52032
 
@@ -51534,13 +52078,13 @@ function parsedPrDescriptionArtifact(facts2, ctx) {
51534
52078
  async function findSubmittedPrDescription(store, key, limit = SUBMITTED_LOOKUP_LIMIT) {
51535
52079
  const rows = await store.list({ limit, visibleTo: { kind: "repos-in", repos: [key.repo] } });
51536
52080
  for (const row2 of rows) {
51537
- const record2 = await store.get(row2.id);
51538
- if (!record2) continue;
51539
- for (let i = record2.events.length - 1; i >= 0; i--) {
51540
- const ev = record2.events[i];
52081
+ const record3 = await store.get(row2.id);
52082
+ if (!record3) continue;
52083
+ for (let i = record3.events.length - 1; i >= 0; i--) {
52084
+ const ev = record3.events[i];
51541
52085
  if (ev.type === "review_artifact" && ev.artifact === "pr_description" && ev.origin === "submitted" && ev.repo === key.repo && ev.pr === key.pr && ev.headSha === key.headSha) {
51542
52086
  const { type: _type, seq: _seq, at: _at, ...artifact } = ev;
51543
- return { artifact: { ...artifact, fromRunId: artifact.fromRunId ?? record2.id }, runId: record2.id };
52087
+ return { artifact: { ...artifact, fromRunId: artifact.fromRunId ?? record3.id }, runId: record3.id };
51544
52088
  }
51545
52089
  }
51546
52090
  }
@@ -51803,7 +52347,7 @@ async function runCodingPrPostStep(input) {
51803
52347
  });
51804
52348
  }
51805
52349
  const ownPr = target.ownPr;
51806
- const requestedBy = input.identity?.requestedBy;
52350
+ const requestedBy = input.requestedBy;
51807
52351
  const onOwnPrBranch = ownPr?.headBranch !== void 0 && branch2 === ownPr.headBranch;
51808
52352
  const pushedPastOwnPr = ownPr !== void 0 && onOwnPrBranch && pushed && headSha !== void 0 && !sameCommit(headSha, ownPr.headSha) ? headSha : void 0;
51809
52353
  if (prDescription && ownPr !== void 0 && ownPr.state !== "open" && pushedPastOwnPr !== void 0 && branch2 !== void 0) {
@@ -52014,16 +52558,17 @@ async function runCodingPrPostStep(input) {
52014
52558
  );
52015
52559
  }
52016
52560
  }
52017
- if (requestedBy !== void 0) {
52018
- const assignable = await input.identity.isAssignable(repo, requestedBy.login).catch(() => void 0);
52561
+ const requestedLogin = input.identity.requestedLogin;
52562
+ if (requestedLogin !== void 0) {
52563
+ const assignable = await input.identity.isAssignable(repo, requestedLogin).catch(() => void 0);
52019
52564
  if (assignable === true)
52020
- await input.identity.addAssignee(repo, opened.number, requestedBy.login).catch(
52565
+ await input.identity.addAssignee(repo, opened.number, requestedLogin).catch(
52021
52566
  (err2) => console.error(
52022
- `[pr-post] ${logKey} assignee add failed for ${requestedBy.login} on ${repo}#${opened.number}: ${err2 instanceof Error ? err2.message : String(err2)}`
52567
+ `[pr-post] ${logKey} assignee add failed for ${requestedLogin} on ${repo}#${opened.number}: ${err2 instanceof Error ? err2.message : String(err2)}`
52023
52568
  )
52024
52569
  );
52025
52570
  else if (assignable === false)
52026
- console.log(`[identity] ${logKey} ${requestedBy.login} is not assignable on ${repo}; skipped`);
52571
+ console.log(`[identity] ${logKey} ${requestedLogin} is not assignable on ${repo}; skipped`);
52027
52572
  }
52028
52573
  }
52029
52574
  console.log(
@@ -52399,6 +52944,7 @@ async function runLoop(deps, ctx) {
52399
52944
  coordinator,
52400
52945
  seed
52401
52946
  } = ctx;
52947
+ const events = ctx.events ?? new RunEventLane((event) => registry.publish(run2.id, event));
52402
52948
  const agent = budgetedAgent(ctx.agent, profile);
52403
52949
  const { round: round2 } = ctx;
52404
52950
  let { executor } = round2.selection;
@@ -52472,13 +53018,11 @@ async function runLoop(deps, ctx) {
52472
53018
  const pushes = trackPushedBranch(typeof restored.pushedBranch === "string" ? restored.pushedBranch : void 0);
52473
53019
  let budgetEnded = false;
52474
53020
  let recordedPushedBranch;
52475
- const onEvent = (e) => {
53021
+ let modelBudgetEndsAt;
53022
+ const publishEvent = (e, startsRunBudget) => {
52476
53023
  registry.publish(run2.id, e);
52477
53024
  if (isSpanRecord(e)) return;
52478
- if (e.type === "lease") {
52479
- run2.control.startLease(() => e.endsAt - clock4());
52480
- return;
52481
- }
53025
+ if (startsRunBudget) return;
52482
53026
  if (e.type === "tool_call") toolCalls++;
52483
53027
  if (e.type === "run_note" && e.kind === "time_budget_exhausted") budgetEnded = true;
52484
53028
  if (isCodingPrRun) {
@@ -52498,6 +53042,47 @@ async function runLoop(deps, ctx) {
52498
53042
  lastActivity = bookkeeping && !showBookkeeping ? void 0 : cardActivity(e);
52499
53043
  card.update(currentFrame());
52500
53044
  };
53045
+ const onEvent = (e) => {
53046
+ const startsRunBudget = e.type === "lease";
53047
+ if (startsRunBudget) {
53048
+ modelBudgetEndsAt = e.endsAt;
53049
+ run2.control.startLease(() => e.endsAt - clock4());
53050
+ }
53051
+ void events.write(async () => {
53052
+ if ((e.type === "tool_call" || e.type === "tool_result") && typeof registry.commitLiveState === "function") {
53053
+ const summary = registry.getById(run2.id);
53054
+ if (summary?.liveState?.state === "working") {
53055
+ const at = e.at ?? clock4();
53056
+ const bound = e.type === "tool_call" && e.boundMs !== void 0 ? at + e.boundMs : modelBudgetEndsAt ?? summary.liveState.bound;
53057
+ if (bound !== void 0) {
53058
+ const sourceSeq = summary.eventCount + 1;
53059
+ const assignment = {
53060
+ expectedSeq: summary.liveStateSeq ?? 0,
53061
+ at,
53062
+ state: "working",
53063
+ bound,
53064
+ detail: e.type === "tool_call" ? "running a tool" : "model turn",
53065
+ sourceEvents: [{ ...e, seq: sourceSeq }]
53066
+ };
53067
+ try {
53068
+ if (ledgerRun?.tracked()) {
53069
+ const committed = await ledgerRun.assignLiveState(assignment);
53070
+ if (committed.ok) registry.commitLiveState(run2.id, committed);
53071
+ } else {
53072
+ const local = assignRunLiveState(summary.liveState, summary.liveStateSeq ?? 0, assignment);
53073
+ if (local.ok) registry.commitLiveState(run2.id, { ...local, liveStateSeq: sourceSeq });
53074
+ }
53075
+ } catch (err2) {
53076
+ console.warn(
53077
+ `[run] ${msg.threadKey} live-state projection failed: ${err2 instanceof Error ? err2.message : String(err2)}`
53078
+ );
53079
+ }
53080
+ }
53081
+ }
53082
+ }
53083
+ publishEvent(e, startsRunBudget);
53084
+ });
53085
+ };
52501
53086
  for (const s of mcpForRun?.servers ?? []) {
52502
53087
  if (s.unavailable !== void 0)
52503
53088
  onEvent({
@@ -52520,14 +53105,14 @@ async function runLoop(deps, ctx) {
52520
53105
  descriptionArtifact = startReviewDescription({
52521
53106
  store: deps.runStore,
52522
53107
  repoCtx,
52523
- publish: (e) => registry.publish(run2.id, e)
53108
+ publish: (event) => events.publish(event)
52524
53109
  });
52525
53110
  readingDiffBaseline = startReviewReadingDiff({
52526
53111
  executor,
52527
53112
  cfg: deps.config.config.review?.readingDiff,
52528
53113
  env: publicEnv(),
52529
53114
  baseRef: repoCtx.baseRef,
52530
- publish: (e) => registry.publish(run2.id, e),
53115
+ publish: (event) => events.publish(event),
52531
53116
  parent: root
52532
53117
  }).baseline.then((published) => {
52533
53118
  console.log(`[reading-diff] ${msg.threadKey} baseline ${published ? "published" : "none"}`);
@@ -52599,10 +53184,11 @@ async function runLoop(deps, ctx) {
52599
53184
  if (interrupted === void 0) runFailed = true;
52600
53185
  throw err2;
52601
53186
  } finally {
53187
+ await events.drain();
52602
53188
  const calls = callsInFlight(recordEvents(), statusNow());
52603
53189
  commandInFlight = calls.length > 0;
52604
53190
  if (commandInFlight)
52605
- registry.publish(run2.id, {
53191
+ events.publish({
52606
53192
  type: "run_note",
52607
53193
  kind: "workspace_torn_down",
52608
53194
  summary: redactAndCap(
@@ -52676,12 +53262,12 @@ async function runLoop(deps, ctx) {
52676
53262
  let reviewStoppedBeforeStart = false;
52677
53263
  const latchReviewStoppedBeforeStart = () => {
52678
53264
  if (reviewStoppedBeforeStart || agent.name !== "review") return reviewStoppedBeforeStart;
52679
- const events = recordEvents();
52680
- const stopAt = events.findIndex(
53265
+ const events2 = recordEvents();
53266
+ const stopAt = events2.findIndex(
52681
53267
  (event) => event.type === "run_note" && (event.kind === "stop_requested" || event.kind === "stopped")
52682
53268
  );
52683
53269
  if (stopAt < 0) return false;
52684
- reviewStoppedBeforeStart = !events.slice(0, stopAt).some((event) => event.type === "tool_call" && event.tool !== "update_status");
53270
+ reviewStoppedBeforeStart = !events2.slice(0, stopAt).some((event) => event.type === "tool_call" && event.tool !== "update_status");
52685
53271
  return reviewStoppedBeforeStart;
52686
53272
  };
52687
53273
  const clearStoppedReviewTail = () => {
@@ -52696,8 +53282,8 @@ async function runLoop(deps, ctx) {
52696
53282
  };
52697
53283
  const tailSkipped = () => run2.control.requested === "hard" || relaunchEndedRun || latchReviewStoppedBeforeStart();
52698
53284
  const releaseWorkspace = (span) => {
52699
- const events = recordEvents();
52700
- const pushed = pushedBranchesOf(events);
53285
+ const events2 = recordEvents();
53286
+ const pushed = pushedBranchesOf(events2);
52701
53287
  return round2.release({
52702
53288
  hardStopped: run2.control.requested === "hard",
52703
53289
  commandInFlight,
@@ -52715,7 +53301,7 @@ async function runLoop(deps, ctx) {
52715
53301
  const staged = await stageIntoWorkspace(files, {
52716
53302
  store: deps.artifacts,
52717
53303
  threadKey: msg.threadKey,
52718
- publish: (e) => registry.publish(run2.id, e),
53304
+ publish: (event) => events.publish(event),
52719
53305
  nextIndex: nextStagedIndex,
52720
53306
  executor,
52721
53307
  resident: round2.selection.resident !== void 0
@@ -52793,9 +53379,9 @@ async function runLoop(deps, ctx) {
52793
53379
  throw new Error(
52794
53380
  `the ${agent.name} preset's run left ${facts2.harness} harness facts on its row, but this process has no harness roster to end that process with`
52795
53381
  );
52796
- const judge4 = harnessOf(deps.harness);
53382
+ const judge5 = harnessOf(deps.harness);
52797
53383
  const container = deps.harness.containerFor?.(executor, profile.machine) ?? harnessContainerFor(executor, profile.machine);
52798
- const found = await judge4.find(facts2, container).catch((err2) => {
53384
+ const found = await judge5.find(facts2, container).catch((err2) => {
52799
53385
  if (!isContainerGone(err2)) throw err2;
52800
53386
  return "gone";
52801
53387
  });
@@ -52820,11 +53406,11 @@ async function runLoop(deps, ctx) {
52820
53406
  onEvent({
52821
53407
  type: "run_note",
52822
53408
  kind: "resumed",
52823
- summary: `the run's row carries ${facts2.harness} harness facts (pid ${facts2.pid}) that the ${judge4.name} harness does not own: that process was neither judged nor ended here`
53409
+ summary: `the run's row carries ${facts2.harness} harness facts (pid ${facts2.pid}) that the ${judge5.name} harness does not own: that process was neither judged nor ended here`
52824
53410
  });
52825
53411
  break;
52826
53412
  default:
52827
- await judge4.end(facts2, container);
53413
+ await judge5.end(facts2, container);
52828
53414
  }
52829
53415
  } else noteUnknownWord("the run finished on the answer it already had");
52830
53416
  windDownEnding = windDownEndingUnder(finish2.answer, loopEnding);
@@ -53161,7 +53747,7 @@ async function runLoop(deps, ctx) {
53161
53747
  return { workspace, description: prDescription !== void 0 ? "submitted" : "not_submitted" };
53162
53748
  };
53163
53749
  if (leftBehind) {
53164
- registry.publish(run2.id, {
53750
+ events.publish({
53165
53751
  type: "run_note",
53166
53752
  kind: "work_left_behind",
53167
53753
  summary: oneLine(workLeftBehindSummary(leftBehind)),
@@ -53170,14 +53756,16 @@ async function runLoop(deps, ctx) {
53170
53756
  shell.note("quiet", workLeftBehindLabel(leftBehind));
53171
53757
  }
53172
53758
  if (prDescription) {
53173
- registry.publish(run2.id, {
53759
+ events.publish({
53174
53760
  type: "pr_description",
53175
53761
  description: redactPrDescription(prDescription),
53176
53762
  at: clock4()
53177
53763
  });
53178
53764
  }
53179
53765
  if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted) {
53180
- const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(msg.userId).catch(() => void 0) : void 0;
53766
+ const instance = coordinator && deps.coordinatorInstances ? await deps.coordinatorInstances.get(coordinator.parentInstanceId).catch(() => void 0) : void 0;
53767
+ const requester = instance?.userId === msg.userId ? instance : msg;
53768
+ const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(requester.userId).catch(() => void 0) : void 0;
53181
53769
  prNote = await root.span(
53182
53770
  "run.pr_post_step",
53183
53771
  () => runCodingPrPostStep({
@@ -53190,6 +53778,10 @@ async function runLoop(deps, ctx) {
53190
53778
  remoteRepo: observedRemoteRepo
53191
53779
  },
53192
53780
  description: prDescription,
53781
+ requestedBy: {
53782
+ name: requester.userName?.trim() || requestedLogin || requester.userId,
53783
+ threadUrl: threadPageLink(requester.threadKey)
53784
+ },
53193
53785
  target: prTarget,
53194
53786
  openPullRequest: deps.openPullRequest ?? openPullRequest,
53195
53787
  findOpenPr: deps.findOpenPrByHead ?? findOpenPrByHead,
@@ -53202,11 +53794,11 @@ async function runLoop(deps, ctx) {
53202
53794
  pullRequestHead: identitySeam.pullRequestHead,
53203
53795
  isAssignable: identitySeam.isAssignable,
53204
53796
  addAssignee: identitySeam.addAssignee,
53205
- ...requestedLogin !== void 0 ? { requestedBy: { login: requestedLogin, surface: msg.channelId } } : {}
53797
+ ...requestedLogin !== void 0 ? { requestedLogin } : {}
53206
53798
  }
53207
53799
  } : {},
53208
53800
  descriptionTurnRan,
53209
- publish: (e) => registry.publish(run2.id, e),
53801
+ publish: (event) => events.publish(event),
53210
53802
  logKey: msg.threadKey
53211
53803
  })
53212
53804
  );
@@ -53244,7 +53836,7 @@ async function runLoop(deps, ctx) {
53244
53836
  fetchPrHead: deps.fetchPrHead ?? currentPrHeadSha,
53245
53837
  reply: (text) => io.reply(text),
53246
53838
  ack: (text) => replyAck(io, resolved.verbosity, text),
53247
- publish: (e) => registry.publish(run2.id, e),
53839
+ publish: (event) => events.publish(event),
53248
53840
  logKey: msg.threadKey
53249
53841
  })
53250
53842
  );
@@ -53255,7 +53847,7 @@ async function runLoop(deps, ctx) {
53255
53847
  gateBypassed = err2 instanceof HarnessGateBypassedError;
53256
53848
  if (err2 instanceof ModelPolicyRefusedError) failure2 = { kind: "policy_refusal" };
53257
53849
  else if (err2 instanceof ModelTransientFailureError) failure2 = { kind: "provider_transient" };
53258
- registry.publish(run2.id, {
53850
+ events.publish({
53259
53851
  type: "run_note",
53260
53852
  kind: "run_failed",
53261
53853
  summary: redactAndCap(err2 instanceof Error ? err2.message : String(err2), ENDING_NOTE_MAX)
@@ -53264,7 +53856,7 @@ async function runLoop(deps, ctx) {
53264
53856
  await endHarness().catch((endErr) => {
53265
53857
  const detail = `the harness session's end failed after the loop's own error: ${endErr instanceof Error ? endErr.message : String(endErr)}`;
53266
53858
  console.warn(`[run] ${msg.threadKey} ${detail}`);
53267
- registry.publish(run2.id, {
53859
+ events.publish({
53268
53860
  type: "run_note",
53269
53861
  kind: "harness_error",
53270
53862
  summary: redactAndCap(detail, ENDING_NOTE_MAX),
@@ -53273,13 +53865,13 @@ async function runLoop(deps, ctx) {
53273
53865
  });
53274
53866
  await preserveCodingChildWork("ending").catch((salvageErr) => {
53275
53867
  const summary = `the interrupted-work checkpoint failed before teardown: ${salvageErr instanceof Error ? salvageErr.message : String(salvageErr)}`;
53276
- registry.publish(run2.id, { type: "run_note", kind: "work_salvage", summary, at: clock4() });
53868
+ events.publish({ type: "run_note", kind: "work_salvage", summary, at: clock4() });
53277
53869
  shell.note("quiet", summary);
53278
53870
  });
53279
53871
  await root.span("post.workspace_release", (span) => releaseWorkspace(span));
53280
53872
  if (!interrupted) throw err2;
53281
53873
  if (coordinator !== void 0)
53282
- registry.publish(run2.id, {
53874
+ events.publish({
53283
53875
  type: "child_interrupted",
53284
53876
  parentInstanceId: coordinator.parentInstanceId,
53285
53877
  reason: redactAndCap(interrupted.message, ENDING_NOTE_MAX),
@@ -53294,12 +53886,49 @@ async function runLoop(deps, ctx) {
53294
53886
  };
53295
53887
  } finally {
53296
53888
  clearInterval(heartbeat);
53889
+ await events.drain();
53297
53890
  const status3 = statusNow();
53891
+ if (typeof registry.commitLiveState === "function") {
53892
+ const commitBoundary = async (state, at2) => {
53893
+ const summary = registry.getById(run2.id);
53894
+ if (!summary) return;
53895
+ const eventSeq = summary.eventCount + 1;
53896
+ const assignment = state === "ended" ? {
53897
+ expectedSeq: summary.liveStateSeq ?? 0,
53898
+ eventSeq,
53899
+ at: at2,
53900
+ state,
53901
+ cause: causeOfClose(status3, interrupted !== void 0)
53902
+ } : {
53903
+ expectedSeq: summary.liveStateSeq ?? 0,
53904
+ eventSeq,
53905
+ at: at2,
53906
+ state,
53907
+ bound: summary.liveState?.bound ?? at2,
53908
+ detail: "writing the final answer"
53909
+ };
53910
+ if (ledgerRun?.tracked()) {
53911
+ const committed = await ledgerRun.assignLiveState(assignment);
53912
+ if (committed.ok) registry.commitLiveState(run2.id, committed);
53913
+ return;
53914
+ }
53915
+ const local = assignRunLiveState(summary.liveState, summary.liveStateSeq ?? 0, assignment);
53916
+ if (!local.ok || !local.event) return;
53917
+ registry.commitLiveState(run2.id, {
53918
+ ...local,
53919
+ event: { ...local.event, seq: eventSeq },
53920
+ liveStateSeq: eventSeq
53921
+ });
53922
+ };
53923
+ const at = clock4();
53924
+ await commitBoundary("wrapping_up", at);
53925
+ await commitBoundary("ended", clock4());
53926
+ }
53298
53927
  registry.finish(run2.id, status3);
53299
53928
  const snap = registry.snapshot(run2.id, run2.token);
53300
- const events = snap?.events ?? [];
53929
+ const recordEventList = snap?.events ?? [];
53301
53930
  const finishedAt = snap?.finishedAt ?? clock4();
53302
- const diagnosis = analyzeRunFriction(events, {
53931
+ const diagnosis = analyzeRunFriction(recordEventList, {
53303
53932
  finished: true,
53304
53933
  truncated: snap?.truncated ?? false,
53305
53934
  window: { start: snap?.receivedAt ?? startedAt, end: finishedAt }
@@ -53432,10 +54061,13 @@ var init_runLoop = __esm({
53432
54061
  init_readingDiff();
53433
54062
  init_reviewDescription();
53434
54063
  init_runEvents();
54064
+ init_runEventLane();
53435
54065
  init_redact();
53436
54066
  init_runFriction();
53437
54067
  init_llmOutput();
53438
54068
  init_runRecord();
54069
+ init_runLiveState();
54070
+ init_decide();
53439
54071
  init_statusCardLabel();
53440
54072
  init_runPace();
53441
54073
  init_statusCardFrame();
@@ -53799,7 +54431,7 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
53799
54431
  p.graph.units.filter((u) => selected.includes(u.id)).map(async (u) => {
53800
54432
  const previous = carried?.get(u.id);
53801
54433
  const taskKey = p.recordTasks?.[u.id];
53802
- const record2 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
54434
+ const record3 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
53803
54435
  return {
53804
54436
  instanceId,
53805
54437
  unit: u.id,
@@ -53810,7 +54442,7 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
53810
54442
  rounds: [],
53811
54443
  ...p.resume !== void 0 ? { resume: p.resume } : {},
53812
54444
  ...previous?.lastPush !== void 0 ? { lastPush: previous.lastPush } : {},
53813
- ...record2 !== void 0 ? { record: record2 } : {}
54445
+ ...record3 !== void 0 ? { record: record3 } : {}
53814
54446
  };
53815
54447
  })
53816
54448
  );
@@ -54226,7 +54858,11 @@ async function runShipBranch(deps, msg, io, ctx) {
54226
54858
  return { hostedLive: false };
54227
54859
  }
54228
54860
  const entry = pre.entry;
54229
- const caps = { ...resolveShipCaps(deps.config.config.ship), maxMinutes: profile.minutes };
54861
+ const configuredCaps = resolveShipCaps(deps.config.config.ship);
54862
+ const caps = {
54863
+ maxRounds: Math.min(configuredCaps.maxRounds, ctx.reissueCaps?.maxRounds ?? configuredCaps.maxRounds),
54864
+ maxMinutes: Math.min(profile.minutes, ctx.reissueCaps?.maxMinutes ?? profile.minutes)
54865
+ };
54230
54866
  const held2 = fit(caps);
54231
54867
  if (!held2.ok) {
54232
54868
  const reason2 = `budget ${caps.maxMinutes} min cannot hold the ship loop (${caps.maxRounds} review rounds need ${held2.need} min)`;
@@ -54387,6 +55023,34 @@ async function runShipBranch(deps, msg, io, ctx) {
54387
55023
  ...REPLAY_EVERYTHING
54388
55024
  });
54389
55025
  }
55026
+ const admittedAt = clock4();
55027
+ const admittedEventSeq = (registry.getById(run2.id)?.eventCount ?? 0) + 1;
55028
+ const admittedAssignment = {
55029
+ expectedSeq: 0,
55030
+ eventSeq: admittedEventSeq,
55031
+ at: admittedAt,
55032
+ state: "admitted",
55033
+ bound: admittedAt + minutesToMs(profile.minutes),
55034
+ detail: "waiting for the pipeline to start"
55035
+ };
55036
+ if (ledgerRun) {
55037
+ const committed = await ledgerRun.assignLiveState(admittedAssignment);
55038
+ if (!committed.ok || !registry.commitLiveState(run2.id, committed))
55039
+ throw new RefusalError(
55040
+ refusalOf(
55041
+ "setup_failed",
55042
+ `hosted live state start was not committed (${committed.ok ? "registry sequence" : committed.reason})`
55043
+ )
55044
+ );
55045
+ } else {
55046
+ const local = assignRunLiveState(void 0, 0, admittedAssignment);
55047
+ if (local.ok && local.event)
55048
+ registry.commitLiveState(run2.id, {
55049
+ ...local,
55050
+ event: { ...local.event, seq: admittedEventSeq },
55051
+ liveStateSeq: admittedEventSeq
55052
+ });
55053
+ }
54390
55054
  const hostRefused = opened.kind === "untracked" && opened.why === "thread-live";
54391
55055
  const liveUrl = liveViewLink(run2.id, run2.token);
54392
55056
  ctx.live.runId = run2.id;
@@ -54457,7 +55121,23 @@ async function runShipBranch(deps, msg, io, ctx) {
54457
55121
  instanceId: outcome.instanceId,
54458
55122
  until: clock4() + minutesToMs(caps.maxMinutes + HOSTED_DEADLINE_MARGIN_MINUTES)
54459
55123
  };
54460
- ledgerRun.setState({ hosting });
55124
+ const summary = registry.getById(run2.id);
55125
+ const committed = await ledgerRun.assignLiveState({
55126
+ expectedSeq: summary?.liveStateSeq ?? 0,
55127
+ eventSeq: (summary?.eventCount ?? 0) + 1,
55128
+ at: clock4(),
55129
+ state: "working",
55130
+ bound: hosting.until,
55131
+ detail: HOSTED_PIPELINE_STARTING_DETAIL,
55132
+ statePatch: { hosting }
55133
+ });
55134
+ if (!committed.ok || !registry.commitLiveState(run2.id, committed))
55135
+ throw new RefusalError(
55136
+ refusalOf(
55137
+ "setup_failed",
55138
+ `hosted live state transition was not committed (${committed.ok ? "registry sequence" : committed.reason})`
55139
+ )
55140
+ );
54461
55141
  registry.publish(run2.id, {
54462
55142
  type: "run_meta",
54463
55143
  agent: agent.name,
@@ -54565,6 +55245,8 @@ var init_ship = __esm({
54565
55245
  init_runFriction();
54566
55246
  init_run2();
54567
55247
  init_runRegistry();
55248
+ init_runLiveState();
55249
+ init_pipelineStanding();
54568
55250
  init_statusCardFrame();
54569
55251
  init_types5();
54570
55252
  init_refusal();
@@ -54891,41 +55573,41 @@ function prName(pr) {
54891
55573
  const m = GITHUB_PULL_URL.exec(pr.url);
54892
55574
  return m ? `${m[1]}#${pr.number} (${pr.url})` : `pull request ${pr.number} at ${pr.url}`;
54893
55575
  }
54894
- function runArtifacts(record2) {
55576
+ function runArtifacts(record3) {
54895
55577
  const lines = [];
54896
- if (record2.pr) {
54897
- const head = record2.pr.head ? `, head branch ${record2.pr.head}` : "";
54898
- lines.push(`- pull request opened or edited: ${prName(record2.pr)}${head}`);
55578
+ if (record3.pr) {
55579
+ const head = record3.pr.head ? `, head branch ${record3.pr.head}` : "";
55580
+ lines.push(`- pull request opened or edited: ${prName(record3.pr)}${head}`);
54899
55581
  }
54900
- if (record2.verdict) {
54901
- lines.push(`- verdict: ${record2.verdict.verdict} \u2014 ${record2.verdict.summary}`);
54902
- const findings = record2.verdict.findings ?? [];
55582
+ if (record3.verdict) {
55583
+ lines.push(`- verdict: ${record3.verdict.verdict} \u2014 ${record3.verdict.summary}`);
55584
+ const findings = record3.verdict.findings ?? [];
54903
55585
  lines.push(findings.length ? `- findings:
54904
55586
  ${indent(findings.map(formatFinding))}` : "- findings: none recorded");
54905
55587
  }
54906
- if (record2.dispositions) {
55588
+ if (record3.dispositions) {
54907
55589
  lines.push(
54908
- record2.dispositions.length ? `- dispositions:
54909
- ${indent(record2.dispositions.map(formatDisposition))}` : "- dispositions: none recorded"
55590
+ record3.dispositions.length ? `- dispositions:
55591
+ ${indent(record3.dispositions.map(formatDisposition))}` : "- dispositions: none recorded"
54910
55592
  );
54911
55593
  }
54912
- if (record2.handoff) {
54913
- const entries = handoffLines(record2.handoff);
55594
+ if (record3.handoff) {
55595
+ const entries = handoffLines(record3.handoff);
54914
55596
  lines.push(
54915
55597
  entries.length ? `- handoff:
54916
55598
  ${indent(entries)}` : "- handoff: nothing to hand off (three empty lists)"
54917
55599
  );
54918
55600
  }
54919
- const tldr = record2.pr ? tldrOf(record2.events) : void 0;
55601
+ const tldr = record3.pr ? tldrOf(record3.events) : void 0;
54920
55602
  if (tldr !== void 0) lines.push(`- description TL;DR: ${tldr}`);
54921
- if (record2.reviewPost) {
54922
- const post = record2.reviewPost;
55603
+ if (record3.reviewPost) {
55604
+ const post = record3.reviewPost;
54923
55605
  lines.push(
54924
55606
  post.posted ? `- review post: posted to ${post.target.repo}#${post.target.number} at ${post.head}${post.verdict ? ` (${post.verdict})` : ""}` : `- review post: not posted \u2014 ${post.reason}`
54925
55607
  );
54926
55608
  }
54927
55609
  if (lines.length === 0) return void 0;
54928
- return [`### run ${record2.id} (${record2.agent ?? "agent unknown"})`, ...lines].join("\n");
55610
+ return [`### run ${record3.id} (${record3.agent ?? "agent unknown"})`, ...lines].join("\n");
54929
55611
  }
54930
55612
  function threadArtifactsBlock(records, opts) {
54931
55613
  const maxRuns = opts.maxRuns ?? THREAD_ARTIFACTS_MAX_RUNS;
@@ -55186,6 +55868,23 @@ async function answerUnitOwnedThread(deps, ctx) {
55186
55868
  function activeRunCount() {
55187
55869
  return activeRuns;
55188
55870
  }
55871
+ function continuationBudgetOf(instance, unit) {
55872
+ if (unit.ending?.kind === "wall_clock_cap" || unit.ending?.kind === "review_pending")
55873
+ return { kind: "exhausted", budget: "wall-clock" };
55874
+ if (unit.ending?.kind === "round_cap") return { kind: "exhausted", budget: "review-round" };
55875
+ const caps = instance.caps;
55876
+ if (caps === void 0 || unit.startedAt === void 0 || unit.ending === void 0 || caps.maxMinutes <= 0 || caps.maxRounds <= 0 || unit.ending.at < unit.startedAt)
55877
+ return { kind: "unavailable" };
55878
+ const remainingMinutes = caps.maxMinutes - (unit.ending.at - unit.startedAt) / MINUTE_MS;
55879
+ if (remainingMinutes <= 0) return { kind: "exhausted", budget: "wall-clock" };
55880
+ const reviewRounds = unit.rounds.reduce(
55881
+ (highest, round2) => round2.agent === "review" ? Math.max(highest, round2.index) : highest,
55882
+ 0
55883
+ );
55884
+ const remainingRounds = caps.maxRounds - reviewRounds;
55885
+ if (remainingRounds <= 0) return { kind: "exhausted", budget: "review-round" };
55886
+ return { kind: "remaining", caps: { maxRounds: remainingRounds, maxMinutes: remainingMinutes } };
55887
+ }
55189
55888
  async function dispatch(deps, msg, io, opts = {}) {
55190
55889
  const ended = { status: "completed" };
55191
55890
  const resume = opts.resume;
@@ -55280,22 +55979,46 @@ async function dispatch(deps, msg, io, opts = {}) {
55280
55979
  try {
55281
55980
  const configuredOperator = !resume && !restart && !opts.parent && !opts.coordinator ? operatorModeOf(deps.config.config) : "off";
55282
55981
  const typedDecision = parseDirectives(msg.text).agent !== void 0 || deps.commands !== void 0 && parseChatCommand(msg.text, deps.commands) !== null;
55283
- const operatorMode = configuredOperator === "on" && typedDecision ? "off" : configuredOperator;
55982
+ let operatorMode = configuredOperator === "on" && typedDecision ? "off" : configuredOperator;
55284
55983
  let operatorEvent;
55285
55984
  let operatorPreset;
55286
55985
  let operatorRequest;
55287
55986
  let operatorModel;
55288
55987
  let operatorRepo;
55289
55988
  let reissuePlanId;
55989
+ let reissueCaps;
55290
55990
  let operatorThread;
55291
55991
  let pageOwner;
55292
55992
  if (operatorMode !== "off") {
55293
55993
  const runsService2 = deps.runs ?? createRunsService({ registry, store: deps.runStore });
55294
55994
  operatorThread = opts.thread ?? await readThread(runsService2, msg.threadKey);
55995
+ if (operatorThread !== void 0 && deps.coordinatorInstances !== void 0) {
55996
+ const unitReads = /* @__PURE__ */ new Map();
55997
+ const unitsOf = (id) => {
55998
+ let read = unitReads.get(id);
55999
+ if (read === void 0) {
56000
+ read = deps.coordinatorInstances.listUnits(id);
56001
+ unitReads.set(id, read);
56002
+ }
56003
+ return read;
56004
+ };
56005
+ pageOwner = await ownerOf2(operatorThread, unitsOf, msg.threadKey);
56006
+ if (operatorMode === "on" && parseDirectives(msg.text).agent === void 0) {
56007
+ const continuationOwner = pageOwner.kind === "pipeline" || pageOwner.kind === "pipeline_ambiguous" ? pageOwner : await ownerOf2(
56008
+ operatorThread.filter((run3) => run3.finished),
56009
+ unitsOf,
56010
+ msg.threadKey
56011
+ );
56012
+ if (continuationOwner.kind === "pipeline" || continuationOwner.kind === "pipeline_ambiguous") {
56013
+ pageOwner = continuationOwner;
56014
+ operatorMode = "off";
56015
+ }
56016
+ }
56017
+ }
56018
+ }
56019
+ if (operatorMode !== "off") {
55295
56020
  const slot = admission.get(msg.threadKey);
55296
56021
  const liveElsewhere = deps.threadsElsewhere.get(msg.threadKey) !== void 0;
55297
- if (slot === void 0 && !liveElsewhere && operatorThread !== void 0 && deps.coordinatorInstances !== void 0)
55298
- pageOwner = await ownerOf2(operatorThread, (id) => deps.coordinatorInstances.listUnits(id), msg.threadKey);
55299
56022
  const pendingQuestion = operatorMode === "on" ? pendingQuestionOf(operatorThread) : void 0;
55300
56023
  const joinedAnswer = pendingQuestion !== void 0 && !(pendingQuestion.proposal !== void 0 && isYesAnswer(msg.text)) ? joinedAnswerRequest(pendingQuestion, msg.text) : void 0;
55301
56024
  const doorMsg = joinedAnswer !== void 0 ? { ...msg, text: joinedAnswer } : msg;
@@ -55307,7 +56030,7 @@ async function dispatch(deps, msg, io, opts = {}) {
55307
56030
  // other, and the fold runs on to the seed refusal below, which
55308
56031
  // names the unit threads to reply in.
55309
56032
  pageOwner.run.instanceId !== void 0 ? { kind: "live" } : { kind: "live", runId: pageOwner.run.id }
55310
- ) : pageOwner?.kind === "unit" ? { kind: "unit", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline" ? { kind: "pipeline", unit: pageOwner.unit.unit } : void 0;
56033
+ ) : pageOwner?.kind === "unit" ? { kind: "unit", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline" ? { kind: "pipeline", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline_ambiguous" ? { kind: "pipeline", unit: pageOwner.units.map((unit) => unit.unit).join(", ") } : void 0;
55311
56034
  operatorEvent = await root.span(
55312
56035
  "dispatch.operator",
55313
56036
  () => operatorStage(deps, {
@@ -55411,20 +56134,120 @@ async function dispatch(deps, msg, io, opts = {}) {
55411
56134
  await recordPendingOperator();
55412
56135
  return ended;
55413
56136
  }
56137
+ if (owner.kind === "pipeline_ambiguous" && directives.agent === void 0) {
56138
+ await io.reply(
56139
+ `This thread matches multiple ended plan units (${owner.units.map((unit) => unit.unit).join(", ")}), so continuation is ambiguous. Nothing started.`
56140
+ );
56141
+ await recordPendingOperator();
56142
+ return ended;
56143
+ }
55414
56144
  if (owner.kind === "pipeline" && directives.agent === void 0) {
56145
+ const instance = await deps.coordinatorInstances.get(owner.instanceId).catch(() => null);
56146
+ if (instance === null) {
56147
+ await io.reply(
56148
+ `The ended pipeline's record \`${owner.instanceId}\` is unavailable, so continuation did not start.`
56149
+ );
56150
+ await recordPendingOperator();
56151
+ return ended;
56152
+ }
56153
+ const actor = chatActorOf(deps.config, msg);
56154
+ if (authorizeSteerOwner({
56155
+ caller: { ids: actorIdsOf(actor), grants: effectiveGrants(actor) },
56156
+ target: { runId: owner.run.id, requesterId: instance.userId }
56157
+ }).kind === "refused") {
56158
+ await io.reply(STEER_OWNER_REFUSED);
56159
+ await recordPendingOperator();
56160
+ return ended;
56161
+ }
56162
+ if (instance.threadKey !== msg.threadKey || instance.plan === void 0 || instance.plan.path !== void 0) {
56163
+ await io.reply(
56164
+ `The ended pipeline's durable generated-task identity does not match this thread, so continuation did not start.`
56165
+ );
56166
+ await recordPendingOperator();
56167
+ return ended;
56168
+ }
56169
+ if (instance.repo !== owner.run.repo || instance.branch !== owner.unit.branch) {
56170
+ await io.reply(
56171
+ `The ended pipeline's repository or branch identity no longer matches unit ${owner.unit.unit}, so continuation did not start.`
56172
+ );
56173
+ await recordPendingOperator();
56174
+ return ended;
56175
+ }
56176
+ const continuationBudget = continuationBudgetOf(instance, owner.unit);
56177
+ if (continuationBudget.kind === "unavailable") {
56178
+ await io.reply(
56179
+ `Unit ${owner.unit.unit} does not retain a complete durable wall-clock and review-round budget, so continuation did not start. Nothing else ran.`
56180
+ );
56181
+ await recordPendingOperator();
56182
+ return ended;
56183
+ }
56184
+ if (continuationBudget.kind === "exhausted") {
56185
+ await io.reply(
56186
+ `Unit ${owner.unit.unit} exhausted the ended pipeline's ${continuationBudget.budget} budget, so continuation did not start. Nothing else ran.`
56187
+ );
56188
+ await recordPendingOperator();
56189
+ return ended;
56190
+ }
55415
56191
  const original = await shipRequestOf(runsService, owner.run.id);
55416
56192
  if (original === void 0) {
55417
56193
  await io.reply(
55418
- "This ship pipeline ended, but its original task could not be read. What was the pipeline's original task?"
56194
+ `The ended pipeline's original task is unavailable from run \`${owner.run.id}\`, so continuation did not start.`
55419
56195
  );
55420
56196
  await recordPendingOperator();
55421
56197
  return ended;
55422
56198
  }
55423
- const instance = await deps.coordinatorInstances.get(owner.instanceId).catch(() => null);
55424
- if (instance?.plan?.path === void 0) reissuePlanId = instance?.plan?.id;
55425
- directives.text = original;
56199
+ const recordedPr = owner.unit.pr;
56200
+ if (recordedPr === void 0) {
56201
+ await io.reply(
56202
+ `Unit ${owner.unit.unit} does not retain a durable pull request identity, so continuation did not start. Nothing else ran.`
56203
+ );
56204
+ await recordPendingOperator();
56205
+ return ended;
56206
+ }
56207
+ const expectedHead = owner.unit.lastPush;
56208
+ if (expectedHead === void 0) {
56209
+ await io.reply(
56210
+ `Unit ${owner.unit.unit} does not retain the pull request's durable expected head, so continuation did not start. Nothing else ran.`
56211
+ );
56212
+ await recordPendingOperator();
56213
+ return ended;
56214
+ }
56215
+ const target = { repo: instance.repo, number: recordedPr.number };
56216
+ const facts2 = await (deps.fetchPrFacts ?? fetchPullRequestFacts)(target).catch(() => void 0);
56217
+ if (facts2 === void 0) {
56218
+ await io.reply(
56219
+ `GitHub did not return current facts for ${instance.repo}#${recordedPr.number}, so continuation did not start.`
56220
+ );
56221
+ await recordPendingOperator();
56222
+ return ended;
56223
+ }
56224
+ if (facts2.state !== "open") {
56225
+ await io.reply(
56226
+ `${instance.repo}#${recordedPr.number} is closed, so this ended pipeline has no open pull request to continue. Nothing started.`
56227
+ );
56228
+ await recordPendingOperator();
56229
+ return ended;
56230
+ }
56231
+ const verifiedHead = facts2.verifiedHead;
56232
+ if (!facts2.sameRepoHead || facts2.headBranchExists !== true || facts2.headRef !== owner.unit.branch || verifiedHead === void 0 || verifiedHead.repo.toLowerCase() !== instance.repo.toLowerCase() || verifiedHead.ref !== owner.unit.branch || facts2.headSha !== verifiedHead.sha) {
56233
+ await io.reply(
56234
+ `${instance.repo}#${recordedPr.number} no longer has the pipeline's verifiable \`${owner.unit.branch}\` head, so continuation did not start.`
56235
+ );
56236
+ await recordPendingOperator();
56237
+ return ended;
56238
+ }
56239
+ if (verifiedHead.sha !== expectedHead) {
56240
+ await io.reply(
56241
+ `${instance.repo}#${recordedPr.number} moved from the pipeline's expected head \`${expectedHead}\` to \`${verifiedHead.sha}\`, so continuation did not start. Nothing else ran.`
56242
+ );
56243
+ await recordPendingOperator();
56244
+ return ended;
56245
+ }
56246
+ reissuePlanId = instance.plan.id;
56247
+ reissueCaps = continuationBudget.caps;
56248
+ const durableRequest = parseDirectives(original);
56249
+ Object.assign(directives, durableRequest, { agent: void 0, budget: reissueCaps.maxMinutes });
55426
56250
  operatorPreset = "ship";
55427
- operatorRequest = original;
55428
56251
  settled = resolveCurrent();
55429
56252
  ({ sticky, resolved, agentSource } = settled);
55430
56253
  agentSource = "sticky";
@@ -55650,6 +56473,7 @@ async function dispatch(deps, msg, io, opts = {}) {
55650
56473
  card,
55651
56474
  directives,
55652
56475
  ...reissuePlanId !== void 0 ? { reissuePlanId } : {},
56476
+ ...reissueCaps !== void 0 ? { reissueCaps: { ...reissueCaps, maxMinutes: Math.min(reissueCaps.maxMinutes, profile.minutes) } } : {},
55653
56477
  sticky,
55654
56478
  history,
55655
56479
  repoCtx,
@@ -55744,12 +56568,12 @@ async function dispatch(deps, msg, io, opts = {}) {
55744
56568
  ...operatorEvent ? { operator: operatorEvent } : {},
55745
56569
  ...references.conversations.length > 0 ? { references } : {}
55746
56570
  });
55747
- const { run: run2, runId: runId3, channelVisibility, liveUrl, publishText, publishMeta } = registration;
56571
+ const { run: run2, runId: runId3, channelVisibility, liveUrl, events, publishText, publishMeta } = registration;
55748
56572
  registered = run2;
55749
56573
  for (const summary of seedNotes)
55750
- registry.publish(run2.id, { type: "run_note", kind: "seed", summary: oneLine(summary), at: clock4() });
56574
+ events.publish({ type: "run_note", kind: "seed", summary: oneLine(summary), at: clock4() });
55751
56575
  if (opts.redispatch)
55752
- registry.publish(run2.id, {
56576
+ events.publish({
55753
56577
  type: "run_note",
55754
56578
  kind: "redispatch",
55755
56579
  summary: `confirmed after question ${opts.redispatch.code}`,
@@ -55764,7 +56588,7 @@ async function dispatch(deps, msg, io, opts = {}) {
55764
56588
  threadKey: msg.threadKey,
55765
56589
  nextIndex: nextStagedIndex,
55766
56590
  preserveWorkspaceIndexes: true,
55767
- publish: (e) => registry.publish(runId3, e)
56591
+ publish: (event) => events.publish(event)
55768
56592
  }) : void 0;
55769
56593
  const threadAssets = !resume && deps.artifacts && thread && thread.length > 0 ? readThreadAssets({ runs: runsService, store: deps.artifacts }, msg.threadKey).then(
55770
56594
  (assets) => assets.filter((a) => a.runId !== runId3)
@@ -55775,7 +56599,7 @@ async function dispatch(deps, msg, io, opts = {}) {
55775
56599
  {
55776
56600
  nextIndex: nextStagedIndex,
55777
56601
  messageId: messageIdOf(msg, runId3),
55778
- publish: (e) => registry.publish(runId3, e)
56602
+ publish: (event) => events.publish(event)
55779
56603
  }
55780
56604
  )
55781
56605
  ) : void 0;
@@ -55807,7 +56631,7 @@ async function dispatch(deps, msg, io, opts = {}) {
55807
56631
  reserved = reservation.reserved;
55808
56632
  requestRow = reservation.requestRow;
55809
56633
  if (reservation.untrackedWhy !== void 0) {
55810
- registry.publish(runId3, {
56634
+ events.publish({
55811
56635
  type: "run_note",
55812
56636
  kind: "ledger_untracked",
55813
56637
  summary: redactAndCap(
@@ -55821,6 +56645,100 @@ async function dispatch(deps, msg, io, opts = {}) {
55821
56645
  shell.note("debug", "untracked by the ledger");
55822
56646
  }
55823
56647
  }
56648
+ if (typeof registry.commitLiveState === "function") {
56649
+ await events.write(async () => {
56650
+ const current = registry.getById(runId3);
56651
+ if (current && current.liveState === void 0) {
56652
+ const at = clock4();
56653
+ const eventSeq = current.eventCount + 1;
56654
+ const assignment = {
56655
+ expectedSeq: 0,
56656
+ eventSeq,
56657
+ at,
56658
+ state: "admitted",
56659
+ bound: resume ? at + resume.plan.remainingMs : Math.max(startedAt + minutesToMs(profile.minutes), at + minutesToMs(profile.minutes)),
56660
+ detail: "waiting to attach the workspace",
56661
+ // A reservation is not subscribed to the registry until promotion.
56662
+ // Commit the setup stream that already exists with admission, or
56663
+ // advancing its ledger cursor here would make promotion skip it.
56664
+ ...reserved ? {
56665
+ sourceEvents: (registry.snapshotById(runId3)?.events ?? []).map((event) => ({
56666
+ ...event,
56667
+ seq: event.seq
56668
+ }))
56669
+ } : {},
56670
+ ...(current.liveStateSeq ?? 0) > 0 ? { restart: true } : {}
56671
+ };
56672
+ const tracked = reserved ?? ledgerRun;
56673
+ if (tracked?.tracked()) {
56674
+ let committed = await tracked.assignLiveState(assignment);
56675
+ if (!committed.ok && committed.reason === "stale-sequence" && (current.liveStateSeq ?? 0) > 0)
56676
+ committed = await tracked.assignLiveState({
56677
+ ...assignment,
56678
+ expectedSeq: current.liveStateSeq ?? 0,
56679
+ sourceEvents: void 0,
56680
+ restart: true
56681
+ });
56682
+ if (!committed.ok || !registry.commitLiveState(runId3, committed))
56683
+ throw new Error(
56684
+ `live state admission was not committed (${committed.ok ? "registry sequence" : committed.reason})`
56685
+ );
56686
+ } else {
56687
+ const expectedSeq = current.liveStateSeq ?? 0;
56688
+ const local = assignRunLiveState(void 0, expectedSeq, { ...assignment, expectedSeq });
56689
+ if (!local.ok || !local.event) throw new Error("live state admission was invalid");
56690
+ registry.commitLiveState(runId3, {
56691
+ ...local,
56692
+ event: { ...local.event, seq: eventSeq },
56693
+ liveStateSeq: eventSeq
56694
+ });
56695
+ }
56696
+ }
56697
+ });
56698
+ }
56699
+ const assignLive = async (next, at, detail) => {
56700
+ if (typeof registry.commitLiveState !== "function") return true;
56701
+ let accepted = false;
56702
+ await events.write(async () => {
56703
+ const summary = registry.getById(runId3);
56704
+ if (!summary) return;
56705
+ const eventSeq = summary.eventCount + 1;
56706
+ const assignment = {
56707
+ expectedSeq: summary.liveStateSeq ?? 0,
56708
+ eventSeq,
56709
+ at,
56710
+ state: next.state,
56711
+ bound: next.bound,
56712
+ ...detail !== void 0 ? { detail } : {}
56713
+ };
56714
+ const tracked = ledgerRun ?? reserved;
56715
+ if (tracked) {
56716
+ const committed = await tracked.assignLiveState(assignment);
56717
+ accepted = committed.ok && (registry.commitLiveState?.(runId3, committed) ?? true);
56718
+ return;
56719
+ }
56720
+ const local = assignRunLiveState(summary.liveState, summary.liveStateSeq ?? 0, assignment);
56721
+ if (!local.ok) return;
56722
+ accepted = registry.commitLiveState?.(runId3, {
56723
+ ...local,
56724
+ ...local.event ? { event: { ...local.event, seq: eventSeq } } : {},
56725
+ liveStateSeq: local.event ? eventSeq : summary.liveStateSeq ?? 0
56726
+ }) ?? true;
56727
+ });
56728
+ return accepted;
56729
+ };
56730
+ let residentAttachAttempt = 0;
56731
+ const observeResidentLiveState = async (observation) => {
56732
+ if (observation.attempt < residentAttachAttempt) return;
56733
+ residentAttachAttempt = observation.attempt;
56734
+ const accepted = await assignLive(
56735
+ { state: observation.state, bound: observation.bound },
56736
+ clock4(),
56737
+ liveStateWords(observation.state)
56738
+ );
56739
+ if (!accepted) throw new Error("resident live-state observation was stale or could not be committed");
56740
+ shell.setSetupLabel(`${liveStateWords(observation.state)}\u2026`);
56741
+ };
55824
56742
  const reattach = resume ? carriedWorkspaceBinding(resume.row) : void 0;
55825
56743
  const control = registered?.control;
55826
56744
  const attach = await attachWorkspace(deps, {
@@ -55840,7 +56758,8 @@ async function dispatch(deps, msg, io, opts = {}) {
55840
56758
  // during the attach ends its wake wait at once, and once the harness
55841
56759
  // starts the lease every attach the executor opens is clipped to the
55842
56760
  // run's remaining clock (execution.md item 9).
55843
- ...control ? { stopSignal: control.hardSignal, remainingMs: () => control.remainingMs() } : {}
56761
+ ...control ? { stopSignal: control.hardSignal, remainingMs: () => control.remainingMs() } : {},
56762
+ onLiveStateObservation: observeResidentLiveState
55844
56763
  });
55845
56764
  if (attach.kind === "refused") return ended;
55846
56765
  if (attach.kind === "stopped") {
@@ -55892,6 +56811,17 @@ async function dispatch(deps, msg, io, opts = {}) {
55892
56811
  return ended;
55893
56812
  }
55894
56813
  const { round: round2 } = attach;
56814
+ if (typeof registry.commitLiveState === "function") {
56815
+ const summary = registry.getById(runId3);
56816
+ const bound2 = summary?.liveState?.bound ?? startedAt + minutesToMs(profile.minutes);
56817
+ if (summary?.liveState && (summary.liveState.state === "waiting_deploy" || summary.liveState.state === "waiting_repository") && round2.selection.backend !== "resident") {
56818
+ if (!await assignLive({ state: "falling_back", bound: bound2 }, clock4(), "switching to a fallback workspace"))
56819
+ throw new Error("fallback live state could not be committed");
56820
+ }
56821
+ if (!await assignLive({ state: "preparing", bound: bound2 }, clock4(), "preparing the workspace"))
56822
+ throw new Error("preparation live state could not be committed");
56823
+ shell.setSetupLabel(`${liveStateWords("preparing")}\u2026`);
56824
+ }
55895
56825
  if (resume && ledgerRun) {
55896
56826
  const rebound = workspaceBindingFor(round2.selection, profile.machine);
55897
56827
  if (rebound !== void 0) ledgerRun.setState({ binding: rebound });
@@ -56073,6 +57003,12 @@ async function dispatch(deps, msg, io, opts = {}) {
56073
57003
  markUntracked: () => shell.note("debug", "untracked by the ledger"),
56074
57004
  ...seedActors !== void 0 ? { seedActors } : {}
56075
57005
  });
57006
+ if (typeof registry.commitLiveState === "function") {
57007
+ const summary = registry.getById(runId3);
57008
+ const bound2 = summary?.liveState?.bound ?? startedAt + minutesToMs(profile.minutes);
57009
+ if (!await assignLive({ state: "working", bound: bound2 }, clock4(), "model turn"))
57010
+ throw new Error("working live state could not be committed");
57011
+ }
56076
57012
  const sessionTools = sessionCapabilityFor(
56077
57013
  ledgerRun,
56078
57014
  deps.runLedger,
@@ -56114,6 +57050,7 @@ async function dispatch(deps, msg, io, opts = {}) {
56114
57050
  mcpForRun,
56115
57051
  run: run2,
56116
57052
  registry,
57053
+ events,
56117
57054
  round: round2,
56118
57055
  admitted,
56119
57056
  ledgerRun,
@@ -56442,6 +57379,8 @@ var init_dispatcher = __esm({
56442
57379
  init_registry();
56443
57380
  init_budgets();
56444
57381
  init_trace();
57382
+ init_runLiveState();
57383
+ init_decide();
56445
57384
  init_requestTrace();
56446
57385
  init_runShape();
56447
57386
  init_runEvents();
@@ -56463,6 +57402,7 @@ var init_dispatcher = __esm({
56463
57402
  init_route();
56464
57403
  init_operator();
56465
57404
  init_authorize2();
57405
+ init_authorize();
56466
57406
  init_messages();
56467
57407
  init_staging();
56468
57408
  init_runRecord();
@@ -56481,6 +57421,7 @@ var init_dispatcher = __esm({
56481
57421
  init_settle();
56482
57422
  init_reattach2();
56483
57423
  init_factory();
57424
+ init_githubPulls();
56484
57425
  init_sandboxErrors();
56485
57426
  init_lineage();
56486
57427
  init_seed();
@@ -56841,8 +57782,8 @@ function createLedgerWriteThrough(opts) {
56841
57782
  settleLanding(this.runId, entry, outcome);
56842
57783
  return value;
56843
57784
  },
56844
- abandoned: (record2, why) => {
56845
- if (record2.id !== this.runId) return;
57785
+ abandoned: (record3, why) => {
57786
+ if (record3.id !== this.runId) return;
56846
57787
  const entry = landing.get(this.runId);
56847
57788
  if (entry !== void 0) settleLanding(this.runId, entry, { kind: "failed", why });
56848
57789
  }
@@ -56852,18 +57793,18 @@ function createLedgerWriteThrough(opts) {
56852
57793
  async land(plain7) {
56853
57794
  await this.close();
56854
57795
  const session = this.sessionOnRecord();
56855
- const record2 = session ? { ...plain7, session } : plain7;
57796
+ const record3 = session ? { ...plain7, session } : plain7;
56856
57797
  let result;
56857
57798
  try {
56858
- result = await ledger.finish(this.runId, gen, record2);
57799
+ result = await ledger.finish(this.runId, gen, record3);
56859
57800
  } catch (err2) {
56860
57801
  if (!(err2 instanceof RouteMissingError)) throw err2;
56861
57802
  routeMissing();
56862
- return { value: await fallback.put(record2), outcome: { kind: "refused" } };
57803
+ return { value: await fallback.put(record3), outcome: { kind: "refused" } };
56863
57804
  }
56864
57805
  if (result.ok) return { value: result, outcome: { kind: "landed" } };
56865
57806
  warn(`[ledger] ${this.threadKey} finish refused (${result.reason}) \u2014 record written to the store directly`);
56866
- return { value: await fallback.put(record2), outcome: { kind: "refused" } };
57807
+ return { value: await fallback.put(record3), outcome: { kind: "refused" } };
56867
57808
  }
56868
57809
  tracked() {
56869
57810
  return !this.detached;
@@ -56933,7 +57874,7 @@ function createLedgerWriteThrough(opts) {
56933
57874
  }));
56934
57875
  if (report2.compaction)
56935
57876
  turns.push({ idx: this.rowIndex(report2.firstIdx + report2.turns.length), compaction: report2.compaction });
56936
- const record2 = {
57877
+ const record3 = {
56937
57878
  step: ++this.stepNo,
56938
57879
  seq: this.lastSeq,
56939
57880
  turnIndex: report2.firstIdx + turns.length,
@@ -56944,10 +57885,10 @@ function createLedgerWriteThrough(opts) {
56944
57885
  turn: report2.turn,
56945
57886
  iteration: report2.iteration
56946
57887
  };
56947
- this.factRound = record2.step;
56948
- const kept = new Set(record2.inFlight.map((c) => c.callId));
57888
+ this.factRound = record3.step;
57889
+ const kept = new Set(record3.inFlight.map((c) => c.callId));
56949
57890
  for (const id of this.factCallStarts.keys()) if (!kept.has(id)) this.factCallStarts.delete(id);
56950
- const call2 = record2.inFlight[0];
57891
+ const call2 = record3.inFlight[0];
56951
57892
  this.factInFlight = call2 ? {
56952
57893
  callId: call2.callId,
56953
57894
  tool: call2.tool,
@@ -56959,13 +57900,13 @@ function createLedgerWriteThrough(opts) {
56959
57900
  } : void 0;
56960
57901
  for (let attempt = 1; ; attempt++) {
56961
57902
  try {
56962
- const result = await ledger.step(this.runId, gen, record2, turns, this.sessionRow?.key);
56963
- if (!result.ok) this.detach(`step ${record2.step} refused (${result.reason})`);
56964
- else this.turnsWritten = record2.turnIndex;
57903
+ const result = await ledger.step(this.runId, gen, record3, turns, this.sessionRow?.key);
57904
+ if (!result.ok) this.detach(`step ${record3.step} refused (${result.reason})`);
57905
+ else this.turnsWritten = record3.turnIndex;
56965
57906
  return;
56966
57907
  } catch (err2) {
56967
57908
  if (err2 instanceof RouteMissingError || err2 instanceof PermanentStoreError || attempt >= 2) {
56968
- this.detach(`step ${record2.step} failed: ${describe9(err2)}`);
57909
+ this.detach(`step ${record3.step} failed: ${describe9(err2)}`);
56969
57910
  return;
56970
57911
  }
56971
57912
  await sleep2(RETRY_MS[0]);
@@ -56973,7 +57914,7 @@ function createLedgerWriteThrough(opts) {
56973
57914
  }
56974
57915
  }
56975
57916
  event(event, seq) {
56976
- if (this.detached || this.finished) return;
57917
+ if (this.detached || this.finished || seq <= this.lastSeq) return;
56977
57918
  this.lastSeq = seq;
56978
57919
  const e = event;
56979
57920
  if (typeof e.at === "number") this.factLastEventAt = e.at;
@@ -56994,6 +57935,30 @@ function createLedgerWriteThrough(opts) {
56994
57935
  };
56995
57936
  this.flusher.push({ ...event, seq });
56996
57937
  }
57938
+ async assignLiveState(assignment) {
57939
+ if (this.detached || this.finished) return { ok: false, reason: "unknown-run" };
57940
+ await this.flusher.flush();
57941
+ await this.stateSending;
57942
+ try {
57943
+ const result = await ledger.assignLiveState(this.runId, gen, assignment);
57944
+ if (!result.ok) {
57945
+ if (result.reason === "fenced" || result.reason === "unknown-run")
57946
+ this.detach(`live state refused (${result.reason})`);
57947
+ return result;
57948
+ }
57949
+ this.state = {
57950
+ ...this.state,
57951
+ ...assignment.statePatch,
57952
+ liveState: result.liveState,
57953
+ liveStateSeq: result.liveStateSeq
57954
+ };
57955
+ this.lastSeq = Math.max(this.lastSeq, result.liveStateSeq);
57956
+ return result;
57957
+ } catch (err2) {
57958
+ warn(`[ledger] ${this.threadKey} live state not written: ${describe9(err2)}`);
57959
+ return { ok: false, reason: "unknown-run" };
57960
+ }
57961
+ }
56997
57962
  setState(patch) {
56998
57963
  if (this.detached) return;
56999
57964
  this.state = { ...this.state, ...patch };
@@ -57378,6 +58343,9 @@ var init_writeThrough = __esm({
57378
58343
  }
57379
58344
  event(_event, _seq) {
57380
58345
  }
58346
+ async assignLiveState(_assignment) {
58347
+ return { ok: false, reason: "unknown-run" };
58348
+ }
57381
58349
  setState(_patch) {
57382
58350
  }
57383
58351
  async finishing() {
@@ -58766,8 +59734,8 @@ async function launchOpenCode(deps, spec, bearer) {
58766
59734
  let consumed = 0;
58767
59735
  for (const line4 of text.split("\n").slice(0, -1)) {
58768
59736
  consumed += Buffer.byteLength(line4, "utf8") + 1;
58769
- const record2 = parseFeedRecord(line4);
58770
- if (record2?.feed === "tailer" && record2.note === "connected") {
59737
+ const record3 = parseFeedRecord(line4);
59738
+ if (record3?.feed === "tailer" && record3.note === "connected") {
58771
59739
  feedOffset = consumed;
58772
59740
  break;
58773
59741
  }
@@ -58847,9 +59815,9 @@ var init_process2 = __esm({
58847
59815
  });
58848
59816
 
58849
59817
  // ../../src/core/harness/opencode/bridge.ts
58850
- function executionStartedFor(record2, sessionID) {
58851
- if (record2.feed !== "event" || record2.event.type !== "session.execution.started") return false;
58852
- const data = record2.event.data;
59818
+ function executionStartedFor(record3, sessionID) {
59819
+ if (record3.feed !== "event" || record3.event.type !== "session.execution.started") return false;
59820
+ const data = record3.event.data;
58853
59821
  return data?.sessionID === sessionID;
58854
59822
  }
58855
59823
  function openCodeToolNameWord(name) {
@@ -59521,18 +60489,18 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
59521
60489
  const after = transport.consumedOffset;
59522
60490
  const reattachCatchUp = conn.reattach !== void 0 && after <= conn.reattach.catchUpTo;
59523
60491
  lastRecord = next.value;
59524
- const record2 = parseFeedRecord(next.value);
59525
- if (!record2) continue;
59526
- if (!reattachCatchUp && executionStartedFor(record2, conn.sessionID)) {
60492
+ const record3 = parseFeedRecord(next.value);
60493
+ if (!record3) continue;
60494
+ if (!reattachCatchUp && executionStartedFor(record3, conn.sessionID)) {
59527
60495
  executionOwned = true;
59528
60496
  awaiting = void 0;
59529
60497
  }
59530
60498
  bridge.observing = reattachCatchUp ? "catching-up" : executionOwned ? "own" : "earlier";
59531
- const obs = bridge.observe(record2);
60499
+ const obs = bridge.observe(record3);
59532
60500
  if (!reattachCatchUp && obs.boundary === true) conn.boundaries?.reached();
59533
60501
  if (!reattachCatchUp && obs.inboxDelivered !== void 0) conn.inboxFate?.deliver(obs.inboxDelivered);
59534
60502
  if (!reattachCatchUp && obs.inboxDropped === true) conn.inboxFate?.interruptAll();
59535
- if (record2.feed === "messages" && conn.saveOffset !== void 0) {
60503
+ if (record3.feed === "messages" && conn.saveOffset !== void 0) {
59536
60504
  const save = conn.saveOffset;
59537
60505
  void bridge.flush().then(() => save(after));
59538
60506
  }
@@ -59619,14 +60587,14 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
59619
60587
  );
59620
60588
  }
59621
60589
  if (replacedBy !== void 0) {
59622
- const record2 = bridge.record(recordBase, deadline);
60590
+ const record3 = bridge.record(recordBase, deadline);
59623
60591
  bridge.closeOpenSpans((open2) => openCodeReplacedCallNote(open2.tool));
59624
60592
  const replaced = replacedBy.condition === "word" ? new OpenCodeContainerReplacedError(
59625
60593
  replacedBy.said.message,
59626
60594
  conn.containerWord,
59627
60595
  await conn.container.identity().catch(() => void 0),
59628
- record2
59629
- ) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now, record2, "identity");
60596
+ record3
60597
+ ) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now, record3, "identity");
59630
60598
  note("sandbox_restarted", replaced.message);
59631
60599
  throw replaced;
59632
60600
  }
@@ -59766,13 +60734,13 @@ var init_bridge3 = __esm({
59766
60734
  diagnostics;
59767
60735
  };
59768
60736
  OpenCodeContainerReplacedError = class extends HarnessContainerReplacedError {
59769
- constructor(said2, was, now, record2, condition = "word") {
60737
+ constructor(said2, was, now, record3, condition = "word") {
59770
60738
  super(
59771
60739
  `the container running OpenCode was replaced (${was ?? "unknown"} \u2192 ${now ?? "unknown"}; ${replacedBecause(condition, said2)})`,
59772
60740
  said2,
59773
60741
  was,
59774
60742
  now,
59775
- record2,
60743
+ record3,
59776
60744
  condition
59777
60745
  );
59778
60746
  this.name = "OpenCodeContainerReplacedError";
@@ -60221,25 +61189,25 @@ var init_bridge3 = __esm({
60221
61189
  emit(event) {
60222
61190
  this.deps.emit(event.at === void 0 ? { ...event, at: this.deps.clock() } : event);
60223
61191
  }
60224
- observe(record2) {
60225
- switch (record2.feed) {
61192
+ observe(record3) {
61193
+ switch (record3.feed) {
60226
61194
  case "event":
60227
- return this.onEvent(record2.event);
61195
+ return this.onEvent(record3.event);
60228
61196
  case "permissions":
60229
61197
  if (this.observing === "earlier") return { replies: [], settled: false };
60230
- return this.onPermissionsRefill(record2.data);
61198
+ return this.onPermissionsRefill(record3.data);
60231
61199
  case "messages": {
60232
- const out = this.onMessagesRefill(record2.data);
60233
- if (this.settleOwedTo !== void 0 && record2.reason === this.settleOwedTo) {
61200
+ const out = this.onMessagesRefill(record3.data);
61201
+ if (this.settleOwedTo !== void 0 && record3.reason === this.settleOwedTo) {
60234
61202
  this.settleOwedTo = void 0;
60235
61203
  out.settled = true;
60236
61204
  }
60237
61205
  return out;
60238
61206
  }
60239
61207
  case "tailer":
60240
- if (record2.note === "stream closed" || record2.note.includes("failed"))
60241
- this.deps.onProgress?.(`opencode feed: ${redactAndCap(record2.note, 120)}`);
60242
- if (this.settleOwedTo !== void 0 && (record2.note === "message refill failed" || record2.note === "refill failed") && record2.reason === this.settleOwedTo) {
61208
+ if (record3.note === "stream closed" || record3.note.includes("failed"))
61209
+ this.deps.onProgress?.(`opencode feed: ${redactAndCap(record3.note, 120)}`);
61210
+ if (this.settleOwedTo !== void 0 && (record3.note === "message refill failed" || record3.note === "refill failed") && record3.reason === this.settleOwedTo) {
60243
61211
  this.settleOwedTo = void 0;
60244
61212
  return { replies: [], settled: true };
60245
61213
  }
@@ -61199,7 +62167,7 @@ ${stagedLine}` : prompt;
61199
62167
  return "lost";
61200
62168
  return "running";
61201
62169
  };
61202
- const resolve13 = async (steerSeq, knownAtSteer, seen, waiting2) => {
62170
+ const resolve13 = async (steerSeq, knownAtSteer, seen, waiting3) => {
61203
62171
  let left = false;
61204
62172
  for (; ; ) {
61205
62173
  const verdict = await learnRow(steerSeq, knownAtSteer);
@@ -61214,7 +62182,7 @@ ${stagedLine}` : prompt;
61214
62182
  "the follow-up's steer",
61215
62183
  "the loop left the feed with no row of the steer and the store still showing its execution under way, so the store cannot say whether the server took it"
61216
62184
  );
61217
- waiting2();
62185
+ waiting3();
61218
62186
  if (await conn.boundaries.wait(seen) === "left") left = true;
61219
62187
  seen = conn.boundaries.count;
61220
62188
  }
@@ -61526,6 +62494,14 @@ var init_requester = __esm({
61526
62494
 
61527
62495
  // ../../src/channels/http.ts
61528
62496
  import { timingSafeEqual as timingSafeEqual4 } from "node:crypto";
62497
+ function ingressComponentError(name, value) {
62498
+ if (value.length === 0) return `\`${name}\` must be a non-empty string`;
62499
+ if (value.length > INGRESS_COMPONENT_MAX_LENGTH) {
62500
+ return `\`${name}\` must be at most ${INGRESS_COMPONENT_MAX_LENGTH} characters`;
62501
+ }
62502
+ if (/[:#]/.test(value)) return `\`${name}\` must not contain ':' or '#'`;
62503
+ return void 0;
62504
+ }
61529
62505
  function safeEqual2(a, b) {
61530
62506
  const ab = Buffer.from(a, "utf8");
61531
62507
  const bb = Buffer.from(b, "utf8");
@@ -61569,11 +62545,15 @@ function parseBody3(raw) {
61569
62545
  if (typeof obj2.text !== "string" || obj2.text.trim() === "") {
61570
62546
  return { error: "`text` is required and must be a non-empty string" };
61571
62547
  }
61572
- if (obj2.channel !== void 0 && typeof obj2.channel !== "string") {
61573
- return { error: "`channel` must be a string" };
62548
+ if (obj2.channel !== void 0) {
62549
+ if (typeof obj2.channel !== "string") return { error: "`channel` must be a string" };
62550
+ const error = ingressComponentError("channel", obj2.channel);
62551
+ if (error) return { error };
61574
62552
  }
61575
- if (obj2.thread !== void 0 && typeof obj2.thread !== "string") {
61576
- return { error: "`thread` must be a string" };
62553
+ if (obj2.thread !== void 0) {
62554
+ if (typeof obj2.thread !== "string") return { error: "`thread` must be a string" };
62555
+ const error = ingressComponentError("thread", obj2.thread);
62556
+ if (error) return { error };
61577
62557
  }
61578
62558
  if (obj2.async !== void 0 && typeof obj2.async !== "boolean") {
61579
62559
  return { error: "`async` must be a boolean" };
@@ -61626,6 +62606,10 @@ async function handleAuthorized(identity2, body, deps, options) {
61626
62606
  if ("error" in parsed) {
61627
62607
  return { status: 400, body: { error: parsed.error } };
61628
62608
  }
62609
+ if (identity2.channel !== void 0) {
62610
+ const error = ingressComponentError("channel", identity2.channel);
62611
+ if (error) return { status: 400, body: { error } };
62612
+ }
61629
62613
  const receivedAt = systemClock();
61630
62614
  const trace = startRequestRoot(deps, { channel: "http", receivedAt });
61631
62615
  const msg = {
@@ -61706,7 +62690,7 @@ function parseIngressTokens(secrets) {
61706
62690
  if (!parsed.ok) console.error(`[ingress] SWITCHBOARD_INGRESS_TOKENS is ${parsed.reason} \u2014 ingress disabled`);
61707
62691
  return { tokens: parsed.tokens };
61708
62692
  }
61709
- var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
62693
+ var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, INGRESS_COMPONENT_MAX_LENGTH, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
61710
62694
  var init_http = __esm({
61711
62695
  "../../src/channels/http.ts"() {
61712
62696
  "use strict";
@@ -61719,6 +62703,7 @@ var init_http = __esm({
61719
62703
  PLATFORM = "http";
61720
62704
  DEFAULT_CHANNEL = "default";
61721
62705
  DEFAULT_THREAD = "default";
62706
+ INGRESS_COMPONENT_MAX_LENGTH = 128;
61722
62707
  MAX_BODY_BYTES2 = 1e6;
61723
62708
  HttpIO = class {
61724
62709
  constructor(priorTurns = []) {
@@ -61915,7 +62900,7 @@ function openAiStopReason(raw) {
61915
62900
  }
61916
62901
  }
61917
62902
  function meterAnthropicMessage(json4) {
61918
- const msg = record(json4);
62903
+ const msg = record2(json4);
61919
62904
  if (!msg) return {};
61920
62905
  const usage = usageFromAnthropic(msg.usage);
61921
62906
  return {
@@ -61924,11 +62909,11 @@ function meterAnthropicMessage(json4) {
61924
62909
  };
61925
62910
  }
61926
62911
  function meterOpenAiCompletion(json4) {
61927
- const body = record(json4);
62912
+ const body = record2(json4);
61928
62913
  if (!body) return {};
61929
62914
  const usage = usageFromOpenAI(body.usage);
61930
62915
  const reported = reportedCostOf(body.usage);
61931
- const choice = Array.isArray(body.choices) ? record(body.choices[0]) : void 0;
62916
+ const choice = Array.isArray(body.choices) ? record2(body.choices[0]) : void 0;
61932
62917
  return {
61933
62918
  ...usage ? { usage } : {},
61934
62919
  ...reported ? { reported } : {},
@@ -61938,15 +62923,15 @@ function meterOpenAiCompletion(json4) {
61938
62923
  function responsesStopReason(response) {
61939
62924
  const output = Array.isArray(response.output) ? response.output : [];
61940
62925
  if (response.status === "completed") {
61941
- return output.some((item) => record(item)?.type === "function_call") ? "tool_use" : "end_turn";
62926
+ return output.some((item) => record2(item)?.type === "function_call") ? "tool_use" : "end_turn";
61942
62927
  }
61943
62928
  if (response.status === "incomplete") {
61944
- return record(response.incomplete_details)?.reason === "max_output_tokens" ? "max_tokens" : "other";
62929
+ return record2(response.incomplete_details)?.reason === "max_output_tokens" ? "max_tokens" : "other";
61945
62930
  }
61946
62931
  return "other";
61947
62932
  }
61948
62933
  function meterResponses(json4) {
61949
- const body = record(json4);
62934
+ const body = record2(json4);
61950
62935
  if (!body) return {};
61951
62936
  const usage = usageFromResponses(body.usage);
61952
62937
  const reported = reportedCostOf(body.usage);
@@ -61968,87 +62953,20 @@ function shapeTools(shape, body, marks) {
61968
62953
  const allowed2 = new Set(marks.turn.tools);
61969
62954
  const table = Array.isArray(body.tools) ? body.tools : [];
61970
62955
  const kept = table.filter((t) => {
61971
- const name = toolNameOf(shape, record(t));
62956
+ const name = toolNameOf(shape, record2(t));
61972
62957
  return typeof name === "string" && allowed2.has(name);
61973
62958
  });
61974
62959
  const { tool_choice: _choice, ...rest } = body;
61975
62960
  const shaped = { ...rest, tools: kept };
61976
62961
  return { body: shaped, toolChoice: toolChoiceWord(shape, void 0, kept.length) };
61977
62962
  }
61978
- function responsesRejectsPattern(pattern) {
61979
- return /\(\?(?:[=!]|<[=!])/.test(pattern);
61980
- }
61981
- function shapeResponsesSchema(value, tool, degradations) {
61982
- const schema = record(value);
61983
- if (!schema) {
61984
- if (!Array.isArray(value)) return { value, changed: false };
61985
- let changed2 = false;
61986
- const items = value.map((item) => {
61987
- const shaped = shapeResponsesSchema(item, tool, degradations);
61988
- changed2 ||= shaped.changed;
61989
- return shaped.value;
61990
- });
61991
- return { value: changed2 ? items : value, changed: changed2 };
61992
- }
61993
- let changed = false;
61994
- const out = {};
61995
- for (const [keyword, item] of Object.entries(schema)) {
61996
- if (keyword === "pattern" && typeof item === "string" && responsesRejectsPattern(item)) {
61997
- degradations.push({
61998
- tool,
61999
- keyword,
62000
- why: "the Responses wire does not accept regular-expression lookaround"
62001
- });
62002
- changed = true;
62003
- continue;
62004
- }
62005
- if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
62006
- let mapChanged = false;
62007
- const mapped = {};
62008
- for (const [name, child] of Object.entries(record(item))) {
62009
- const shaped = shapeResponsesSchema(child, tool, degradations);
62010
- mapChanged ||= shaped.changed;
62011
- mapped[name] = shaped.value;
62012
- }
62013
- out[keyword] = mapChanged ? mapped : item;
62014
- changed ||= mapChanged;
62015
- continue;
62016
- }
62017
- if (["additionalProperties", "allOf", "anyOf", "if", "items", "not", "oneOf", "then", "else"].includes(keyword) && typeof item === "object" && item !== null) {
62018
- const shaped = shapeResponsesSchema(item, tool, degradations);
62019
- out[keyword] = shaped.value;
62020
- changed ||= shaped.changed;
62021
- continue;
62022
- }
62023
- out[keyword] = item;
62024
- }
62025
- return { value: changed ? out : value, changed };
62026
- }
62027
- function shapeToolSchemasForWire(shape, body) {
62028
- if (shape !== "openai-responses" || !Array.isArray(body.tools)) return { body, degradations: [] };
62029
- const degradations = [];
62030
- let changed = false;
62031
- const tools = body.tools.map((item) => {
62032
- const tool = record(item);
62033
- if (!tool) return item;
62034
- const name = typeof tool.name === "string" ? tool.name : "unnamed";
62035
- const shaped = shapeResponsesSchema(tool.parameters, name, degradations);
62036
- if (!shaped.changed) return item;
62037
- changed = true;
62038
- return { ...tool, parameters: shaped.value };
62039
- });
62040
- const unique = degradations.filter(
62041
- (candidate, index, all) => all.findIndex((other) => other.tool === candidate.tool && other.keyword === candidate.keyword) === index
62042
- );
62043
- return { body: changed ? { ...body, tools } : body, degradations: unique };
62044
- }
62045
62963
  function toolNameOf(shape, tool) {
62046
62964
  if (tool === void 0) return void 0;
62047
- return shape === "openai-chat" ? record(tool.function)?.name : tool.name;
62965
+ return shape === "openai-chat" ? record2(tool.function)?.name : tool.name;
62048
62966
  }
62049
62967
  function toolSchemaOf(shape, tool) {
62050
62968
  if (shape === "anthropic-messages") return tool.input_schema;
62051
- if (shape === "openai-chat") return record(tool.function)?.parameters;
62969
+ if (shape === "openai-chat") return record2(tool.function)?.parameters;
62052
62970
  return tool.parameters;
62053
62971
  }
62054
62972
  function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
@@ -62056,12 +62974,12 @@ function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
62056
62974
  for (const item of value) schemaKeywords(item, into);
62057
62975
  return into;
62058
62976
  }
62059
- const schema = record(value);
62977
+ const schema = record2(value);
62060
62978
  if (!schema) return into;
62061
62979
  for (const [keyword, item] of Object.entries(schema)) {
62062
62980
  into.add(keyword);
62063
- if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
62064
- for (const child of Object.values(record(item))) schemaKeywords(child, into);
62981
+ if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record2(item)) {
62982
+ for (const child of Object.values(record2(item))) schemaKeywords(child, into);
62065
62983
  } else {
62066
62984
  schemaKeywords(item, into);
62067
62985
  }
@@ -62072,7 +62990,7 @@ function providerErrorProse(body) {
62072
62990
  const strings2 = (value, into = []) => {
62073
62991
  if (typeof value === "string") into.push(value);
62074
62992
  else if (Array.isArray(value)) for (const item of value) strings2(item, into);
62075
- else if (record(value)) for (const item of Object.values(record(value))) strings2(item, into);
62993
+ else if (record2(value)) for (const item of Object.values(record2(value))) strings2(item, into);
62076
62994
  return into;
62077
62995
  };
62078
62996
  try {
@@ -62090,7 +63008,7 @@ function providerSchemaRejectionOf2(shape, body, errorBody) {
62090
63008
  prose2
62091
63009
  ))
62092
63010
  return void 0;
62093
- const candidates2 = (Array.isArray(body.tools) ? body.tools : []).map((item) => record(item)).filter((item) => item !== void 0).map((tool) => ({ name: toolNameOf(shape, tool), schema: toolSchemaOf(shape, tool) })).filter(
63011
+ const candidates2 = (Array.isArray(body.tools) ? body.tools : []).map((item) => record2(item)).filter((item) => item !== void 0).map((tool) => ({ name: toolNameOf(shape, tool), schema: toolSchemaOf(shape, tool) })).filter(
62094
63012
  (tool) => typeof tool.name === "string" && quoted(prose2, tool.name)
62095
63013
  );
62096
63014
  if (candidates2.length !== 1) return void 0;
@@ -62113,7 +63031,7 @@ function toolsOffered(shape, body) {
62113
63031
  const table = Array.isArray(body.tools) ? body.tools : [];
62114
63032
  const names = [];
62115
63033
  for (const t of table) {
62116
- const name = toolNameOf(shape, record(t));
63034
+ const name = toolNameOf(shape, record2(t));
62117
63035
  if (typeof name === "string") names.push(name);
62118
63036
  }
62119
63037
  names.sort();
@@ -62126,7 +63044,7 @@ function toolsOffered(shape, body) {
62126
63044
  function toolChoiceWord(shape, choice, tools) {
62127
63045
  if (choice === void 0 || choice === null) return tools > 0 ? "auto" : "none";
62128
63046
  if (shape === "anthropic-messages") {
62129
- const type = record(choice)?.type;
63047
+ const type = record2(choice)?.type;
62130
63048
  if (type === "none" || type === "any" || type === "tool") return type;
62131
63049
  if (type === "auto") return tools > 0 ? "auto" : "none";
62132
63050
  return tools > 0 ? "auto" : "none";
@@ -62134,7 +63052,7 @@ function toolChoiceWord(shape, choice, tools) {
62134
63052
  if (choice === "none") return "none";
62135
63053
  if (choice === "required") return "any";
62136
63054
  if (choice === "auto") return tools > 0 ? "auto" : "none";
62137
- return record(choice) ? "tool" : tools > 0 ? "auto" : "none";
63055
+ return record2(choice) ? "tool" : tools > 0 ? "auto" : "none";
62138
63056
  }
62139
63057
  function turnAttrs(grant2, meter, ttftMs, offered, price) {
62140
63058
  const u = meter.usage;
@@ -62243,7 +63161,7 @@ async function handleAdmitted(door, req, deps) {
62243
63161
  } catch {
62244
63162
  return refusalResponse(shape, 400, "invalid_body", "the body is not JSON");
62245
63163
  }
62246
- const body = record(parsed);
63164
+ const body = record2(parsed);
62247
63165
  if (!body) return refusalResponse(shape, 400, "invalid_body", "the body must be a JSON object");
62248
63166
  const upstream = upstreamFor(
62249
63167
  shape,
@@ -62514,7 +63432,7 @@ function createModelProxyHandler(deps) {
62514
63432
  })();
62515
63433
  };
62516
63434
  }
62517
- var ANTHROPIC_MESSAGES_PATH, OPENAI_CHAT_COMPLETIONS_PATH, OPENAI_RESPONSES_PATH, PROXY_PATHS, RESPONSES_MIN_OUTPUT_TOKENS, MAX_PROXY_BODY_BYTES, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_ANTHROPIC_VERSION, FORWARDED_REQUEST_HEADERS, FORWARDED_RESPONSE_HEADERS, record, SseMeter, MODEL_STREAM_HEARTBEAT;
63435
+ var ANTHROPIC_MESSAGES_PATH, OPENAI_CHAT_COMPLETIONS_PATH, OPENAI_RESPONSES_PATH, PROXY_PATHS, RESPONSES_MIN_OUTPUT_TOKENS, MAX_PROXY_BODY_BYTES, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_ANTHROPIC_VERSION, FORWARDED_REQUEST_HEADERS, FORWARDED_RESPONSE_HEADERS, record2, SseMeter, MODEL_STREAM_HEARTBEAT;
62518
63436
  var init_modelProxy = __esm({
62519
63437
  "../../src/channels/modelProxy.ts"() {
62520
63438
  "use strict";
@@ -62522,6 +63440,7 @@ var init_modelProxy = __esm({
62522
63440
  init_providerFailureAuth();
62523
63441
  init_usage();
62524
63442
  init_modelPricing();
63443
+ init_providerToolSchemas();
62525
63444
  init_provider();
62526
63445
  init_http();
62527
63446
  ANTHROPIC_MESSAGES_PATH = "/v1/messages";
@@ -62538,7 +63457,7 @@ var init_modelProxy = __esm({
62538
63457
  DEFAULT_ANTHROPIC_VERSION = "2023-06-01";
62539
63458
  FORWARDED_REQUEST_HEADERS = ["accept", "anthropic-version", "anthropic-beta"];
62540
63459
  FORWARDED_RESPONSE_HEADERS = ["content-type", "request-id", "x-request-id"];
62541
- record = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
63460
+ record2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
62542
63461
  SseMeter = class {
62543
63462
  constructor(shape) {
62544
63463
  this.shape = shape;
@@ -62582,7 +63501,7 @@ var init_modelProxy = __esm({
62582
63501
  };
62583
63502
  }
62584
63503
  apply(json4) {
62585
- const event = record(json4);
63504
+ const event = record2(json4);
62586
63505
  if (!event) return;
62587
63506
  if (this.shape === "openai-responses") {
62588
63507
  if (event.type !== "response.completed" && event.type !== "response.incomplete" && event.type !== "response.failed")
@@ -62595,12 +63514,12 @@ var init_modelProxy = __esm({
62595
63514
  }
62596
63515
  if (this.shape === "anthropic-messages") {
62597
63516
  if (event.type === "message_start") {
62598
- const usage2 = usageFromAnthropic(record(event.message)?.usage);
63517
+ const usage2 = usageFromAnthropic(record2(event.message)?.usage);
62599
63518
  if (usage2) this.usage = { ...this.usage, ...usage2 };
62600
63519
  } else if (event.type === "message_delta") {
62601
- const stop = record(event.delta)?.stop_reason;
63520
+ const stop = record2(event.delta)?.stop_reason;
62602
63521
  if (typeof stop === "string") this.stopReason = anthropicStopReason(stop);
62603
- const usage2 = record(event.usage);
63522
+ const usage2 = record2(event.usage);
62604
63523
  if (usage2) {
62605
63524
  if (typeof usage2.output_tokens === "number") this.usage.outputTokens = usage2.output_tokens;
62606
63525
  if (typeof usage2.input_tokens === "number") this.usage.inputTokens = usage2.input_tokens;
@@ -62613,7 +63532,7 @@ var init_modelProxy = __esm({
62613
63532
  }
62614
63533
  return;
62615
63534
  }
62616
- const choice = Array.isArray(event.choices) ? record(event.choices[0]) : void 0;
63535
+ const choice = Array.isArray(event.choices) ? record2(event.choices[0]) : void 0;
62617
63536
  if (typeof choice?.finish_reason === "string") this.stopReason = openAiStopReason(choice.finish_reason);
62618
63537
  const usage = usageFromOpenAI(event.usage);
62619
63538
  if (usage) this.usage = { ...this.usage, ...usage };
@@ -62635,7 +63554,7 @@ function bearerOf(headers) {
62635
63554
  const m = value ? /^Bearer\s+(\S+)$/i.exec(value.trim()) : null;
62636
63555
  return m ? m[1] : void 0;
62637
63556
  }
62638
- function judge3(deps, presented) {
63557
+ function judge4(deps, presented) {
62639
63558
  const verdict = deps.bearers.verify(presented);
62640
63559
  if (!verdict.ok) {
62641
63560
  const status3 = verdict.reason === "unknown_run" ? 404 : verdict.reason === "malformed" ? 401 : verdict.reason === "unknown_bearer" ? 401 : 403;
@@ -62657,12 +63576,12 @@ function held(path, reason2, runId3) {
62657
63576
  async function admitHarnessRequest(deps, headers, path) {
62658
63577
  const presented = bearerOf(headers);
62659
63578
  if (!presented) return { ok: false, response: { status: 401, body: { error: "missing_bearer" } } };
62660
- let verdict = judge3(deps, presented);
63579
+ let verdict = judge4(deps, presented);
62661
63580
  if (verdict.ok) return verdict;
62662
63581
  if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
62663
63582
  if (!deps.takeover.settled) {
62664
63583
  await deps.takeover.whenSettled(DOOR_HOLD_MS);
62665
- verdict = judge3(deps, presented);
63584
+ verdict = judge4(deps, presented);
62666
63585
  if (verdict.ok) return verdict;
62667
63586
  if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
62668
63587
  if (!deps.takeover.settled) return held(path, "reclaim_pending", verdict.runId);
@@ -63998,13 +64917,13 @@ async function catchUpMissedMentions(opts) {
63998
64917
  const orphanCutoffMs = now - (opts.orphanWindowMs ?? ORPHAN_CARD_WINDOW_MS);
63999
64918
  const threadCutoffMs = sweep ? Math.min(cutoffMs, orphanCutoffMs) : cutoffMs;
64000
64919
  const { client, botUserId } = opts;
64001
- const record2 = opts.record ?? recordCatchUpOutcome;
64920
+ const record3 = opts.record ?? recordCatchUpOutcome;
64002
64921
  let channels;
64003
64922
  try {
64004
64923
  channels = await listChannels(client);
64005
64924
  } catch (err2) {
64006
64925
  log(`[catch-up] cannot list channels: ${errMsg(err2)}`);
64007
- record2({ at: now, channels: 0, missed: 0, skippedChannels: 0, error: errMsg(err2) });
64926
+ record3({ at: now, channels: 0, missed: 0, skippedChannels: 0, error: errMsg(err2) });
64008
64927
  return { channels: 0, missed: 0, orphans: 0, skippedChannels: 0 };
64009
64928
  }
64010
64929
  const scans = await mapLimit(channels, CATCH_UP_CHANNEL_CONCURRENCY, async (channel) => {
@@ -64073,7 +64992,7 @@ async function catchUpMissedMentions(opts) {
64073
64992
  log(
64074
64993
  `[catch-up] scanned ${channels.length} channel(s): ${missed} missed message(s), ${orphans} orphaned card(s), ${skippedChannels} skipped`
64075
64994
  );
64076
- record2({ at: now, channels: channels.length, missed, skippedChannels });
64995
+ record3({ at: now, channels: channels.length, missed, skippedChannels });
64077
64996
  return { channels: channels.length, missed, orphans, skippedChannels };
64078
64997
  }
64079
64998
  async function listChannels(client) {
@@ -65961,11 +66880,19 @@ ${JSON.stringify(result.value)}` }] });
65961
66880
  if (typeof args.text !== "string" || args.text.trim() === "") {
65962
66881
  return err(id, INVALID_PARAMS, "`text` is required and must be a non-empty string");
65963
66882
  }
65964
- if (args.channel !== void 0 && typeof args.channel !== "string") {
65965
- return err(id, INVALID_PARAMS, "`channel` must be a string");
66883
+ if (args.channel !== void 0) {
66884
+ if (typeof args.channel !== "string") return err(id, INVALID_PARAMS, "`channel` must be a string");
66885
+ const error = ingressComponentError("channel", args.channel);
66886
+ if (error) return err(id, INVALID_PARAMS, error);
66887
+ }
66888
+ if (args.thread !== void 0) {
66889
+ if (typeof args.thread !== "string") return err(id, INVALID_PARAMS, "`thread` must be a string");
66890
+ const error = ingressComponentError("thread", args.thread);
66891
+ if (error) return err(id, INVALID_PARAMS, error);
65966
66892
  }
65967
- if (args.thread !== void 0 && typeof args.thread !== "string") {
65968
- return err(id, INVALID_PARAMS, "`thread` must be a string");
66893
+ if (identity2.channel !== void 0) {
66894
+ const error = ingressComponentError("channel", identity2.channel);
66895
+ if (error) return err(id, INVALID_PARAMS, error);
65969
66896
  }
65970
66897
  const receivedAt = systemClock();
65971
66898
  const trace = startRequestRoot(deps, { channel: "mcp", receivedAt });
@@ -67601,16 +68528,16 @@ function residentStateTone(state) {
67601
68528
  }
67602
68529
  function residentsFleetTone(records) {
67603
68530
  if (records.length === 0) return "grey";
67604
- const tones = new Set(records.map((record2) => residentStateTone(residentLive(rec(record2)).state)));
68531
+ const tones = new Set(records.map((record3) => residentStateTone(residentLive(rec(record3)).state)));
67605
68532
  if (tones.has("red")) return "red";
67606
68533
  if (tones.has("amber")) return "amber";
67607
68534
  return tones.size === 1 && tones.has("green") ? "green" : "grey";
67608
68535
  }
67609
- function residentSlug(record2) {
67610
- return str8(record2.resource).replace(/^repo:/, "");
68536
+ function residentSlug(record3) {
68537
+ return str8(record3.resource).replace(/^repo:/, "");
67611
68538
  }
67612
- function residentLive(record2) {
67613
- const live = rec(record2.live);
68539
+ function residentLive(record3) {
68540
+ const live = rec(record3.live);
67614
68541
  if (typeof live.error === "string") return { ...live, state: "unreachable", reason: live.error };
67615
68542
  return { ...live, state: str8(live.state) || "unknown", reason: str8(live.reason) };
67616
68543
  }
@@ -67910,12 +68837,12 @@ function createResidentsViewHandler(deps) {
67910
68837
  finish2(200);
67911
68838
  return;
67912
68839
  }
67913
- const record2 = listing.residents.find((x) => residentSlug(x) === route2.slug);
67914
- if (!record2) {
68840
+ const record3 = listing.residents.find((x) => residentSlug(x) === route2.slug);
68841
+ if (!record3) {
67915
68842
  plainEnd(404, `${route2.slug} is not onboarded as a resident`);
67916
68843
  return;
67917
68844
  }
67918
- page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record: record2 });
68845
+ page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record: record3 });
67919
68846
  finish2(200);
67920
68847
  });
67921
68848
  return true;
@@ -70057,9 +70984,9 @@ function startReclaimSweep(opts) {
70057
70984
  return { stop: () => stop(timer) };
70058
70985
  }
70059
70986
  async function closeReclaimed(ledger, gen, input) {
70060
- const record2 = reclaimedRunRecord(input);
70061
- const result = await ledger.finish(input.row.runId, gen, record2);
70062
- return result.ok ? { ok: true, record: record2 } : { ok: false, reason: result.reason ?? "refused" };
70987
+ const record3 = reclaimedRunRecord(input);
70988
+ const result = await ledger.finish(input.row.runId, gen, record3);
70989
+ return result.ok ? { ok: true, record: record3 } : { ok: false, reason: result.reason ?? "refused" };
70063
70990
  }
70064
70991
  async function completenessVerdict(ledger, row2, lastStep) {
70065
70992
  if (!lastStep) return { resumable: false, why: "no step record: killed before its conversation was stored" };
@@ -70226,7 +71153,13 @@ async function launchResumes(deps, resumable, opts) {
70226
71153
  const handle2 = registry.create(row2.meta.label, rehostMeta(row2), {
70227
71154
  id: row2.runId,
70228
71155
  startedAt: row2.startedAt,
70229
- replay: run2.events
71156
+ replay: run2.events,
71157
+ ...row2.liveState !== void 0 ? {
71158
+ liveState: {
71159
+ liveState: row2.liveState,
71160
+ ...row2.liveStateSeq !== void 0 ? { liveStateSeq: row2.liveStateSeq } : {}
71161
+ }
71162
+ } : {}
70230
71163
  });
70231
71164
  const adopted = deps.runLedger.adopt({
70232
71165
  runId: row2.runId,
@@ -70667,6 +71600,72 @@ var init_adminModelProxy = __esm({
70667
71600
  }
70668
71601
  });
70669
71602
 
71603
+ // ../../src/core/providerLiveStateCoordinator.ts
71604
+ function createProviderLiveStateCoordinator(deps) {
71605
+ let tail = Promise.resolve();
71606
+ const enqueue = (label, work) => {
71607
+ const next = tail.then(work).catch((error) => {
71608
+ deps.warn(`${label} failed: ${describe11(error)}`);
71609
+ });
71610
+ tail = next;
71611
+ return next;
71612
+ };
71613
+ return {
71614
+ level: (provider, side, cause) => enqueue(`provider ${side} report for ${provider}`, async () => {
71615
+ await deps.runLedger.planeLevel({
71616
+ provider,
71617
+ name: "provider",
71618
+ side,
71619
+ ...side === "down" && cause !== void 0 ? { cause } : {}
71620
+ });
71621
+ if (side !== "up" || !deps.ledger) return;
71622
+ const rows = await deps.ledger.listLive();
71623
+ for (const row2 of rows) {
71624
+ if (row2.state.liveProvider !== provider || row2.liveState?.state !== "waiting_provider") continue;
71625
+ const tracked = deps.runLedger.liveRuns().find((run2) => run2.runId === row2.runId);
71626
+ const summary = deps.registry.getById(row2.runId);
71627
+ if (!tracked || !summary || row2.liveState.bound === void 0) continue;
71628
+ const committed = await tracked.assignLiveState({
71629
+ expectedSeq: summary.liveStateSeq ?? 0,
71630
+ eventSeq: summary.eventCount + 1,
71631
+ at: deps.clock(),
71632
+ state: "working",
71633
+ bound: row2.liveState.bound,
71634
+ detail: "model turn",
71635
+ statePatch: { liveProvider: void 0 }
71636
+ });
71637
+ if (committed.ok) deps.registry.commitLiveState(row2.runId, committed);
71638
+ }
71639
+ }),
71640
+ park: (runId3, provider) => enqueue(`provider park for ${runId3}`, async () => {
71641
+ await deps.runLedger.planePark(runId3, provider);
71642
+ const summary = deps.registry.getById(runId3);
71643
+ const tracked = deps.runLedger.liveRuns().find((run2) => run2.runId === runId3);
71644
+ const events = deps.registry.snapshotById(runId3)?.events ?? [];
71645
+ let leaseEndsAt;
71646
+ for (const event of events) if (event.type === "lease") leaseEndsAt = event.endsAt;
71647
+ if (!summary || !tracked || leaseEndsAt === void 0) return;
71648
+ const committed = await tracked.assignLiveState({
71649
+ expectedSeq: summary.liveStateSeq ?? 0,
71650
+ eventSeq: summary.eventCount + 1,
71651
+ at: deps.clock(),
71652
+ state: "waiting_provider",
71653
+ bound: leaseEndsAt,
71654
+ detail: "waiting for the model provider",
71655
+ statePatch: { liveProvider: provider }
71656
+ });
71657
+ if (committed.ok) deps.registry.commitLiveState(runId3, committed);
71658
+ })
71659
+ };
71660
+ }
71661
+ var describe11;
71662
+ var init_providerLiveStateCoordinator = __esm({
71663
+ "../../src/core/providerLiveStateCoordinator.ts"() {
71664
+ "use strict";
71665
+ describe11 = (error) => error instanceof Error ? error.message : String(error);
71666
+ }
71667
+ });
71668
+
70670
71669
  // ../../src/channels/adminTraceLog.ts
70671
71670
  function parseTraceLogQuery(search) {
70672
71671
  const query = {};
@@ -71176,7 +72175,7 @@ async function openThreadFromRequester(deps, instance, lead, at) {
71176
72175
  }
71177
72176
  };
71178
72177
  } catch (err2) {
71179
- return { ok: false, response: json3(502, { ok: false, error: "thread_failed", message: describe11(err2), at }) };
72178
+ return { ok: false, response: json3(502, { ok: false, error: "thread_failed", message: describe12(err2), at }) };
71180
72179
  }
71181
72180
  }
71182
72181
  function authorizeStep(headers, step, deps) {
@@ -71300,8 +72299,8 @@ async function spawn6(body, deps) {
71300
72299
  try {
71301
72300
  turn = await composeChild(req.brief, instance, row2, briefReaders(deps, instance));
71302
72301
  } catch (err2) {
71303
- log(`[coordinator] ${instance.id} ${req.step}: the brief could not be composed: ${describe11(err2)}`);
71304
- return json3(502, { ok: false, error: "brief_failed", message: describe11(err2), at });
72302
+ log(`[coordinator] ${instance.id} ${req.step}: the brief could not be composed: ${describe12(err2)}`);
72303
+ return json3(502, { ok: false, error: "brief_failed", message: describe12(err2), at });
71305
72304
  }
71306
72305
  } else {
71307
72306
  turn = { prompt: req.prompt };
@@ -71371,7 +72370,7 @@ ${foldThreadEvents(folded)}` };
71371
72370
  });
71372
72371
  } catch (err2) {
71373
72372
  releaseAdmission();
71374
- return json3(502, { ok: false, error: "spawn_failed", message: describe11(err2), at });
72373
+ return json3(502, { ok: false, error: "spawn_failed", message: describe12(err2), at });
71375
72374
  }
71376
72375
  const settled = dispatching.then(
71377
72376
  (outcome) => ({ kind: "ended", outcome }),
@@ -71380,7 +72379,7 @@ ${foldThreadEvents(folded)}` };
71380
72379
  void settled.then((end) => {
71381
72380
  releaseAdmission();
71382
72381
  if (end.kind === "threw")
71383
- log(`[coordinator] ${instance.id} ${req.step}: the child's dispatch threw: ${describe11(end.err)}`);
72382
+ log(`[coordinator] ${instance.id} ${req.step}: the child's dispatch threw: ${describe12(end.err)}`);
71384
72383
  });
71385
72384
  const first = await Promise.race([started.then((id) => ({ kind: "started", id })), settled]);
71386
72385
  if (first.kind === "started" || startedId !== void 0) {
@@ -71390,7 +72389,7 @@ ${foldThreadEvents(folded)}` };
71390
72389
  { instanceId: instance.id, unit: row2.unit },
71391
72390
  folded.map((e) => e.seq),
71392
72391
  req.step
71393
- ).catch((err2) => log(`[coordinator] ${instance.id} ${req.step}: the consumed marks failed: ${describe11(err2)}`));
72392
+ ).catch((err2) => log(`[coordinator] ${instance.id} ${req.step}: the consumed marks failed: ${describe12(err2)}`));
71394
72393
  log(
71395
72394
  `[coordinator] ${instance.id} ${req.step}: folded ${folded.length} thread event(s) into the ${req.preset} child`
71396
72395
  );
@@ -71398,7 +72397,7 @@ ${foldThreadEvents(folded)}` };
71398
72397
  log(`[coordinator] ${instance.id} ${req.step}: spawned ${req.preset} run ${runId3} in ${threadKey}`);
71399
72398
  return json3(200, { ok: true, runId: runId3, threadKey, at });
71400
72399
  }
71401
- if (first.kind === "threw") return json3(502, { ok: false, error: "spawn_failed", message: describe11(first.err), at });
72400
+ if (first.kind === "threw") return json3(502, { ok: false, error: "spawn_failed", message: describe12(first.err), at });
71402
72401
  const refusal = first.outcome.refusal;
71403
72402
  if (refusal === "coordinator_thread_live") {
71404
72403
  const now = await liveOnThread(deps.runs, instance, threadKey);
@@ -71537,11 +72536,11 @@ async function reviewPostedAtPatiently(deps, pr, verdict, head) {
71537
72536
  }
71538
72537
  return answer;
71539
72538
  }
71540
- function reviewPostedByRecord(record2, pr) {
71541
- const post = record2.reviewPost;
71542
- if (post === void 0 || record2.reviewHead === void 0 || record2.verdict === void 0) return void 0;
72539
+ function reviewPostedByRecord(record3, pr) {
72540
+ const post = record3.reviewPost;
72541
+ if (post === void 0 || record3.reviewHead === void 0 || record3.verdict === void 0) return void 0;
71543
72542
  if (!post.posted) return { reviewPosted: false, reviewPostReason: post.reason };
71544
- const same = post.target.repo === pr.repo && post.target.number === pr.number && sameCommit(post.head.toLowerCase(), record2.reviewHead) && post.verdict === record2.verdict.verdict;
72543
+ const same = post.target.repo === pr.repo && post.target.number === pr.number && sameCommit(post.head.toLowerCase(), record3.reviewHead) && post.verdict === record3.verdict.verdict;
71545
72544
  return same ? { reviewPosted: true } : void 0;
71546
72545
  }
71547
72546
  function headBranchStateUnknown(facts2) {
@@ -71558,6 +72557,7 @@ function pullRequestState(number, fallbackUrl, facts2) {
71558
72557
  state: "merged",
71559
72558
  prNumber: number,
71560
72559
  url,
72560
+ ...facts2.headSha !== void 0 ? { headSha: facts2.headSha } : {},
71561
72561
  sha: facts2.mergeCommitSha,
71562
72562
  mergedAt: facts2.mergedAt,
71563
72563
  ...facts2.mergedBy !== void 0 ? { mergedBy: facts2.mergedBy } : {}
@@ -71618,7 +72618,7 @@ async function readRecord(body, deps) {
71618
72618
  try {
71619
72619
  pullRequest = await childPullRequestState(deps, instanceRow, typeof body.unit === "string" ? body.unit : void 0);
71620
72620
  } catch (err2) {
71621
- return json3(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
72621
+ return json3(502, { ok: false, error: "github_unavailable", message: describe12(err2), at });
71622
72622
  }
71623
72623
  if (!view.finished)
71624
72624
  return json3(200, {
@@ -71628,15 +72628,16 @@ async function readRecord(body, deps) {
71628
72628
  at
71629
72629
  });
71630
72630
  const full = await deps.runs.getRun(body.runId, { include: "messages" });
71631
- const record2 = full.ok ? full.value : view;
71632
- const finalReply = finalReplyOf2(record2.events);
71633
- const reviewAskedAt = reviewAskedAtOf(record2.events);
71634
- const opened = prOpenedOf(record2.events);
72631
+ const record3 = full.ok ? full.value : view;
72632
+ const finalReply = finalReplyOf2(record3.events);
72633
+ const reviewAskedAt = reviewAskedAtOf(record3.events);
72634
+ const description = record3.events?.some((event) => event.type === "pr_description") === true;
72635
+ const opened = prOpenedOf(record3.events);
71635
72636
  const pr = opened !== void 0 ? { number: opened.number, url: opened.url, created: opened.created } : void 0;
71636
72637
  let coordinatorUnit;
71637
72638
  if (instanceRow !== null && typeof body.unit === "string") {
71638
72639
  coordinatorUnit = (await deps.instances.listUnits(instanceRow.id)).find((u) => u.unit === body.unit);
71639
- const pushedPrRef = opened?.head !== void 0 && record2.pushed?.some((pushed) => pushed.ref === opened.head) ? opened.head : void 0;
72640
+ const pushedPrRef = opened?.head !== void 0 && record3.pushed?.some((pushed) => pushed.ref === opened.head) ? opened.head : void 0;
71640
72641
  if (coordinatorUnit !== void 0 && pushedPrRef !== void 0 && coordinatorUnit.branch !== pushedPrRef) {
71641
72642
  coordinatorUnit = { ...coordinatorUnit, branch: pushedPrRef };
71642
72643
  await deps.instances.putUnits([coordinatorUnit]);
@@ -71663,13 +72664,13 @@ async function readRecord(body, deps) {
71663
72664
  });
71664
72665
  }
71665
72666
  let posted;
71666
- if (record2.verdict !== void 0 && record2.reviewHead !== void 0 && typeof body.unit === "string") {
72667
+ if (record3.verdict !== void 0 && record3.reviewHead !== void 0 && typeof body.unit === "string") {
71667
72668
  const instance = instanceRow;
71668
72669
  if (instance && coordinatorUnit?.pr !== void 0) {
71669
72670
  const unitPr = { repo: instance.repo, number: coordinatorUnit.pr.number };
71670
- posted = reviewPostedByRecord(record2, unitPr);
72671
+ posted = reviewPostedByRecord(record3, unitPr);
71671
72672
  if (posted === void 0) {
71672
- const seen = await reviewPostedAtPatiently(deps, unitPr, record2.verdict.verdict, record2.reviewHead);
72673
+ const seen = await reviewPostedAtPatiently(deps, unitPr, record3.verdict.verdict, record3.reviewHead);
71673
72674
  if (seen !== void 0) posted = { reviewPosted: seen };
71674
72675
  }
71675
72676
  }
@@ -71681,27 +72682,31 @@ async function readRecord(body, deps) {
71681
72682
  run: {
71682
72683
  ...coordinatorRunView(view, id.value, finalReply),
71683
72684
  ...pr !== void 0 ? { pr } : {},
71684
- ...record2.verdict !== void 0 ? { verdict: record2.verdict } : {},
71685
- ...record2.reviewHead !== void 0 ? { reviewHead: record2.reviewHead } : {},
72685
+ ...record3.verdict !== void 0 ? { verdict: record3.verdict } : {},
72686
+ ...record3.reviewHead !== void 0 ? { reviewHead: record3.reviewHead } : {},
71686
72687
  ...reviewAskedAt !== void 0 ? { reviewAskedAt } : {},
71687
72688
  ...posted !== void 0 ? { reviewPosted: posted.reviewPosted } : {},
71688
72689
  ...posted?.reviewPostReason !== void 0 ? { reviewPostReason: posted.reviewPostReason } : {},
71689
- ...record2.dispositions !== void 0 ? { dispositions: record2.dispositions } : {},
71690
- ...record2.handoff !== void 0 ? { handoff: true } : {},
72690
+ ...record3.dispositions !== void 0 ? { dispositions: record3.dispositions } : {},
72691
+ // Findings readiness requires the typed description output. Its event is
72692
+ // durable on the child record, so the runner can verify it without
72693
+ // trusting final-reply prose.
72694
+ ...description ? { description: true } : {},
72695
+ ...record3.handoff !== void 0 ? { handoff: true } : {},
71691
72696
  // The renewal's facts (decision 0046): progress is read off these.
71692
- ...record2.pushed !== void 0 ? { pushed: record2.pushed } : {},
71693
- ...record2.lease !== void 0 ? { leaseStartedAt: record2.lease.startedAt } : {},
72697
+ ...record3.pushed !== void 0 ? { pushed: record3.pushed } : {},
72698
+ ...record3.lease !== void 0 ? { leaseStartedAt: record3.lease.startedAt } : {},
71694
72699
  // What the child cost, as the runs service prices it (costs.md item 4c):
71695
72700
  // null when unknown — no usage on the record, or a model without a price
71696
72701
  // — so a capped grant never renews on an understated total.
71697
- costUsd: record2.cost?.usd ?? null,
71698
- ...record2.handoff !== void 0 ? { handoffLists: record2.handoff } : {},
72702
+ costUsd: record3.cost?.usd ?? null,
72703
+ ...record3.handoff !== void 0 ? { handoffLists: record3.handoff } : {},
71699
72704
  // The failure by name (run-history item 57): a `provider_transient` lets
71700
72705
  // the machine re-run a round-0 child that pushed nothing (issue 1932).
71701
- ...record2.failure !== void 0 ? { failure: record2.failure } : {},
72706
+ ...record3.failure !== void 0 ? { failure: record3.failure } : {},
71702
72707
  // What ended an interrupted child (issue 1876), off its record's own
71703
72708
  // events: the unit's ending names the actual cause in the user's nouns.
71704
- ...view.status === "interrupted" ? interruptionOf(record2.events) : {}
72709
+ ...view.status === "interrupted" ? interruptionOf(record3.events) : {}
71705
72710
  },
71706
72711
  at
71707
72712
  });
@@ -71780,6 +72785,13 @@ async function recoverPushedBranch(deps, instance, row2, branch2, runId3) {
71780
72785
  if (subject2 !== void 0 && checkPrTitle(subject2, prTitleVocabulary_default).ok) title = subject2;
71781
72786
  }
71782
72787
  title ??= recoveredFallbackTitle(row2, branch2, instance.plan?.id ?? parsePlanBranch(branch2)?.planId);
72788
+ const header4 = requestedByLine({
72789
+ name: instance.userName?.trim() || instance.userId,
72790
+ threadUrl: threadPageLink(instance.threadKey, deps.runPageBase?.replace(/\/runs\/?$/, "") ?? "")
72791
+ });
72792
+ prBody = `${header4}
72793
+
72794
+ ${prBody}`;
71783
72795
  if (deps.rewriteIdentities !== void 0) {
71784
72796
  const rewritten = await deps.rewriteIdentities({
71785
72797
  repo: instance.repo,
@@ -71934,7 +72946,7 @@ async function prCheck(body, deps) {
71934
72946
  if (recover === void 0) {
71935
72947
  const ahead = instance.base === void 0 ? void 0 : await deps.commitsOverBase(instance.repo, instance.base, branch2).catch((err2) => {
71936
72948
  (deps.log ?? console.log)(
71937
- `[coordinator] ${instance.id} pr-check: the compare of ${branch2} over ${instance.base} could not be read: ${describe11(err2)}`
72949
+ `[coordinator] ${instance.id} pr-check: the compare of ${branch2} over ${instance.base} could not be read: ${describe12(err2)}`
71938
72950
  );
71939
72951
  return void 0;
71940
72952
  });
@@ -71958,7 +72970,7 @@ async function prCheck(body, deps) {
71958
72970
  if (state?.state !== "merged") throw new Error(`${instance.repo}#${merged.number} no longer reads merged`);
71959
72971
  return json3(200, { ok: true, ...state, at });
71960
72972
  } catch (err2) {
71961
- return json3(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
72973
+ return json3(502, { ok: false, error: "github_unavailable", message: describe12(err2), at });
71962
72974
  }
71963
72975
  }
71964
72976
  async function hostRunOf(deps, instance) {
@@ -71971,14 +72983,68 @@ async function hostRunOf(deps, instance) {
71971
72983
  return { kind: "not_host" };
71972
72984
  return { kind: "untracked" };
71973
72985
  }
71974
- function hostPublish(deps, instance, runId3, events, at) {
71975
- for (const event of events) deps.registry.publish(runId3, event);
72986
+ async function hostPublish(deps, instance, runId3, events, at) {
71976
72987
  const caps = instance.caps ?? resolveShipCaps(void 0);
71977
72988
  const hosting = {
71978
72989
  instanceId: instance.id,
71979
72990
  until: at + minutesToMs(caps.maxMinutes + HOSTED_DEADLINE_MARGIN_MINUTES)
71980
72991
  };
71981
- deps.ledgerRuns().find((run2) => run2.runId === runId3)?.setState({ hosting });
72992
+ const ledgerRun = deps.ledgerRuns().find((run2) => run2.runId === runId3);
72993
+ let summary = deps.registry.getById(runId3);
72994
+ if (ledgerRun && summary) {
72995
+ if (summary.liveState === void 0) {
72996
+ const admitted = await ledgerRun.assignLiveState({
72997
+ expectedSeq: 0,
72998
+ eventSeq: summary.eventCount + 1,
72999
+ at,
73000
+ state: "admitted",
73001
+ bound: hosting.until,
73002
+ detail: "waiting for the pipeline hand-off"
73003
+ });
73004
+ if (!admitted.ok || !deps.registry.commitLiveState(runId3, admitted))
73005
+ throw new Error(
73006
+ `hosted live state backfill was not committed (${admitted.ok ? "registry sequence" : admitted.reason})`
73007
+ );
73008
+ summary = deps.registry.getById(runId3);
73009
+ if (!summary) throw new Error("hosted run disappeared during live-state backfill");
73010
+ const working = await ledgerRun.assignLiveState({
73011
+ expectedSeq: summary.liveStateSeq ?? 0,
73012
+ eventSeq: summary.eventCount + 1,
73013
+ at,
73014
+ state: "working",
73015
+ bound: hosting.until,
73016
+ detail: "pipeline starting",
73017
+ statePatch: { hosting }
73018
+ });
73019
+ if (!working.ok || !deps.registry.commitLiveState(runId3, working))
73020
+ throw new Error(
73021
+ `hosted working backfill was not committed (${working.ok ? "registry sequence" : working.reason})`
73022
+ );
73023
+ summary = deps.registry.getById(runId3);
73024
+ if (!summary) throw new Error("hosted run disappeared after live-state backfill");
73025
+ }
73026
+ const firstSeq = summary.eventCount + 1;
73027
+ const sourceEvents = events.map((event, index) => ({ ...event, seq: firstSeq + index }));
73028
+ const prior = deps.registry.snapshotById(runId3)?.events.filter((event) => event.type === "ship_round" || event.type === "ship_unit") ?? [];
73029
+ const standing = pipelineStandingOf([...prior, ...events]);
73030
+ const stage = standing.changes.at(-1)?.stage ?? "idle";
73031
+ const committed = await ledgerRun.assignLiveState({
73032
+ expectedSeq: summary.liveStateSeq ?? 0,
73033
+ at,
73034
+ state: "working",
73035
+ bound: hosting.until,
73036
+ detail: hostedStageDetail(stage),
73037
+ statePatch: { hosting },
73038
+ sourceEvents
73039
+ });
73040
+ if (!committed.ok || !deps.registry.commitLiveState(runId3, committed))
73041
+ throw new Error(
73042
+ `hosted live state refresh was not committed (${committed.ok ? "registry sequence" : committed.reason})`
73043
+ );
73044
+ } else {
73045
+ ledgerRun?.setState({ hosting });
73046
+ }
73047
+ for (const event of events) deps.registry.publish(runId3, event);
71982
73048
  }
71983
73049
  function namesForeignRun(instance, body) {
71984
73050
  return typeof body.runId === "string" && body.runId !== instance.runId;
@@ -72064,7 +73130,7 @@ async function unitStart(body, deps) {
72064
73130
  row2 = { ...row2, startedAt: row2.startedAt ?? at };
72065
73131
  await deps.instances.putUnits([row2]);
72066
73132
  if (host.kind === "host")
72067
- hostPublish(
73133
+ await hostPublish(
72068
73134
  deps,
72069
73135
  instance,
72070
73136
  host.runId,
@@ -72113,7 +73179,7 @@ async function branch(body, deps) {
72113
73179
  await deps.createBranchRef(instance.repo, name, base);
72114
73180
  return json3(200, { ok: true, branch: name, base, at });
72115
73181
  } catch (err2) {
72116
- return json3(200, { ok: false, reason: describe11(err2), at });
73182
+ return json3(200, { ok: false, reason: describe12(err2), at });
72117
73183
  }
72118
73184
  }
72119
73185
  function unitLines(units, generated, severity) {
@@ -72194,7 +73260,7 @@ async function round(body, deps) {
72194
73260
  await deps.instances.putUnits([updated]);
72195
73261
  if (host.kind === "host") {
72196
73262
  const thread = unitThread(instance, updated, units.length);
72197
- hostPublish(
73263
+ await hostPublish(
72198
73264
  deps,
72199
73265
  instance,
72200
73266
  host.runId,
@@ -72228,7 +73294,7 @@ async function round(body, deps) {
72228
73294
  instance,
72229
73295
  units.map((u) => u.unit === updated.unit ? updated : u)
72230
73296
  ).catch(
72231
- (err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${describe11(err2)}`)
73297
+ (err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${describe12(err2)}`)
72232
73298
  );
72233
73299
  return json3(200, { ok: true, at });
72234
73300
  }
@@ -72402,7 +73468,7 @@ async function unitWake(body, deps) {
72402
73468
  instance,
72403
73469
  units.map((u) => u.unit === visible.unit ? visible : u)
72404
73470
  ).catch(
72405
- (err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${describe11(err2)}`)
73471
+ (err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${describe12(err2)}`)
72406
73472
  );
72407
73473
  return json3(200, { ok: true, answer, at });
72408
73474
  }
@@ -72465,7 +73531,7 @@ async function unitEnd(body, deps) {
72465
73531
  deps.runnerOwnership?.release(instance.repo, updated.pr.number);
72466
73532
  const thread = unitThread(instance, updated, units.length);
72467
73533
  if (host.kind === "host")
72468
- hostPublish(
73534
+ await hostPublish(
72469
73535
  deps,
72470
73536
  instance,
72471
73537
  host.runId,
@@ -72496,7 +73562,7 @@ async function unitEnd(body, deps) {
72496
73562
  `unit-end:${row2.unit}`
72497
73563
  ).catch(
72498
73564
  (err2) => (deps.log ?? console.warn)(
72499
- `[coordinator] ${instance.id} ${row2.unit}: the leftover marks failed: ${describe11(err2)}`
73565
+ `[coordinator] ${instance.id} ${row2.unit}: the leftover marks failed: ${describe12(err2)}`
72500
73566
  )
72501
73567
  );
72502
73568
  const freshTurn = {
@@ -72514,7 +73580,7 @@ async function unitEnd(body, deps) {
72514
73580
  };
72515
73581
  void deps.dispatch(freshTurn, io).catch(
72516
73582
  (err2) => (deps.log ?? console.warn)(
72517
- `[coordinator] ${instance.id} ${row2.unit}: the leftovers' fresh turn threw: ${describe11(err2)}`
73583
+ `[coordinator] ${instance.id} ${row2.unit}: the leftovers' fresh turn threw: ${describe12(err2)}`
72518
73584
  )
72519
73585
  );
72520
73586
  (deps.log ?? console.log)(
@@ -72531,7 +73597,7 @@ async function unitEnd(body, deps) {
72531
73597
  told = true;
72532
73598
  } catch (err2) {
72533
73599
  (deps.log ?? console.warn)(
72534
- `[coordinator] ${instance.id} ${row2.unit}: the report could not be posted: ${describe11(err2)}`
73600
+ `[coordinator] ${instance.id} ${row2.unit}: the report could not be posted: ${describe12(err2)}`
72535
73601
  );
72536
73602
  }
72537
73603
  }
@@ -72545,7 +73611,7 @@ ${ending.report}` + (rendered !== void 0 ? `
72545
73611
  ${rendered}` : "");
72546
73612
  await deps.github.commentIssue(instance.repo, row2.issue, comment).catch(
72547
73613
  (err2) => (deps.log ?? console.warn)(
72548
- `[coordinator] ${instance.id} ${row2.unit}: the board comment could not be posted: ${describe11(err2)}`
73614
+ `[coordinator] ${instance.id} ${row2.unit}: the board comment could not be posted: ${describe12(err2)}`
72549
73615
  )
72550
73616
  );
72551
73617
  }
@@ -72556,7 +73622,7 @@ ${rendered}` : "");
72556
73622
 
72557
73623
  ${ending.report}`).catch(
72558
73624
  (err2) => (deps.log ?? console.warn)(
72559
- `[coordinator] ${instance.id} ${row2.unit}: the human-gated report could not be posted on the pull request: ${describe11(err2)}`
73625
+ `[coordinator] ${instance.id} ${row2.unit}: the human-gated report could not be posted on the pull request: ${describe12(err2)}`
72560
73626
  )
72561
73627
  );
72562
73628
  }
@@ -72618,7 +73684,7 @@ async function rebaseStep(body, deps) {
72618
73684
  at
72619
73685
  });
72620
73686
  } catch (err2) {
72621
- return json3(200, { ok: true, outcome: "refused", reason: describe11(err2), at });
73687
+ return json3(200, { ok: true, outcome: "refused", reason: describe12(err2), at });
72622
73688
  }
72623
73689
  }
72624
73690
  async function merge(body, deps, subject2) {
@@ -72656,7 +73722,7 @@ async function merge(body, deps, subject2) {
72656
73722
  try {
72657
73723
  facts2 = await deps.fetchPrFacts(pr);
72658
73724
  } catch (err2) {
72659
- return json3(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
73725
+ return json3(502, { ok: false, error: "github_unavailable", message: describe12(err2), at });
72660
73726
  }
72661
73727
  if (facts2 === void 0)
72662
73728
  return json3(502, { ok: false, error: "github_unavailable", message: `${where} could not be read`, at });
@@ -72782,7 +73848,7 @@ async function merge(body, deps, subject2) {
72782
73848
  try {
72783
73849
  queued = await deps.enqueuePullRequest(pr, { sha: headSha });
72784
73850
  } catch (err2) {
72785
- return json3(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
73851
+ return json3(502, { ok: false, error: "github_unavailable", message: describe12(err2), at });
72786
73852
  }
72787
73853
  if (!queued.ok) return refused7(`GitHub refused to enqueue ${where}: ${queued.reason}`);
72788
73854
  log(
@@ -72799,7 +73865,7 @@ async function merge(body, deps, subject2) {
72799
73865
  title: facts2.title ?? `Merge pull request #${pr.number}`
72800
73866
  });
72801
73867
  } catch (err2) {
72802
- return json3(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
73868
+ return json3(502, { ok: false, error: "github_unavailable", message: describe12(err2), at });
72803
73869
  }
72804
73870
  if (!merged.ok) {
72805
73871
  if (merged.status === 405 && MERGE_QUEUE_405.test(merged.reason)) return enqueue();
@@ -72910,13 +73976,54 @@ async function finish(body, deps) {
72910
73976
  if (host.kind === "not_host") return json3(409, { ok: false, error: "not_host", at });
72911
73977
  const units = await deps.instances.listUnits(instance.id);
72912
73978
  if (host.kind === "host") {
72913
- hostPublish(
72914
- deps,
72915
- instance,
72916
- host.runId,
72917
- [{ type: "answer", text: planSummary(units, isGenerated2(instance)), at }],
72918
- at
72919
- );
73979
+ const ledgerRun = deps.ledgerRuns().find((run2) => run2.runId === host.runId);
73980
+ let summary = deps.registry.getById(host.runId);
73981
+ if (ledgerRun && summary) {
73982
+ if (summary.liveState === void 0) {
73983
+ await hostPublish(deps, instance, host.runId, [], at);
73984
+ summary = deps.registry.getById(host.runId);
73985
+ if (!summary) throw new Error("hosted run disappeared during finish backfill");
73986
+ }
73987
+ const bound = summary.liveState?.bound ?? at + minutesToMs(HOSTED_DEADLINE_MARGIN_MINUTES);
73988
+ const wrapping = await ledgerRun.assignLiveState({
73989
+ expectedSeq: summary.liveStateSeq ?? 0,
73990
+ eventSeq: summary.eventCount + 1,
73991
+ at,
73992
+ state: "wrapping_up",
73993
+ bound,
73994
+ detail: "writing the pipeline summary"
73995
+ });
73996
+ if (!wrapping.ok || !deps.registry.commitLiveState(host.runId, wrapping))
73997
+ throw new Error(`hosted wrap-up was not committed (${wrapping.ok ? "registry sequence" : wrapping.reason})`);
73998
+ summary = deps.registry.getById(host.runId);
73999
+ if (!summary) throw new Error("hosted run disappeared during wrap-up");
74000
+ const answer = { type: "answer", text: planSummary(units, isGenerated2(instance)), at };
74001
+ const answerSeq = summary.eventCount + 1;
74002
+ const refreshed = await ledgerRun.assignLiveState({
74003
+ expectedSeq: summary.liveStateSeq ?? 0,
74004
+ at,
74005
+ state: "wrapping_up",
74006
+ bound,
74007
+ detail: "writing the pipeline summary",
74008
+ sourceEvents: [{ ...answer, seq: answerSeq }]
74009
+ });
74010
+ if (!refreshed.ok || !deps.registry.commitLiveState(host.runId, refreshed))
74011
+ throw new Error(`hosted summary was not committed (${refreshed.ok ? "registry sequence" : refreshed.reason})`);
74012
+ deps.registry.publish(host.runId, answer);
74013
+ summary = deps.registry.getById(host.runId);
74014
+ if (!summary) throw new Error("hosted run disappeared before ending");
74015
+ const ended = await ledgerRun.assignLiveState({
74016
+ expectedSeq: summary.liveStateSeq ?? 0,
74017
+ eventSeq: summary.eventCount + 1,
74018
+ at,
74019
+ state: "ended",
74020
+ cause: body.outcome === "completed" ? "completed" : "failed"
74021
+ });
74022
+ if (!ended.ok || !deps.registry.commitLiveState(host.runId, ended))
74023
+ throw new Error(`hosted ending was not committed (${ended.ok ? "registry sequence" : ended.reason})`);
74024
+ } else {
74025
+ deps.registry.publish(host.runId, { type: "answer", text: planSummary(units, isGenerated2(instance)), at });
74026
+ }
72920
74027
  deps.registry.finish(host.runId, body.outcome);
72921
74028
  }
72922
74029
  await drawCard(deps, instance, units, { icon: body.outcome === "completed" ? "\u2705" : "\u26A0\uFE0F" }).catch(() => {
@@ -72934,7 +74041,7 @@ ${planSummary(units)}`).catch(() => {
72934
74041
  const summary = deps.registry.getById(host.runId);
72935
74042
  const visibility = await deps.channelVisibilityOf(instance.channelId);
72936
74043
  const finishedAt = snap?.finishedAt ?? at;
72937
- const record2 = assembleRunRecord({
74044
+ const record3 = assembleRunRecord({
72938
74045
  run: { id: host.runId, ...summary?.label !== void 0 ? { label: summary.label } : {} },
72939
74046
  snap,
72940
74047
  agent: "ship",
@@ -72964,7 +74071,7 @@ ${planSummary(units)}`).catch(() => {
72964
74071
  seal
72965
74072
  });
72966
74073
  const handle2 = deps.ledgerRuns().find((run2) => run2.runId === host.runId);
72967
- deps.runHistoryWriter.write(record2, handle2 !== void 0 ? { via: handle2.sink } : void 0);
74074
+ deps.runHistoryWriter.write(record3, handle2 !== void 0 ? { via: handle2.sink } : void 0);
72968
74075
  }
72969
74076
  (deps.log ?? console.log)(`[coordinator] ${instance.id}: finished ${body.outcome} \u2014 record ${runId3}`);
72970
74077
  return json3(200, { ok: true, runId: runId3, at });
@@ -73078,18 +74185,20 @@ function createAdminCoordinatorHandler(deps) {
73078
74185
  }
73079
74186
  write(res, await answerCoordinatorStep(door, read.body, deps));
73080
74187
  } catch (err2) {
73081
- (deps.log ?? console.error)(`[coordinator] ${describe11(err2)}`);
74188
+ (deps.log ?? console.error)(`[coordinator] ${describe12(err2)}`);
73082
74189
  write(res, json3(500, { ok: false, error: "internal error" }));
73083
74190
  }
73084
74191
  })();
73085
74192
  };
73086
74193
  }
73087
- var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json3, describe11, isGenerated2, EVERY_RUN, REVIEW_POSTED_CHECKS, REVIEW_POSTED_RECHECK_MS, githubSecondStart, CHILD_SUPERSESSION_REASONS, ROUND_OUTCOMES, ENDING_ICON, STEPS;
74194
+ var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json3, describe12, isGenerated2, EVERY_RUN, REVIEW_POSTED_CHECKS, REVIEW_POSTED_RECHECK_MS, githubSecondStart, CHILD_SUPERSESSION_REASONS, ROUND_OUTCOMES, ENDING_ICON, STEPS;
73088
74195
  var init_adminCoordinator = __esm({
73089
74196
  "../../src/channels/adminCoordinator.ts"() {
73090
74197
  "use strict";
73091
74198
  init_budgets();
73092
74199
  init_verbosity();
74200
+ init_prDescription();
74201
+ init_reply();
73093
74202
  init_registry();
73094
74203
  init_authorize();
73095
74204
  init_actor();
@@ -73131,7 +74240,7 @@ var init_adminCoordinator = __esm({
73131
74240
  };
73132
74241
  invalid = (error) => ({ ok: false, error });
73133
74242
  json3 = (status3, body) => ({ status: status3, body });
73134
- describe11 = (err2) => err2 instanceof Error ? err2.message : String(err2);
74243
+ describe12 = (err2) => err2 instanceof Error ? err2.message : String(err2);
73135
74244
  isGenerated2 = (instance) => instance.plan?.path === void 0;
73136
74245
  EVERY_RUN = { kind: "all" };
73137
74246
  REVIEW_POSTED_CHECKS = 3;
@@ -74366,6 +75475,13 @@ async function runBot() {
74366
75475
  if (process.env.PORT) {
74367
75476
  const ingress = createIngressHandler(deps, { auth, publicBaseUrl: process.env.PUBLIC_BASE_URL, personByEmail });
74368
75477
  const mcp2 = createMcpHandler(deps, { auth, commands, grantsFor: (id) => config.grantsFor(id), personByEmail });
75478
+ const providerLiveState = createProviderLiveStateCoordinator({
75479
+ runLedger,
75480
+ ledger: ledgerClient,
75481
+ registry: defaultRunRegistry,
75482
+ clock: systemClock,
75483
+ warn: (message4) => console.warn(`[provider-state] ${message4}`)
75484
+ });
74369
75485
  const modelProxy = createModelProxyHandler({
74370
75486
  bearers: runBearers,
74371
75487
  providers: () => config.config.providers,
@@ -74374,17 +75490,11 @@ async function runBot() {
74374
75490
  ...costsCfg?.prices ? { prices: () => costsCfg.prices } : {},
74375
75491
  secrets: processSecrets,
74376
75492
  clock: systemClock,
74377
- // The plane's provider seam (model-proxy item 12a; record 0064): a level
74378
- // and a park land on the write-through's plane routes, fire and forget —
74379
- // the null write-through swallows both where no ledger is configured.
75493
+ // The plane's provider seam (model-proxy item 12a; record 0064): one
75494
+ // coordinator sequences level and park writes; the proxy never waits.
74380
75495
  plane: {
74381
- level: (provider, side, cause) => void runLedger.planeLevel({
74382
- provider,
74383
- name: "provider",
74384
- side,
74385
- ...side === "down" && cause !== void 0 ? { cause } : {}
74386
- }),
74387
- park: (runId3, provider) => void runLedger.planePark(runId3, provider)
75496
+ level: (provider, side, cause) => void providerLiveState.level(provider, side, cause),
75497
+ park: (runId3, provider) => void providerLiveState.park(runId3, provider)
74388
75498
  }
74389
75499
  });
74390
75500
  const harnessRoutes = createHarnessRoutesHandler({ bearers: runBearers, harnesses, takeover });
@@ -75128,6 +76238,7 @@ var init_index = __esm({
75128
76238
  init_adminModelProxy();
75129
76239
  init_modelProxy();
75130
76240
  init_runBearers();
76241
+ init_providerLiveStateCoordinator();
75131
76242
  init_piHarness();
75132
76243
  init_harness2();
75133
76244
  init_relay();