lobstah 0.6.5 → 0.6.6

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/main.js CHANGED
@@ -3893,11 +3893,19 @@ function askTrapTitle(trapId, title) {
3893
3893
  atomicWrite4(regPath(trapId), JSON.stringify(next, null, 2));
3894
3894
  return next;
3895
3895
  }
3896
+ function countTitleReminder(trapId) {
3897
+ const reg = readTrap(trapId);
3898
+ if (!reg?.titlePending)
3899
+ return void 0;
3900
+ const n = (reg.titleReminders ?? 0) + 1;
3901
+ atomicWrite4(regPath(trapId), JSON.stringify({ ...reg, titleReminders: n }, null, 2));
3902
+ return n;
3903
+ }
3896
3904
  function confirmTrapTitle(trapId, now2 = Date.now()) {
3897
3905
  const reg = readTrap(trapId);
3898
3906
  if (!reg)
3899
3907
  return void 0;
3900
- const { titlePending, ...rest } = reg;
3908
+ const { titlePending, titleReminders: _reminders, ...rest } = reg;
3901
3909
  const next = { ...rest, titleSet: titlePending ?? reg.titleSet ?? reg.name, titleSetAt: new Date(now2).toISOString() };
3902
3910
  atomicWrite4(regPath(trapId), JSON.stringify(next, null, 2));
3903
3911
  return next;
@@ -4160,7 +4168,7 @@ function beatTrap(input) {
4160
4168
  writeActivity(reg.claimed, laneOf(reg.claimed) ?? "work", { at, kind: "tool", summary: toolSummary(name, toolTarget(input.toolInput), reg.worktree) });
4161
4169
  return { beat: true, trapId, activityFor: reg.claimed };
4162
4170
  }
4163
- var TRAP_FILE, TRAP_ANCHOR_FILE, TRAP_ADDRESS_RE, signedOffDir, signedOffPath, msOf;
4171
+ var TRAP_FILE, TRAP_ANCHOR_FILE, TRAP_ADDRESS_RE, signedOffDir, signedOffPath, TITLE_REMINDERS, msOf;
4164
4172
  var init_soak = __esm({
4165
4173
  "packages/core/dist/soak.js"() {
4166
4174
  "use strict";
@@ -4183,6 +4191,7 @@ var init_soak = __esm({
4183
4191
  TRAP_ADDRESS_RE = /\bwt:([a-z0-9][a-z0-9-]*)/g;
4184
4192
  signedOffDir = () => path18.join(path18.dirname(soakingDir()), "signed-off");
4185
4193
  signedOffPath = (trapId) => path18.join(signedOffDir(), `${trapId}.json`);
4194
+ TITLE_REMINDERS = 3;
4186
4195
  msOf = (iso) => Date.parse(iso) || 0;
4187
4196
  }
4188
4197
  });
@@ -5403,10 +5412,10 @@ function repairKind(pr, gates = []) {
5403
5412
  function repairLimit(n) {
5404
5413
  return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 2;
5405
5414
  }
5406
- function pushRule(branch, id = "<dispatch id>", mode = "rebase") {
5415
+ function pushRule(branch, id = "<dispatch id>", mode = "rebase", also = []) {
5407
5416
  const b = branch ?? "the PR's head branch";
5408
5417
  return [
5409
- `Push only to the existing branch ${b}.`,
5418
+ also.length ? `Push only to the existing branch ${b} and the branches of the PRs stacked on it named below.` : `Push only to the existing branch ${b}.`,
5410
5419
  mode === "merge" ? `If the push is rejected as non-fast-forward because ${b} moved, fetch ${b}, merge the moved head into your branch, and push again with a normal push. Retry at most ${PUSH_RETRIES} times. Never force-push.` : `If the push is rejected as non-fast-forward because ${b} moved, fetch ${b}, rebase your commits onto the moved head again, and push with \`--force-with-lease=${b}:<the head you just fetched>\`. Retry at most ${PUSH_RETRIES} times.`,
5411
5420
  "If a push hook fails with a real test or type error, do not retry the push: fix the error, commit, and push again.",
5412
5421
  `If you still cannot push, report \`lobstah report ${id} failed "${PUSH_REJECTED} <rejection text>; moved head <full sha of the head you fetched>"\` and leave the PR as it was.`,
@@ -5421,6 +5430,13 @@ function conflictUpdate(base, branch, standalone) {
5421
5430
  const head = branch ?? "the PR's head branch";
5422
5431
  return standalone ? `Fetch origin/${b} and merge it into ${head} with a merge commit (\`git merge origin/${b}\`), resolving the conflicts in that merge. Do not rebase, and do not force-push: push the merge commit with a normal push.` : `This PR is stacked on its base branch ${b}. Fetch origin/${b} and rebase ${head} onto it, resolving the conflicts. Push with \`--force-with-lease=${head}:<the head you started from>\`.`;
5423
5432
  }
5433
+ function stackUpdate(above, mode) {
5434
+ if (above.length === 0)
5435
+ return "";
5436
+ const list2 = above.map((p) => `#${p.number} (${p.url}, branch ${p.headRefName} on ${p.baseRefName})`).join(", ");
5437
+ const how = mode === "merge" ? "fetch origin, check out its branch, merge its updated base into it with a merge commit (`git merge origin/<its base>`), and push with a normal push. Never force-push." : `fetch origin, check out its branch, rebase it onto its updated base (\`git rebase --onto origin/<its base> <its base's head before your push> <its branch>\`), and push with \`--force-with-lease=<its branch>:<its head you started from>\`; on a non-fast-forward rejection, fetch and redo the rebase, at most ${PUSH_RETRIES} times.`;
5438
+ return `PRs are stacked on this PR: ${list2}. After you push this PR, bring each of them up to date with its base, in that order: ${how} If an update conflicts, abort it (\`git merge --abort\` or \`git rebase --abort\`), leave that PR and the PRs above it as they were, and name them in your report note: lobstah repairs a conflicting PR as its own repair.`;
5439
+ }
5424
5440
  function prReviewed(pr) {
5425
5441
  return pr.reviewDecision === "APPROVED" || pr.reviewDecision === "CHANGES_REQUESTED" || pr.review?.lastReviewAt !== void 0 || pr.review?.changesRequested === true;
5426
5442
  }
@@ -5436,7 +5452,8 @@ function repairBrief(pr, kind, arg = {}) {
5436
5452
  const resolution = "For code already on main, take main's version. Keep only this PR's own changes. Never change behavior. If resolving a conflict would change code behavior, stop and report needs-decision.";
5437
5453
  const standalone = standalonePr(pr.baseRefName, opts.trunk);
5438
5454
  const report = prReviewed(pr) ? rerequestReview(pr, opts.id) : "Report done with the same PR URL.";
5439
- const finish = (mode = "rebase") => `Run the relevant tests. ${pushRule(pr.headRefName, opts.id, mode)} ${report}`;
5455
+ const above = opts.above ?? [];
5456
+ const finish = (mode = "rebase", stackMode = mode) => `Run the relevant tests. ${pushRule(pr.headRefName, opts.id, mode, above.map((p) => p.headRefName))}${above.length ? ` ${stackUpdate(above, stackMode)}` : ""} ${report}`;
5440
5457
  if (kind === "conflict")
5441
5458
  return `${intro}
5442
5459
  ${resolution} ${conflictUpdate(pr.baseRefName, pr.headRefName, standalone)} ${finish(standalone ? "merge" : "rebase")}`;
@@ -5450,10 +5467,10 @@ These failing checks are human gates. They pass only when a person approves. Do
5450
5467
  ${resolution}
5451
5468
  Latest failing checks:
5452
5469
  ${checks || "- Read the failing check from GitHub"}${gates}
5453
- Read each check log. Fix a real failure. If it is a flake, rerun it at most once. If a check cannot pass until a person approves the change, it is a human gate: do not change code for it, and name it on your report with --human-gate "<check name>", once per check. ${finish()}`;
5470
+ Read each check log. Fix a real failure. If it is a flake, rerun it at most once. If a check cannot pass until a person approves the change, it is a human gate: do not change code for it, and name it on your report with --human-gate "<check name>", once per check. ${finish("rebase", "merge")}`;
5454
5471
  }
5455
5472
  return `${intro}
5456
- ${resolution} Read the requested review changes and comments with gh pr view --comments. Address the feedback. If a comment needs a person's decision, report needs-decision instead of guessing. ${finish()}`;
5473
+ ${resolution} Read the requested review changes and comments with gh pr view --comments. Address the feedback. If a comment needs a person's decision, report needs-decision instead of guessing. ${finish("rebase", "merge")}`;
5457
5474
  }
5458
5475
  var PUSH_RETRIES, PUSH_REJECTED;
5459
5476
  var init_pr_repair = __esm({
@@ -10561,6 +10578,20 @@ function prsBelow(records, key) {
10561
10578
  }
10562
10579
  return out;
10563
10580
  }
10581
+ function prsAbove(records, key) {
10582
+ const { prs } = deriveGlassPrs([], [], records);
10583
+ const parent = stackParents(prs);
10584
+ const out = [];
10585
+ const seen = /* @__PURE__ */ new Set([key]);
10586
+ let level = [key];
10587
+ while (level.length > 0) {
10588
+ const next = prs.filter((p) => p.state === "OPEN" && !seen.has(p.key) && level.includes(parent.get(p.key)?.key ?? "")).sort((a, b) => a.number - b.number);
10589
+ for (const p of next) seen.add(p.key);
10590
+ out.push(...next);
10591
+ level = next.map((p) => p.key);
10592
+ }
10593
+ return out;
10594
+ }
10564
10595
  function dispatchPrList(urls, prs) {
10565
10596
  const byKey = new Map(prs.map((p) => [parsePrRef(p.url)?.key ?? p.url, p]));
10566
10597
  const rows = urls.flatMap((u, i) => {
@@ -10870,6 +10901,16 @@ function progressStreak(pr) {
10870
10901
  if (!own && s.startHead !== void 0 && s.startHead !== pr.headSha) return { count: 0 };
10871
10902
  return { count: s.count + 1 };
10872
10903
  }
10904
+ function stackedAbove(pr, records, workers) {
10905
+ const out = [];
10906
+ for (const p of prsAbove(records, pr.key)) {
10907
+ if (!p.headRefName || !p.baseRefName) break;
10908
+ const rec = records.find((r) => r.key === p.key);
10909
+ if (rec && repairHold({ ...rec }, [], workers)) break;
10910
+ out.push({ url: p.url, number: p.number, headRefName: p.headRefName, baseRefName: p.baseRefName });
10911
+ }
10912
+ return out;
10913
+ }
10873
10914
  function trapAddressOf(id) {
10874
10915
  const claim = readSessionClaim(id, "work");
10875
10916
  const deliveredTo = readEvidence(id, "work").deliveredTo;
@@ -10984,6 +11025,12 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
10984
11025
  (workers ??= (opts.workerHolds ?? readWorkerHolds)()).filter((w2) => !(w2.dispatchId === void 0 && w2.trapId !== void 0 && `wt:${w2.trapId}` === ownerTrap))
10985
11026
  );
10986
11027
  if (held) return wait(held);
11028
+ for (const below of prsBelow(records, pr.key)) {
11029
+ const r = readPr(below.key)?.repair;
11030
+ if (r?.status === "repairing" && r.dispatchId && !isTerminal2(r.dispatchId)) {
11031
+ return wait({ heldBy: "stack", reason: `a repair of #${below.number} below this PR also updates it (dispatch ${r.dispatchId.slice(0, 8)})` });
11032
+ }
11033
+ }
10987
11034
  const changed = Math.max(
10988
11035
  Date.parse(pr.headSince ?? pr.observedAt) || now2,
10989
11036
  Date.parse(pr.baseSince ?? pr.observedAt) || now2,
@@ -11060,6 +11107,7 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
11060
11107
  id,
11061
11108
  gates,
11062
11109
  trunk: config.repos[target.repo]?.trunk,
11110
+ above: stackedAbove(pr, records ??= readPrs(), workers ?? []),
11063
11111
  ...kind === "checks" && fresh.length > 0 ? { checks: fresh } : {}
11064
11112
  }),
11065
11113
  config.repos[target.repo],
@@ -12429,8 +12477,8 @@ function sessionGrounds(cwd) {
12429
12477
  if (owned) return { grounds: owned };
12430
12478
  return { placeholder: `<${all.map((g) => g.name).join("|")}>` };
12431
12479
  }
12432
- function titleReminder(title) {
12433
- return `lobstah: sign-on is not complete. Apply this title: ${title}. Then run \`lobstah trap title-set\`.`;
12480
+ function titleReminder(title, last = false) {
12481
+ return `lobstah: sign-on is not complete. Apply this title: ${title} (skip this if you have no tool that sets the session title). Then run \`lobstah soak title-set\`.` + (last ? " This is the last reminder: if the command cannot run here, carry on; lobstah stops asking." : "");
12434
12482
  }
12435
12483
  async function buildBriefContext(sessionId, cwd) {
12436
12484
  let fleet = "";
@@ -12457,7 +12505,7 @@ async function buildBriefContext(sessionId, cwd) {
12457
12505
  }
12458
12506
  const workerTrap = trapBySession(sessionId);
12459
12507
  if (workerTrap) {
12460
- const title = workerTrap.titlePending ? `
12508
+ const title = workerTrap.titlePending && (workerTrap.titleReminders ?? 0) < TITLE_REMINDERS ? `
12461
12509
  ${titleReminder(workerTrap.titlePending)}` : "";
12462
12510
  return `lobstah: session id ${sessionId} \u2014 this session mans trap ${trapLabel(workerTrap)} (it takes assigned work at turn end) and works in ${workerTrap.worktree}; \`lobstah stow\` signs it off.${fleet}${title}`;
12463
12511
  }
@@ -14082,7 +14130,7 @@ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from
14082
14130
  import { fileURLToPath as fileURLToPath3 } from "node:url";
14083
14131
 
14084
14132
  // apps/cli/src/glass-page.generated.ts
14085
- var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:10;padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.lob .sprite{width:72px;height:56px;background:url(/lob-sprite.png) 0 0 no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .fallback{display:inline-block;animation:waddle 0.45s ease-in-out infinite alternate}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.lob .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}@keyframes waddle{from{transform:rotate(-8deg) translateY(0)}to{transform:rotate(8deg) translateY(-3px)}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dcards{display:flex;flex-direction:column;gap:10px}.dcard{width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.dcard.focus{animation:dflash 1.6s ease-out 1}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dcard.answered{opacity:0.7}.dcard .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2)}.dcard .dtop .badge{margin-top:0}.dcard .dtitle{white-space:pre-wrap;overflow-wrap:anywhere}.dcard .dmeta{font-size:11px;margin-left:auto}.dcard .dlink{color:var(--link)}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;overflow-x:auto;white-space:pre}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{max-width:1100px;margin:0 auto;padding:20px 16px 48px}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var Ae,b,Bt,Bs,O,Nt,Ot,qt,Je,Ge,le,Ft,Ze,Xe,Qe,Os,xe={},Ce=[],qs=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Re=Array.isArray;function E(e,t){for(var n in t)e[n]=t[n];return e}function Ye(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Q(e,t,n){var s,r,i,o={};for(i in t)i=="key"?s=t[i]:i=="ref"?r=t[i]:o[i]=t[i];if(arguments.length>2&&(o.children=arguments.length>3?Ae.call(arguments,2):n),typeof e=="function"&&e.defaultProps!=null)for(i in e.defaultProps)o[i]===void 0&&(o[i]=e.defaultProps[i]);return Te(e,o,s,r,null)}function Te(e,t,n,s,r){var i={type:e,props:t,key:n,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:r??++Bt,__i:-1,__u:0};return r==null&&b.vnode!=null&&b.vnode(i),i}function Z(e){return e.children}function Se(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type=="function"?V(e):null}function Fs(e){if(e.__P&&e.__d){var t=e.__v,n=t.__e,s=[],r=[],i=E({},t);i.__v=t.__v+1,b.vnode&&b.vnode(i),et(e.__P,i,t,e.__n,e.__P.namespaceURI,32&t.__u?[n]:null,s,n??V(t),!!(32&t.__u),r),i.__v=t.__v,i.__.__k[i.__i]=i,Jt(s,i,r),t.__e=t.__=null,i.__e!=n&&Wt(i)}}function Wt(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Wt(e)}function Ht(e){(!e.__d&&(e.__d=!0)&&O.push(e)&&!De.__r++||Nt!=b.debounceRendering)&&((Nt=b.debounceRendering)||Ot)(De)}function De(){try{for(var e,t=1;O.length;)O.length>t&&O.sort(qt),e=O.shift(),t=O.length,Fs(e)}finally{O.length=De.__r=0}}function Vt(e,t,n,s,r,i,o,c,u,l,f){var h,d,m,p,g,T,G=s&&s.__k||Ce,$=t.length;for(u=Ws(n,t,G,u,$),h=0;h<$;h++)(m=n.__k[h])!=null&&(d=m.__i!=-1&&G[m.__i]||xe,m.__i=h,T=et(e,m,d,r,i,o,c,u,l,f),p=m.__e,m.ref&&d.ref!=m.ref&&(d.ref&&tt(d.ref,null,m),f.push(m.ref,m.__c||p,m)),g==null&&p!=null&&(g=p),4&m.__u?(u=Kt(m,u,e),d.__e&&(d.__e=null)):typeof m.type=="function"&&T!==void 0?u=T:p&&(u=p.nextSibling),m.__u&=-7);return n.__e=g,u}function Ws(e,t,n,s,r){var i,o,c,u,l,f=n.length,h=f,d=0;for(e.__k=new Array(r),i=0;i<r;i++)(o=t[i])!=null&&typeof o!="boolean"&&typeof o!="function"?(typeof o=="string"||typeof o=="number"||typeof o=="bigint"||o.constructor==String?o=e.__k[i]=Te(null,o,null,null,null):Re(o)?o=e.__k[i]=Te(Z,{children:o},null,null,null):o.constructor===void 0&&o.__b>0?o=e.__k[i]=Te(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):e.__k[i]=o,u=i+d,o.__=e,o.__b=e.__b+1,c=null,(l=o.__i=Vs(o,n,u,h))!=-1&&(h--,(c=n[l])&&(c.__u|=2)),c==null||c.__v==null?(l==-1&&(r>f?d--:r<f&&d++),typeof o.type!="function"&&(o.__u|=4)):l!=u&&(l==u-1?d--:l==u+1?d++:(l>u?d--:d++,o.__u|=4))):e.__k[i]=null;if(h)for(i=0;i<f;i++)(c=n[i])!=null&&(2&c.__u)==0&&(c.__e==s&&(s=V(c)),Qt(c,c));return s}function Kt(e,t,n){var s,r;if(typeof e.type=="function"){for(s=e.__k,r=0;s&&r<s.length;r++)s[r]&&(s[r].__=e,t=Kt(s[r],t,n));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=n.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function Vs(e,t,n,s){var r,i,o,c=e.key,u=e.type,l=t[n],f=l!=null&&(2&l.__u)==0;if(l===null&&c==null||f&&c==l.key&&u==l.type)return n;if(s>(f?1:0)){for(r=n-1,i=n+1;r>=0||i<t.length;)if((l=t[o=r>=0?r--:i++])!=null&&(2&l.__u)==0&&c==l.key&&u==l.type)return o}return-1}function Ut(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||qs.test(t)?n:n+"px"}function we(e,t,n,s,r){var i,o;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof s=="string"&&(e.style.cssText=s=""),s)for(t in s)n&&t in n||Ut(e.style,t,"");if(n)for(t in n)s&&n[t]==s[t]||Ut(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")i=t!=(t=t.replace(Ft,"$1")),o=t.toLowerCase(),t=o in e||t=="onFocusOut"||t=="onFocusIn"?o.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+i]=n,n?s?n[le]=s[le]:(n[le]=Ze,e.addEventListener(t,i?Qe:Xe,i)):e.removeEventListener(t,i?Qe:Xe,i);else{if(r=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&n==1?"":n))}}function jt(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t[Ge]==null)t[Ge]=Ze++;else if(t[Ge]<n[le])return;return n(b.event?b.event(t):t)}}}function et(e,t,n,s,r,i,o,c,u,l){var f,h,d,m,p,g,T,G,$,P,ie,W,ae,Et,ye,ze,I=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(u=!!(32&n.__u),i=[c=t.__e=n.__e]),(f=b.__b)&&f(t);e:if(typeof I=="function"){h=o.length;try{if($=t.props,P=I.prototype&&I.prototype.render,ie=(f=I.contextType)&&s[f.__c],W=f?ie?ie.props.value:f.__:s,n.__c?G=(d=t.__c=n.__c).__=d.__E:(P?t.__c=d=new I($,W):(t.__c=d=new Se($,W),d.constructor=I,d.render=zs),ie&&ie.sub(d),d.state||(d.state={}),d.__n=s,m=d.__d=!0,d.__h=[],d._sb=[]),P&&d.__s==null&&(d.__s=d.state),P&&I.getDerivedStateFromProps!=null&&(d.__s==d.state&&(d.__s=E({},d.__s)),E(d.__s,I.getDerivedStateFromProps($,d.__s))),p=d.props,g=d.state,d.__v=t,m)P&&I.getDerivedStateFromProps==null&&d.componentWillMount!=null&&d.componentWillMount(),P&&d.componentDidMount!=null&&d.__h.push(d.componentDidMount);else{if(P&&I.getDerivedStateFromProps==null&&$!==p&&d.componentWillReceiveProps!=null&&d.componentWillReceiveProps($,W),t.__v==n.__v||!d.__e&&d.shouldComponentUpdate!=null&&d.shouldComponentUpdate($,d.__s,W)===!1){t.__v!=n.__v&&(d.props=$,d.state=d.__s,d.__d=!1),t.__e=n.__e,t.__k=n.__k,t.__k.some(function(X){X&&(X.__=t)}),Ce.push.apply(d.__h,d._sb),d._sb=[],d.__h.length&&o.push(d),c=V(n);break e}d.componentWillUpdate!=null&&d.componentWillUpdate($,d.__s,W),P&&d.componentDidUpdate!=null&&d.__h.push(function(){d.componentDidUpdate(p,g,T)})}if(d.context=W,d.props=$,d.__P=e,d.__e=!1,ae=b.__r,Et=0,P)d.state=d.__s,d.__d=!1,ae&&ae(t),f=d.render(d.props,d.state,d.context),Ce.push.apply(d.__h,d._sb),d._sb=[];else do d.__d=!1,ae&&ae(t),f=d.render(d.props,d.state,d.context),d.state=d.__s;while(d.__d&&++Et<25);d.state=d.__s,d.getChildContext!=null&&(s=E(E({},s),d.getChildContext())),P&&!m&&d.getSnapshotBeforeUpdate!=null&&(T=d.getSnapshotBeforeUpdate(p,g)),ye=f!=null&&f.type===Z&&f.key==null?Xt(f.props.children):f,c=Vt(e,Re(ye)?ye:[ye],t,n,s,r,i,o,c,u,l),d.base=t.__e,t.__u&=-161,d.__h.length&&o.push(d),G&&(d.__E=d.__=null)}catch(X){if(o.length=h,t.__v=null,u||i!=null){if(X.then){for(t.__u|=u?160:128;c&&c.nodeType==8&&c.nextSibling;)c=c.nextSibling;i!=null&&(i[i.indexOf(c)]=null),t.__e=c}else if(i!=null)for(ze=i.length;ze--;)Ye(i[ze])}else t.__e=n.__e;t.__k==null&&(t.__k=n.__k||[]),X.then||zt(t),b.__e(X,t,n)}}else i==null&&t.__v==n.__v?(t.__k=n.__k,t.__e=n.__e):c=t.__e=Ks(n.__e,t,n,s,r,i,o,u,l);return(f=b.diffed)&&f(t),128&t.__u?void 0:c}function zt(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(zt))}function Jt(e,t,n){for(var s=0;s<n.length;s++)tt(n[s],n[++s],n[++s]);b.__c&&b.__c(t,e),e.some(function(r){try{e=r.__h,r.__h=[],e.some(function(i){i.call(r)})}catch(i){b.__e(i,r.__v)}})}function Xt(e){return typeof e!="object"||e==null||e.__b>0?e:Re(e)?e.map(Xt):e.constructor!==void 0?null:E({},e)}function Ks(e,t,n,s,r,i,o,c,u){var l,f,h,d,m,p,g,T=n.props||xe,G=t.props,$=t.type;if($=="svg"?r="http://www.w3.org/2000/svg":$=="math"?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),i!=null){for(l=0;l<i.length;l++)if((m=i[l])&&"setAttribute"in m==!!$&&($?m.localName==$:m.nodeType==3)){e=m,i[l]=null;break}}if(e==null){if($==null)return document.createTextNode(G);e=document.createElementNS(r,$,G.is&&G),c&&(b.__m&&b.__m(t,i),c=!1),i=null}if($==null)T===G||c&&e.data==G||(e.data=G);else{if(i=$=="textarea"&&G.defaultValue!=null?null:i&&Ae.call(e.childNodes),!c&&i!=null)for(T={},l=0;l<e.attributes.length;l++)T[(m=e.attributes[l]).name]=m.value;for(l in T)m=T[l],l=="dangerouslySetInnerHTML"?h=m:l=="children"||l in G||l=="value"&&"defaultValue"in G||l=="checked"&&"defaultChecked"in G||we(e,l,null,m,r);for(l in G)m=G[l],l=="children"?d=m:l=="dangerouslySetInnerHTML"?f=m:l=="value"?p=m:l=="checked"?g=m:c&&typeof m!="function"||T[l]===m||we(e,l,m,T[l],r);if(f)c||h&&(f.__html==h.__html||f.__html==e.innerHTML)||(e.innerHTML=f.__html),t.__k=[];else if(h&&(e.innerHTML=""),Vt(t.type=="template"?e.content:e,Re(d)?d:[d],t,n,s,$=="foreignObject"?"http://www.w3.org/1999/xhtml":r,i,o,i?i[0]:n.__k&&V(n,0),c,u),i!=null)for(l=i.length;l--;)Ye(i[l]);c&&$!="textarea"||(l="value",$=="progress"&&p==null?e.removeAttribute("value"):p!=null&&(p!==e[l]||$=="progress"&&!p||$=="option"&&p!=T[l])&&we(e,l,p,T[l],r),l="checked",g!=null&&g!=e[l]&&we(e,l,g,T[l],r))}return e}function tt(e,t,n){try{if(typeof e=="function"){var s=typeof e.__u=="function";s&&e.__u(),s&&t==null||(e.__u=e(t))}else e.current=t}catch(r){b.__e(r,n)}}function Qt(e,t,n){var s,r;if(b.unmount&&b.unmount(e),(s=e.ref)&&(s.current&&s.current!=e.__e||tt(s,null,t)),(s=e.__c)!=null){if(s.componentWillUnmount)try{s.componentWillUnmount()}catch(i){b.__e(i,t)}s.base=s.__P=s.__n=null}if(s=e.__k)for(r=0;r<s.length;r++)s[r]&&Qt(s[r],t,n||typeof e.type!="function");n||Ye(e.__e),e.__c=e.__=e.__e=void 0}function zs(e,t,n){return this.constructor(e,n)}function nt(e,t,n){var s,r,i,o;t==document&&(t=document.documentElement),b.__&&b.__(e,t),r=(s=typeof n=="function")?null:n&&n.__k||t.__k,i=[],o=[],et(t,e=(!s&&n||t).__k=Q(Z,null,[e]),r||xe,xe,t.namespaceURI,!s&&n?[n]:r?null:t.firstChild?Ae.call(t.childNodes):null,i,!s&&n?n:r?r.__e:t.firstChild,s,o),Jt(i,e,o),e.props.children=null}Ae=Ce.slice,b={__e:function(e,t,n,s){for(var r,i,o;t=t.__;)if((r=t.__c)&&!r.__)try{if((i=r.constructor)&&i.getDerivedStateFromError!=null&&(r.setState(i.getDerivedStateFromError(e)),o=r.__d),r.componentDidCatch!=null&&(r.componentDidCatch(e,s||{}),o=r.__d),o)return r.__E=r}catch(c){e=c}throw e}},Bt=0,Bs=function(e){return e!=null&&e.constructor===void 0},Se.prototype.setState=function(e,t){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=E({},this.state),typeof e=="function"&&(e=e(E({},n),this.props)),e&&E(n,e),e!=null&&this.__v&&(t&&this._sb.push(t),Ht(this))},Se.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),Ht(this))},Se.prototype.render=Z,O=[],Ot=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,qt=function(e,t){return e.__v.__b-t.__v.__b},De.__r=0,Je=Math.random().toString(8),Ge="__d"+Je,le="__a"+Je,Ft=/(PointerCapture)$|Capture$/i,Ze=0,Xe=jt(!1),Qe=jt(!0),Os=0;function Js(e){return{id:e,lane:"work",bucket:"done",repo:"",brief:"",attachments:[],messageAttachments:[],verb:"unknown",log:[],inbox:[],sort:0}}var Zt=new WeakMap;function st(e,t){let n=e.dispatches||[],s=Zt.get(n);return s||Zt.set(n,s=new Map(n.map(r=>[r.id,r]))),{...t,catches:(t.catches||[]).map(r=>s.get(r)??Js(r))}}function M(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var ce=["deck","dispatches","traps","prs","reports","notices"];function nn(e){let t=String(e||"").replace(/^#/,"");return ce.includes(t)?t:"deck"}var rt=e=>`#decision/${encodeURIComponent(e)}`;function ot(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var it=(e,t,n)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(n)}`,sn=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,rn=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),at=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function lt(e,t){let n=new Map(t.map(r=>[r.key,r])),s=[];for(let r of e)if(r.kind==="decision"){let i=n.get(r.key);if(!i)continue;s.push({kind:"decision",key:i.key,title:i.title,detail:i.detail,options:i.options,attachments:i.attachments,...i.dispatch?{dispatch:i.dispatch,lane:i.lane??"work"}:{},...i.repo?{repo:i.repo}:{},at:i.askedAt})}else r.kind==="question"&&s.push({kind:"question",key:r.key,verb:r.verb,note:r.note||r.verb,dispatch:r.id,lane:r.lane,...r.repo?{repo:r.repo}:{},at:r.at??""});return s.sort((r,i)=>i.at.localeCompare(r.at)||r.key.localeCompare(i.key))}function on(e){let t=(e.text??"").split(`\n`).map(n=>n.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function an(e,t){return(e.reports||[]).find(n=>n.key===`report:${t.lane}:${t.id}`)}function de(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var Yt=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),D=e=>`/report/${encodeURIComponent(e)}`;function ln(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function cn(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var dn=e=>`/report/${encodeURIComponent(e)}/md`,ct=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function en(e,t,n){return!!e&&n-Date.parse(e)>t}function tn(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let n=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.brief||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(n))return!1}return!0}function Y(e,t){if(!t)return null;if(t.type==="helm")return e.helms.find(s=>s.grounds===t.key)||null;if(t.type==="dispatch")return e.dispatches.find(s=>s.lane+":"+s.id===t.key)||null;if(t.type==="pr")return(e.prs||[]).find(s=>s.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(s=>s.trapId===t.key);return n?st(e,n):null}function pn(e,t,n){let s=t.st,r=String(s.q||"").toLowerCase(),i=(...p)=>!r||p.join(" ").toLowerCase().includes(r),o=p=>({x:p,stale:en(p.heartbeatAt,18e5,n)}),c=Y(e,t.modal),u=(p,g)=>!!p&&n-Date.parse(p)<=g,l=p=>Math.max(0,...(p.notices||[]).filter(g=>g.kind==="trap-stowed"||g.kind==="trap-ghosted").map(g=>Date.parse(g.at)||0)),f=p=>p.live||!!p.starting||!!p.requested,h=[...e.traps||[]].sort((p,g)=>Number(f(g))-Number(f(p))||Number(g.live)-Number(p.live)||(p.live?(Date.parse(p.signedOnAt||"")||0)-(Date.parse(g.signedOnAt||"")||0):l(g)-l(p))||(p.name??p.trapId).localeCompare(g.name??g.trapId)),d=h.filter(p=>(f(p)||(p.notices||[]).some(g=>(g.kind==="trap-stowed"||g.kind==="trap-ghosted")&&u(g.at,36e5)))&&i(p.name,p.trapId,p.repo,p.worktree)),m=({ageSecs:p,...g})=>g;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&en(e.daemon.heartbeat,9e4,n),helms:e.helms.map(o)},deck:{view:s.view,decisions:lt(e.attention||[],e.decisions||[]).filter(p=>p.kind==="decision"?i(p.kind,p.repo,p.title,p.dispatch,p.detail):i(p.kind,p.repo,p.note,p.dispatch)),prAttention:(e.attention||[]).filter(p=>p.kind&&p.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(p=>u(p.at,864e5)&&i(p.repo,p.note,p.id)).sort((p,g)=>Date.parse(g.at)-Date.parse(p.at)).slice(0,8),reports:(e.reports||[]).filter(p=>i(p.title,p.author,p.dispatch,p.repo)).sort(Yt),inflight:e.dispatches.filter(p=>p.bucket!=="done"&&tn(p,{...s,lane:"",repo:"",verb:""})),traps:d.map(p=>o(st(e,p))),stacks:(e.stacks||[]).filter(p=>p.open&&i(p.repo,p.numbers.join(" "))),prs:(e.prs||[]).filter(p=>p.state==="OPEN"),error:e.attentionError},dispatches:{view:s.view,chain:s.chain,list:e.dispatches.filter(p=>tn(p,s))},traps:{view:s.view,list:h.filter(p=>(!s.repo||p.repo===s.repo)&&i(p.name,p.trapId,p.repo,p.worktree,p.harness)).map(p=>o(st(e,p)))},prs:{view:s.view,stacks:(e.stacks||[]).filter(p=>!s.repo||p.repo===s.repo),prs:(e.prs||[]).filter(p=>(!s.repo||p.repo===s.repo)&&i(p.number,p.title,p.url,p.state,p.baseRefName,p.headRefName)),watches:(e.watches||[]).filter(p=>!String(p.key).startsWith("pr:"))},reports:{view:s.view,list:(e.reports||[]).filter(p=>(!s.repo||p.repo===s.repo)&&i(p.title,p.author,p.dispatch,p.repo)).sort(Yt)},notices:{list:(e.notices||[]).filter(p=>(!s.repo||!p.repo||p.repo===s.repo)&&(!s.noticeKind||p.kind===s.noticeKind)&&i(p.kind,p.text,p.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:c&&o(c),prefs:t.modal&&t.modal.type==="settings"?{view:s.view,lobs:s.lobs}:void 0}}}function un(e,t){let n=(e.prs||[]).find(c=>c.key===t);if(!n)return null;let s=(e.stacks||[]).find(c=>c.id===n.stackId),r=new Map((e.dispatches||[]).map(c=>[c.id,c])),i=(n.dispatchIds||[]).map(c=>{let u=r.get(c);return u?{id:c,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:c,culled:!0}}),o=n.watch;return{pr:n,stack:s?{numbers:s.numbers,titles:s.numbers.map(c=>(e.prs||[]).find(u=>u.stackId===s.id&&u.number===c)?.title||""),position:n.position+1,size:s.numbers.length,floor:s.floor,nextNumber:s.nextNumber,nextMergeable:!!n.nextMergeable,blockedBy:n.blockedBy}:null,chain:i,watch:o?{key:o.key,owner:o.owner||"",lastCheckedAt:o.lastCheckedAt||null,lastError:o.lastError||null,cursor:o.cursor}:null}}function fn(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function mn(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function hn(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function gn(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function vn(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var pe,_n=new Set,_=()=>pe;function dt(e){pe=e}function S(e){pe={...pe,...e};for(let t of _n)t(pe)}var pt=e=>{_n.add(e)};function ut(e){let{modal:t,drafts:n}=_(),s=new Set(lt(e.attention||[],e.decisions||[]).map(i=>i.key)),r=Object.fromEntries(Object.entries(n).filter(([i])=>s.has(i)));S({snapshot:e,stale:!1,modal:t&&Y(e,t)?t:null,...Object.keys(r).length!==Object.keys(n).length?{drafts:r}:{}})}var $n=()=>({text:"",files:[]});function A(e,t){let n=_().drafts;S({drafts:{...n,[e]:{...n[e]??$n(),...t}}})}function bn(e,t){let n=_().drafts[e]?.option;A(e,{option:n===t?void 0:t,error:void 0})}var Xs=e=>new Promise((t,n)=>{let s=new FileReader;s.onload=()=>t(String(s.result).replace(/^data:[^,]*,/,"")),s.onerror=()=>n(s.error),s.readAsDataURL(e)});async function ft(e,t){let n=_().snapshot?.answerLimits,s=[...t??[]],r=_().drafts[e]?.files??[],i=[],o=[];for(let c of s){let u=(/\\.[^.]+$/.exec(c.name)?.[0]??"").toLowerCase();if(n&&!n.extensions.includes(u))i.push(`${c.name}: type not accepted`);else if(n&&c.size>n.maxBytes)i.push(`${c.name}: larger than ${n.maxBytes} bytes`);else if(n&&r.length+o.length>=n.maxFiles)i.push(`${c.name}: at most ${n.maxFiles} files`);else try{o.push({name:c.name,bytes:c.size,data:await Xs(c)})}catch{i.push(`${c.name}: could not be read`)}}A(e,{files:[..._().drafts[e]?.files??[],...o],error:i.length?i.join("; "):void 0})}var Qs={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function kn(e,t){let n=[...t.clipboardData?.items??[]],s=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),r=n.filter(i=>i.kind==="file"&&i.type.startsWith("image/")).map(i=>i.getAsFile()).filter(i=>i!==null).map((i,o,c)=>{let u=`pasted-${s}${c.length>1?`-${o+1}`:""}${Qs[i.type]??".png"}`;return new File([i],u,{type:i.type})});r.length&&await ft(e,r)}function yn(e,t){let n=(_().drafts[e]?.files??[]).filter((s,r)=>r!==t);A(e,{files:n})}async function wn(e){let t=_().snapshot?.focusToken,n=_().drafts[e]??$n();if(!(n.sending||n.sent)){if(!n.option&&!n.text.trim()&&n.files.length===0){A(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){A(e,{error:"This page has no answer token; reload it."});return}A(e,{sending:!0,error:void 0});try{let s=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...n.option?{option:n.option}:{},...n.text.trim()?{text:n.text}:{},files:n.files.map(i=>({name:i.name,data:i.data}))}})}),r=await s.json();if(!s.ok||!r.ok){A(e,{sending:!1,error:r.reason??`The answer was refused (${s.status}).`});return}A(e,{sending:!1,sent:on({option:n.option,text:n.text,files:n.files.length})})}catch{A(e,{sending:!1,error:"The answer could not be sent."})}}}var Gn=()=>S({stale:!0}),Tn=(e,t)=>S({lightbox:{src:e,name:t}}),ee=()=>S({lightbox:null});function L(e,t){let{snapshot:n}=_(),s={type:e,key:t};S({modal:!n||Y(n,s)?s:null})}function te(){S({modal:null})}async function Sn(e){let t=_().snapshot?.focusToken;if(t)try{let s=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();S({focusResults:{..._().focusResults,[e]:s.message??s.reason??"Window focus failed."}})}catch{S({focusResults:{..._().focusResults,[e]:"Window focus failed."}})}}async function xn(e,t){let n=_().snapshot?.focusToken;if(!n)return"The glass has no page token yet.";try{let r=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":n},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!r.ok)return r.reason??"The request was refused."}catch{return"The request could not be sent."}try{ut(await(await fetch("/data")).json())}catch{}}function N(e){let t={..._().prefs,...e};hn(t),S({prefs:t})}function Cn(e){e!=="table"&&e!=="cards"||N({view:e})}var Dn=e=>N({lobs:e==="on"});function An(e,t){let n={..._().lobHidden,[e]:t};vn(n),setTimeout(()=>S({lobHidden:n}),0)}async function Rn(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var In=function(e,t,n,s){var r;t[0]=0;for(var i=1;i<t.length;i++){var o=t[i++],c=t[i]?(t[0]|=o?1:2,n[t[i++]]):t[++i];o===3?s[0]=c:o===4?s[1]=Object.assign(s[1]||{},c):o===5?(s[1]=s[1]||{})[t[++i]]=c:o===6?s[1][t[++i]]+=c+"":o?(r=e.apply(c,In(e,c,n,["",null])),s.push(r),c[0]?t[0]|=2:(t[i-2]=0,t[i]=r)):s.push(c)}return s},Pn=new Map;function Mn(e){var t=Pn.get(this);return t||(t=new Map,Pn.set(this,t)),(t=In(this,t.get(e)||(t.set(e,t=(function(n){for(var s,r,i=1,o="",c="",u=[0],l=function(d){i===1&&(d||(o=o.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,d,o):i===3&&(d||o)?(u.push(3,d,o),i=2):i===2&&o==="..."&&d?u.push(4,d,0):i===2&&o&&!d?u.push(5,0,!0,o):i>=5&&((o||!d&&i===5)&&(u.push(i,0,o,r),i=6),d&&(u.push(i,d,0,r),i=6)),o=""},f=0;f<n.length;f++){f&&(i===1&&l(),l(f));for(var h=0;h<n[f].length;h++)s=n[f][h],i===1?s==="<"?(l(),u=[u],i=3):o+=s:i===4?o==="--"&&s===">"?(i=1,o=""):o=s+o[0]:c?s===c?c="":o+=s:s===\'"\'||s==="\'"?c=s:s===">"?(l(),i=1):i&&(s==="="?(i=5,r=o,o=""):s==="/"&&(i<5||n[f][h+1]===">")?(l(),i===3&&(u=u[0]),i=u,(u=u[0]).push(2,0,i),i=0):s===" "||s===" "||s===`\n`||s==="\\r"?(l(),i=2):o+=s),i===3&&o==="!--"&&(i=4,u=u[0])}return l(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Ln=e=>Array.isArray(e)?e.map(Ln):e===""?null:e,Zs=(e,t,...n)=>Q(e,t,...n.map(Ln)),a=Mn.bind(Zs);var Ie,k,mt,En,ht=0,Fn=[],y=b,Nn=y.__b,Hn=y.__r,Un=y.diffed,jn=y.__c,Bn=y.unmount,On=y.__;function Wn(e,t){y.__h&&y.__h(k,e,ht||t),ht=0;var n=k.__H||(k.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function ne(e){return ht=1,Ys(Kn,e)}function Ys(e,t,n){var s=Wn(Ie++,2);if(s.t=e,!s.__c&&(s.__=[n?n(t):Kn(void 0,t),function(c){var u=s.__N?s.__N[0]:s.__[0],l=s.t(u,c);u!==l&&(s.__N=[l,s.__[1]],s.__c.setState({}))}],s.__c=k,!k.__f)){var r=function(c,u,l){if(!s.__c.__H)return!0;var f=!1,h=s.__c.props!==c;if(s.__c.__H.__.some(function(m){if(m.__N){f=!0;var p=m.__[0];m.__=m.__N,m.__N=void 0,p!==m.__[0]&&(h=!0)}}),i){var d=i.call(this,c,u,l);return f?d||h:d}return!f||h};k.__f=!0;var i=k.shouldComponentUpdate,o=k.componentWillUpdate;k.componentWillUpdate=function(c,u,l){if(this.__e){var f=i;i=void 0,r(c,u,l),i=f}o&&o.call(this,c,u,l)},k.shouldComponentUpdate=r}return s.__N||s.__}function Vn(e,t){var n=Wn(Ie++,7);return nr(n.__H,t)&&(n.__=e(),n.__H=t,n.__h=e),n.__}function er(){for(var e;e=Fn.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(Pe),t.__h.some(gt),t.__h=[]}catch(n){t.__h=[],y.__e(n,e.__v)}}}y.__b=function(e){k=null,Nn&&Nn(e)},y.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),On&&On(e,t)},y.__r=function(e){Hn&&Hn(e),Ie=0;var t=(k=e.__c).__H;t&&(mt===k?(t.__h=[],k.__h=[],t.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(t.__h.some(Pe),t.__h.some(gt),t.__h=[],Ie=0)),mt=k},y.diffed=function(e){Un&&Un(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(Fn.push(t)!==1&&En===y.requestAnimationFrame||((En=y.requestAnimationFrame)||tr)(er)),t.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0)})),mt=k=null},y.__c=function(e,t){t.some(function(n){try{n.__h.some(Pe),n.__h=n.__h.filter(function(s){return!s.__||gt(s)})}catch(s){t.some(function(r){r.__h&&(r.__h=[])}),t=[],y.__e(s,n.__v)}}),jn&&jn(e,t)},y.unmount=function(e){Bn&&Bn(e);var t,n=e.__c;n&&n.__H&&(n.__H.__.some(function(s){try{Pe(s)}catch(r){t=r}}),n.__H=void 0,t&&y.__e(t,n.__v))};var qn=typeof requestAnimationFrame=="function";function tr(e){var t,n=function(){clearTimeout(s),qn&&cancelAnimationFrame(t),setTimeout(e)},s=setTimeout(n,35);qn&&(t=requestAnimationFrame(n))}function Pe(e){var t=k,n=e.__c;typeof n=="function"&&(e.__c=void 0,n()),k=t}function gt(e){var t=k;e.__c=e.__(),k=t}function nr(e,t){return!e||e.length!==t.length||t.some(function(n,s){return n!==e[s]})}function Kn(e,t){return typeof t=="function"?t(e):t}var K=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>a`<span data-age=${e??""}>${K(e)}</span>`,Le=e=>e.activity&&a`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,sr=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,Ee=e=>{let t=e.waiting;if(!t)return;let n=sr(t.link),s=n;try{n&&(s=new URL(n).host+new URL(n).pathname.replace(/\\/$/,""))}catch{s=n}return a`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${n&&[" \\xB7 ",a`<a href=${n} target="_blank" rel="noopener noreferrer" onClick=${x}>${s}</a>`]}</div>`},rr=12,zn=e=>e!==void 0&&e.length>rr,H=e=>zn(e)?e:void 0,U=e=>zn(e)?" long":"",w=(e,t)=>()=>L(e,t),x=e=>e.stopPropagation(),or=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function z(e){if(e.requested)return a`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return a`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let s=or(e);return s?a`<span class="dim">Resume: <code onClick=${x}>${s}</code></span>`:a`<span class="dim">Resume command unavailable</span>`}let t=_();return!t.snapshot?.focusSupported&&!e.link?a`<span class="dim">Window focus is not supported here</span>`:a`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${s=>{x(s),Sn(e.trapId)}}>\u2197 open</button>${e.link&&[" ",a`<a href=${e.link} onClick=${x}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",a`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function R(e,t,n){return t.length?a`<div class="wrap"><table><tbody><tr>${e.map(s=>a`<th>${s}</th>`)}</tr>${t}</tbody></table></div>`:a`<div class="empty">${n}</div>`}function ir({text:e}){let[t,n]=ne(null),s=r=>async()=>{await Rn(e)&&(n(r),setTimeout(()=>n(null),1e3))};return a`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${s("code")}>${e}</code><button title="copy" onClick=${s("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var C=(e,t)=>a`<${ir} key=${t} text=${e} />`,ue=(e,t,n="thumb")=>a`<button\n type="button"\n class=${n}\n title=${"view "+t}\n onClick=${s=>{x(s),Tn(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,se=(e,t)=>e.map(n=>[a`<div class="sub">${n.name} \xB7 ${n.type} \xB7 ${n.bytes} bytes</div>`,t&&rn(n.name)&&ue(t(n.name),n.name),C(n.path)]),Jn=/\\bwt:([a-z0-9][a-z0-9-]*)/g,vt=e=>_().snapshot?.trapNames?.[e],j=e=>e.replace(Jn,(t,n,s)=>{let r=vt(n);return r&&!Xn(e,s,t.length,r)?r:t}),Xn=(e,t,n,s)=>e.slice(Math.max(0,t-s.length-2),t)===`${s} (`&&e[t+n]===")";function Ne(e){let t="wt:"+e,n=vt(e)??t;return _().snapshot?.traps.some(r=>r.trapId===e)?a`<a class="trapname" href="#traps" title=${t} onClick=${r=>{r.preventDefault(),x(r),L("trap",e)}}>${n}</a>`:a`<span class="trapname" title=${t}>${n}</span>`}var Me=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?Ne(t[1]):e};function B(e,t=1/0){let n=[],s=0;for(let c of e.matchAll(Jn)){c.index>s&&n.push({text:e.slice(s,c.index)});let u=vt(c[1]);n.push(u&&Xn(e,c.index,c[0].length,u)?{text:c[0]}:{text:u??c[0],trapId:c[1]}),s=c.index+c[0].length}s<e.length&&n.push({text:e.slice(s)});let r=n.reduce((c,u)=>c+u.text.length,0),i=r>t?t-1:1/0,o=[];for(let c of n){if(i<=0)break;c.text.length<=i?o.push(c.trapId?Ne(c.trapId):c.text):o.push(c.text.slice(0,i)),i-=c.text.length}return r>t&&o.push("\\u2026"),o}var _t=e=>e.log.flatMap((t,n)=>[n?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...B(t.note)]:[]]);function Qn(e){let t=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`);return a`${e.waiting&&[a`<div class="sec">waiting</div>`,Ee(e)]}${e.activity&&[a`<div class="sec">activity</div>`,Le(e)]}${t&&[a`<div class="sec">progress</div>`,a`<pre>${t}</pre>`]}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[a`<div class="sec">attachments (${e.attachments.length})</div>`,se(e.attachments,n=>it(e.lane,e.id,n))]}${e.messageAttachments.length>0&&[a`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,se(e.messageAttachments,n=>it(e.lane,e.id,n))]}${e.followUp&&[a`<div class="sec">forks</div>`,a`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?_t(e):"no entries yet"}</div>${e.inbox.length>0&&[a`<div class="sec">inbox</div>`,a`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[a`<div class="sec">awaiting reply</div>`,a`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${e.evidence&&[a`<div class="sec">evidence</div>`,a`<div class="loglines">${JSON.stringify(e.evidence)}</div>`]}`}function fe(e){return e.for?[Me(e.for),e.evidence&&e.evidence.deliveredTo?[" ",a`<span class="ok">\u2713delivered</span>`]:[" ",a`<span class="warn">waiting</span>`]]:e.claimedBy?Me(e.claimedBy):""}function re(e){if(e.prList&&e.prList.length>1)return[e.prList.map((r,i)=>[i?" \\u2192 ":"",a`<a href=${r.url} target="_blank" rel="noopener" onClick=${x}>#${r.number}</a>`,r.badge&&[" ",a`<span class=${"badge "+M(r.badge)}>${r.badge.text}</span>`]]),e.prGate&&[" ",a`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let n=e.prBadge,s=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[s?s.map((r,i)=>[i?" ":"",a`<a href=${r} target="_blank" rel="noopener" onClick=${x}>#${r.split("/").pop()}</a>`]):a`<a href=${t} target="_blank" rel="noopener" onClick=${x}>PR</a>`,n&&[" ",a`<span class=${"badge "+M(n)} title=${"observed "+n.observedAt}>${n.text}</span>`],e.prGate&&[" ",a`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var ar={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready",landed:"landed",watch:"watch"},$t={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","pr:draft":"dim",watch:"warn"},Zn=e=>ar[e]||"";var He=e=>a`<a href=${e.url} target="_blank" rel="noopener" onClick=${x}>#${e.number}</a>`,Ue=e=>a`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function oe(e,t,n){return e.flatMap((s,r)=>{let i=t.get(s)||void 0,o=s===n?a`<b title=${i}>#${s}</b>`:a`<span title=${i}>#${s}</span>`;return r?[" \\u2192 ",o]:[o]})}function me(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function je(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function he(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",a`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function bt(e){let t=fn(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:a`<span class="dim">${t.text}</span>`}function ge(e){if(e.requested)return{stale:!1,listen:[a`<span class="dot"></span>`,a`<span class="dim">requested</span>`],hb:a`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[a`<span class="dot bad"></span>`,a`<span class="bad">start failed</span>`]:[a`<span class="dot warn"></span>`,"starting"],hb:a`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[a`<span class="dot"></span>`,a`<span class="dim">signed off</span>`],hb:a`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,n=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:n?[a`<span class="dot ok"></span>`,"listening"]:[a`<span class="dot warn"></span>`,"not listening"],hb:a`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function kt(e){if(e.requested)return{kind:"requested",helmOn:!!_().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(n=>n.id===e.claimed&&n.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function lr(e){let t=kt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var Yn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",es=e=>e.brief.split(/\\r?\\n/,1)[0]?.trim().slice(0,40)||"(no title)";function ve(e){let t=kt(e),n=a`<span class=${"dot trapdot "+lr(e)}></span>`;if(t.kind==="requested")return[n,a`<span class="dim">${ts(e,t.helmOn)}</span>`];if(t.kind==="starting")return[n,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[n,a`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[n,a`<span class="dim">signed off</span>`];if(t.kind==="idle")return[n,"idle \\xB7 ",t.listening?"listening":"not listening"];let s=t.current;return t.kind==="parked"?[n,"parked \\xB7 ",Yn(s)]:[n,"working \\xB7 ",a`<a href="#dispatches" onClick=${i=>{i.preventDefault(),x(i),L("dispatch",`${s.lane}:${s.id}`)}}>${s.id.slice(0,8)}</a>`," \\xB7 ",es(s),s.activity&&[" \\xB7 ",s.activity.summary," ",v(s.activity.at)," ago"]]}var ts=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function J(e){let t=e.starting?.commands;return t?.length?a`<div class="startcmds" onClick=${x}><div class="dim">start it: run one in a terminal</div>${t.map(n=>C(n.command,n.harness))}</div>`:null}function Be(){let t=_().snapshot?.repoKeys??[],[n,s]=ne(!1),[r,i]=ne(""),[o,c]=ne("claude"),[u,l]=ne(null),f=r||t[0]||"",h=async d=>{d.preventDefault();let m=await xn(f,o);l(m??null),m||s(!1)};return n?a`<form class="newtrap" onClick=${x} onSubmit=${h}>\n <label>repo <select name="repo" value=${f} onChange=${d=>i(d.target.value)}>${t.map(d=>a`<option value=${d}>${d}</option>`)}</select></label>\n <label>harness <select name="harness" value=${o} onChange=${d=>c(d.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!f}>Request</button>\n <button class="btn" type="button" onClick=${()=>s(!1)}>Cancel</button>\n ${u&&a`<span class="bad">${u}</span>`}\n </form>`:a`<span class="newtrap"><button class="btn" onClick=${d=>(x(d),s(!0),l(null))}>+ New trap</button></span>`}function Oe(e){let t=kt(e);if(t.kind==="requested")return ts(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${Yn(t.current)}`;let n=t.current;return[`working \\xB7 ${n.id.slice(0,8)} \\xB7 ${es(n)}`,n.activity&&`${n.activity.summary} ${K(n.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function yt(e){let t=e.messages.filter(n=>n.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",a`<span class="warn">(${t} pending)</span>`]]:null}var cr=e=>()=>{window.open(D(e.key),"_blank","noopener")};function wt(e){let t=de(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${K(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function dr(e){let t=wt(e);return a`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${D(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function pr(e){return a`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${cr(e)}><td class="grow"><a href=${D(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${de(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function ns({inp:e}){return e.view==="cards"?e.list.length?a`<div class="cards">${e.list.map(dr)}</div>`:a`<div class="empty">no reports match</div>`:R(["title","from","filed","acked"],e.list.map(pr),"no reports match")}function os(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function is(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function ss(e,t,n,s){let r=0;for(let i=t;i<e.length;i++){if(e[i]==="\\\\"){i++;continue}if(e[i]===n)r++;else if(e[i]===s){if(r===0)return i;r--}}return-1}function rs(e,t){let n=ss(e,t+1,"[","]");if(n<0||e[n+1]!=="(")return;let s=ss(e,n+2,"(",")");if(s<0)return;let r=e.slice(n+2,s).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,n),target:r,end:s+1}}function q(e){let t=[],n="",s=()=>{n&&t.push({t:"text",v:n}),n=""};for(let r=0;r<e.length;){let i=e[r];if(i==="\\\\"&&r+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[r+1])){n+=e[r+1],r+=2;continue}if(i==="`"){let o=/^`+/.exec(e.slice(r))[0],c=e.indexOf(o,r+o.length);if(c>0){s(),t.push({t:"code",v:e.slice(r+o.length,c).replace(/^ (.*) $/,"$1")}),r=c+o.length;continue}}if(i==="!"&&e[r+1]==="["){let o=rs(e,r+1);if(o){s(),t.push({t:"img",alt:o.text,src:o.target}),r=o.end;continue}}if(i==="["){let o=rs(e,r);if(o){s(),t.push({t:"a",href:o.target,c:q(o.text)}),r=o.end;continue}}if((i==="*"||i==="_")&&e[r+1]===i){let o=e.indexOf(i+i,r+2);if(o>r+2){s(),t.push({t:"b",c:q(e.slice(r+2,o))}),r=o+2;continue}}if(i==="*"||i==="_"&&(r===0||!/\\w/.test(e[r-1]))){let o=r+1;for(;(o=e.indexOf(i,o))>0&&(e[o+1]===i||i==="_"&&/\\w/.test(e[o+1]??""));)o+=2;if(o>r+1&&e[r+1]!==" "){s(),t.push({t:"i",c:q(e.slice(r+1,o))}),r=o+1;continue}}if(i===`\n`){/ {2,}$/.test(n)||n.endsWith("\\\\")?(n=n.replace(/( {2,}|\\\\)$/,""),s(),t.push({t:"br"})):n+=" ",r++;continue}n+=i,r++}return s(),t}var as=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,ls=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,cs=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,St=/^( {0,3})([-*+])\\s+(.*)$/,xt=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,qe=/^ {0,3}>\\s?(.*)$/,ur=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Gt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let n=[],s="";for(let r=0;r<t.length;r++)t[r]==="\\\\"&&t[r+1]==="|"?(s+="|",r++):t[r]==="|"?(n.push(s.trim()),s=""):s+=t[r];return n.push(s.trim()),n}var Tt=e=>as.test(e)||ls.test(e)||cs.test(e)||St.test(e)||xt.test(e)||qe.test(e);function Fe(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),n=[],s=0;for(;s<t.length;){let r=t[s];if(!r.trim()){s++;continue}let i=as.exec(r);if(i){let f=i[1],h=[];for(s++;s<t.length&&!new RegExp(`^ {0,3}${f[0]}{${f.length},}\\\\s*$`).test(t[s]);)h.push(t[s++]);s++,n.push({t:"code",lang:i[2]??"",v:h.join(`\n`)});continue}let o=ls.exec(r);if(o){n.push({t:"h",level:o[1].length,c:q(o[2])}),s++;continue}if(cs.test(r)){n.push({t:"hr"}),s++;continue}if(qe.test(r)){let f=[];for(;s<t.length&&t[s].trim()&&(qe.test(t[s])||!Tt(t[s]));)f.push(qe.exec(t[s])?.[1]??t[s]),s++;n.push({t:"quote",c:Fe(f.join(`\n`))});continue}let c=St.exec(r),u=xt.exec(r);if(c||u){let f=!c,h=f?xt:St,d=[];for(;s<t.length;){let m=t[s],p=h.exec(m);if(p&&p[1].length<=3){d.push([p[3]]),s++;continue}if(!m.trim()){if(s+1<t.length&&/^ {2,}\\S/.test(t[s+1])){d.at(-1).push(""),s++;continue}break}if(/^ {2,}\\S/.test(m)){d.at(-1).push(m.replace(/^ {2,4}/,"")),s++;continue}if(Tt(m))break;d.at(-1).push(m.trim()),s++}n.push({t:"list",ordered:f,start:f?Number(u[2]):1,items:d.map(m=>Fe(m.join(`\n`)))});continue}if(r.includes("|")&&s+1<t.length&&ur.test(t[s+1])&&t[s+1].includes("-")){let f=Gt(r),h=Gt(t[s+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");s+=2;let d=[];for(;s<t.length&&t[s].trim()&&t[s].includes("|");){let m=Gt(t[s++]);d.push(f.map((p,g)=>q(m[g]??"")))}n.push({t:"table",align:f.map((m,p)=>h[p]??""),head:f.map(q),rows:d});continue}let l=[];for(;s<t.length&&t[s].trim()&&!Tt(t[s])&&!(l.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[s]));)l.push(t[s++].replace(/^ +/,""));if(l.length===0){n.push({t:"p",c:q(r.trim())}),s++;continue}n.push({t:"p",c:q(l.join(`\n`))})}return n}function F(e,t){return t.map(n=>{switch(n.t){case"text":return n.v;case"code":return a`<code>${n.v}</code>`;case"b":return a`<strong>${F(e,n.c)}</strong>`;case"i":return a`<em>${F(e,n.c)}</em>`;case"br":return a`<br />`;case"a":{let s=os(n.href);return s?a`<a href=${s} target="_blank" rel="noopener noreferrer">${F(e,n.c)}</a>`:F(e,n.c)}case"img":{let s=is(n.src);return s?ue(e(s),n.alt||s,"mdimg"):a`<span class="dim">[image not shown: ${n.alt||n.src}]</span>`}}})}function We(e,t){return t.map(n=>{switch(n.t){case"h":{let s=F(e,n.c);return n.level===1?a`<h1>${s}</h1>`:n.level===2?a`<h2>${s}</h2>`:n.level===3?a`<h3>${s}</h3>`:a`<h4>${s}</h4>`}case"p":return a`<p>${F(e,n.c)}</p>`;case"code":return a`<pre class="mdcode"><code>${n.v}</code></pre>`;case"hr":return a`<hr />`;case"quote":return a`<blockquote>${We(e,n.c)}</blockquote>`;case"list":{let s=n.items.map(r=>a`<li>${We(e,r)}</li>`);return n.ordered?a`<ol start=${n.start}>${s}</ol>`:a`<ul>${s}</ul>`}case"table":return a`<div class="mdtable"><table><thead><tr>${n.head.map((s,r)=>a`<th style=${n.align[r]?"text-align:"+n.align[r]:void 0}>${F(e,s)}</th>`)}</tr></thead><tbody>${n.rows.map(s=>a`<tr>${s.map((r,i)=>a`<td style=${n.align[i]?"text-align:"+n.align[i]:void 0}>${F(e,r)}</td>`)}</tr>`)}</tbody></table></div>`}})}var _e=new Map;function ds(e){let t=_e.get(e);return t||(t=Fe(e),_e.set(e,t),_e.size>8&&_e.delete(_e.keys().next().value)),t}function fr({r:e,text:t}){let n=t?.text!==void 0?We(s=>ct(e.key,s),ds(t.text)):t?.error?a`<div class="bad">${t.error}</div>`:a`<div class="dim">loading\u2026</div>`;return a`<div class="mdpage" data-report=${e.key}>${n}</div>`}function ps({text:e,fileUrl:t}){return a`<div class="mdpage">${We(t,ds(e))}</div>`}function Ct(e){let t=de(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",a`<span class="ok">acked</span>`]]}function us({r:e,text:t,heading:n}){return[n&&a`<div class="sec">report \xB7 ${e.title}</div>`,a`<div class="sub">${Ct(e)}</div>`,a`<${fr} r=${e} text=${t} />`,e.attachments.length>0&&[a`<div class="sec">report attachments (${e.attachments.length})</div>`,se(e.attachments,s=>ct(e.key,s))],!e.acked&&[a`<div class="sec">ack</div>`,C("lobstah attention ack "+e.key)]]}var mr=/\\.(png|jpe?g|gif|webp)$/i,hr=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function gr(e,t){return t.length?a`<div class="dfiles">${t.map(n=>mr.test(n.name)?a`<span key=${n.name}>${ue(at(e,n.name),n.name,"dimg")}</span>`:a`<div key=${n.name} class="dfile"><span>${n.name}</span> <span class="dim">\xB7 ${n.type} \xB7 ${n.bytes} bytes \xB7 </span><code>${n.path}</code></div>`)}</div>`:null}function vr(e,t,n){let s=e.key,r=e.kind==="decision"?e.options:[],i=t?.text??"",o=t?.files??[],c=!!t?.sending;return[r.length>0&&a`<div class="doptions">${r.map(u=>a`<button key=${u} type="button" class=${"btn dopt"+(t?.option===u?" on":"")} aria-pressed=${t?.option===u?"true":"false"} disabled=${c} onClick=${()=>bn(s,u)}>${u}</button>`)}</div>`,a`<textarea\n class="danswer"\n rows="2"\n placeholder=${r.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${i}\n disabled=${c}\n onInput=${u=>{let l=u.currentTarget;hr(l),A(s,{text:l.value,error:void 0})}}\n onPaste=${u=>{kn(s,u)}}\n ></textarea>`,a`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${n} disabled=${c} onChange=${u=>{let l=u.currentTarget;ft(s,l.files).then(()=>{l.value=""})}} /></label>\n ${o.map((u,l)=>a`<span key=${u.name+l} class="dchip">${u.name}<button type="button" class="dx" title="remove" disabled=${c} onClick=${()=>yn(s,l)}>\xD7</button></span>`)}\n ${t?.error&&a`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${c} onClick=${()=>{wn(s)}}>${c?"Sending\\u2026":"Send"}</button>\n </div>`]}function _r(e,t,n,s){let r=e.dispatch,i=e.lane??"work",c=[r&&a`<a class="dlink" href="#" onClick=${l=>{l.preventDefault(),w("dispatch",i+":"+r)()}}>${r.slice(0,8)}</a>`,e.repo,[v(e.at)," ago"]].filter(Boolean).flatMap((l,f)=>f?[" \\xB7 ",l]:[l]),u="dcard"+(e.kind==="question"?" plain":"")+(t?.sent?" answered":"")+(n?" focus":"");return a`<div key=${e.key} class=${u} data-decision=${e.key}>\n <div class="dtop">\n ${e.kind==="question"&&a`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${e.kind==="decision"?e.title:B(e.note)}</b>\n <span class="dmeta dim">${c}</span>\n </div>\n ${e.kind==="decision"&&e.detail.trim()&&a`<${ps} text=${e.detail} fileUrl=${l=>at(e.key,l)} />`}\n ${e.kind==="decision"&&gr(e.key,e.attachments)}\n ${t?.sent?a`<div class="danswered ok">answered \xB7 ${t.sent}</div>`:vr(e,t,s)}\n </div>`}function fs({cards:e,drafts:t,focus:n,extensions:s}){let r=s.join(","),i=e.length?a`<div class="dcards">${e.map(o=>_r(o,t[o.key],o.key===n,r))}</div>`:a`<div class="empty">none</div>`;return a`<section class="decisions"><h2>decisions</h2>${i}</section>`}function yr(e,t){let n=e.badge&&a`<span class=${"badge "+(e.badge.tone||"dim")+U(e.badge.text)} title=${H(e.badge.text)}>${e.badge.text}</span>`;if(e.href){let s=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?a`<a key=${e.key} class=${s} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${n}</div>${e.meta&&a`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:a`<a key=${e.key} class=${s} href=${e.href} target="_blank" rel="noopener">${n&&[n," "]}<b>${e.title}</b>${e.meta&&[" ",a`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?a`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${n}</div>${e.meta&&a`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&a`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:a`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${n&&[n," "]}<b>${e.title}</b>${e.meta&&[" ",a`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",a`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var ms=(e,t)=>e>0&&a`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function Ve(e,t,n,s,r,i){let o=t.slice(0,s),c=o.map(l=>yr(l,r)),u=o.length?r==="cards"?a`<div class="cards">${c}</div>`:c:a`<div class="empty">none</div>`;return a`<section><h2><a href=${"#"+n}>${e} \u2192</a></h2>${i&&a`<div class="tabhead">${i}</div>`}${u}${ms(t.length-o.length,n)}</section>`}function wr(e,t,n,s){let r=t.find(l=>l.number===e.nextNumber)||t[0],i=oe(e.numbers,new Map(t.map(l=>[l.number,l.title]))),o=r?"next #"+r.number:"nothing mergeable";if(s==="cards"){let l=f=>{let h=n.get(f.key)||[],d=h.length>0&&h.every(g=>g.acked),m=h.length?h.map(g=>a`<span class=${"badge "+($t[g.kind]||"dim")}>${Zn(g.kind)}</span>`):a`<span class=${"badge "+M(f.badge)+U(f.badge.text)} title=${H(f.badge.text)}>${f.badge.text}</span>`,p=`${f.repo} \\xB7 ${f.badge.text}${d?" \\xB7 acked":""}`;return a`<div key=${f.key} class=${"card"+(d?" acked":"")} onClick=${w("pr",f.key)}><div class="top">${Ue(f)}${m}</div><div class="meta" title=${p}>${p}</div></div>`};return a`<div key=${e.id} class="deckstack"><div class="dim">${i} \xB7 ${o}</div><div class="cards">${t.map(l)}</div></div>`}let c=t.flatMap(l=>(n.get(l.key)||[]).map(f=>a`<span class=${"badge "+($t[f.kind]||M(l.badge))+(f.acked?" acked":"")}>#${l.number} ${l.badge.text}</span>`)),u=c.flatMap((l,f)=>f?[" ",l]:[l]);return a`<div key=${e.id} class=${"deckline"+(r?" click":"")} onClick=${r&&w("pr",r.key)}><b>${i}</b> <span class="dim">\xB7 ${o}</span>${c.length>0&&[" \\xB7 ",u]}</div>`}function Gr(e,t){let n=e.stacks.slice(0,3),s=new Map;for(let i of e.prAttention){let o=s.get(i.key)||[];o.push(i),s.set(i.key,o)}let r=n.length?n.map(i=>wr(i,e.prs.filter(o=>o.stackId===i.id).sort((o,c)=>o.position-c.position),s,t)):a`<div class="empty">none</div>`;return a`<section><h2><a href="#prs">PRs \u2192</a></h2>${r}${ms(e.stacks.length-n.length,"prs")}</section>`}function hs({inp:e,drafts:t,focus:n,extensions:s}){let r=e.view,i=e.inflight.map(l=>({key:l.lane+":"+l.id,title:[l.id.slice(0,8)," ",l.repo||""],badge:{text:l.verb,tone:l.verb==="needs-decision"||l.verb==="blocked"?"bad":"dim"},meta:[B(l.note||"",90),l.verbAt&&[" \\xB7 ",v(l.verbAt)," ago"],(l.for||l.claimedBy)&&[" \\xB7 ",Me(l.for||l.claimedBy)]],metaText:[j(l.note||""),l.verbAt&&`${K(l.verbAt)} ago`,j(l.for||l.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:w("dispatch",l.lane+":"+l.id)})),o=e.landed.map(l=>({key:l.key,title:[l.id.slice(0,8)," ",l.repo||"",l.unreported&&[" ",a`<span class="badge warn unreported">unreported</span>`]],badge:{text:l.verb,tone:l.verb==="failed"?"bad":"ok"},meta:[B(l.note||"",90)," \\xB7 ",v(l.at)," ago"],metaText:`${j(l.note||"")} \\xB7 ${K(l.at)} ago`,open:w("dispatch",l.lane+":"+l.id)})),c=e.reports.map(l=>{let f=wt(l);return{key:l.key,title:l.title,meta:f.nodes,metaText:f.text,href:D(l.key),acked:!!l.acked}}),u=e.traps.map(({x:l})=>({key:l.trapId,title:"\\u{1FAA4} "+(l.label??`wt:${l.trapId}`),badge:l.requested?{text:"requested",tone:"dim"}:l.starting?{text:l.starting.failedAt?"start failed":"starting",tone:l.starting.failedAt?"bad":"warn"}:{text:l.live?l.harness||"live":"signed off",tone:l.live?"ok":"dim"},meta:[l.repo||""," \\xB7 ",ve(l)],metaText:`${l.repo||""} \\xB7 ${Oe(l)}`,action:z(l),extra:J(l),open:w("trap",l.trapId)}));return a`<div class="deckgrid"><${fs} cards=${e.decisions} drafts=${t} focus=${n} extensions=${s} />${Ve("in flight",i,"dispatches",4,r)}${Ve("Landed \\xB7 24h",o,"dispatches",8,r)}${Ve("reports",c,"reports",8,r)}${Ve("traps",u,"traps",8,r,a`<${Be} />`)}${Gr(e,r)}</div>`}function Tr(e,t){if(!t)return e;let n=new Map(e.map(i=>[i.id,i])),s=i=>{let o=i,c=new Set;for(;o.followUp&&n.has(o.followUp)&&!c.has(o.id);)c.add(o.id),o=n.get(o.followUp);return o.id},r=new Map;for(let i of e){let o=s(i),c=r.get(o)||[];c.push(i),r.set(o,c)}return[...r.values()].sort((i,o)=>o[0].sort-i[0].sort).flatMap(i=>i.sort((o,c)=>o.id===s(o)?-1:c.id===s(c)?1:o.sort-c.sort))}function Sr(e,t){let n=e.lane+":"+e.id;return a`<tr key=${n} class="rowhead" onClick=${w("dispatch",n)}><td>${t&&e.followUp&&a`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${B(e.note??"",90)}${Ee(e)}${Le(e)}</td><td>${v(e.verbAt)}</td><td>${fe(e)}</td><td>${re(e)}</td></tr>`}function xr(e){let t=e.lane+":"+e.id;return a`<div key=${t} class="card" onClick=${w("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b><span class=${"badge v-"+e.verb+U(e.verb)} title=${H(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&a`<div class="note" title=${j(e.note)}>${B(e.note)}</div>`}${Ee(e)}${Le(e)}<div class="foot">${fe(e)} ${re(e)}</div></div>`}function gs({inp:e}){let t=e.list;return e.view==="cards"?t.length?a`<div class="cards">${t.map(xr)}</div>`:a`<div class="empty">no dispatches match</div>`:R(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Tr(t,e.chain).map(n=>Sr(n,e.chain)),"no dispatches match")}var Cr={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices"},Dr=["working","needs-decision","blocked","paused","done","failed","unknown"];function Ar(e,t){let n=t.daemonStale,s=e.daemon?[a`<span class=${n?"bad":"ok"}>${n&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",a`<span class="dim">v${e.daemon.version}</span>`]:a`<span class="bad">down</span>`;return[a`<span class="chip">daemon ${s}</span>`,e.slots&&a`<span class="chip">active headless: ${e.slots.headless} of ${e.slots.limit}; traps: ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:r,stale:i})=>a`<span key=${r.grounds} class="chip click" onClick=${w("helm",r.grounds)}>\u26F5 <b>${r.man}</b> <span class="dim">helm ${r.grounds}</span> <span class=${i?"warn":"ok"}>${i&&"stale "}${v(r.heartbeatAt)} ago</span></span>`)]}var Dt=[];function Rr(e){let t=[...new Set([...e.dispatches.map(n=>n.repo),...e.notices.map(n=>n.repo),...e.prs.map(n=>n.repo)].filter(Boolean))].sort();return t.length!==Dt.length&&(Dt=t),Dt}var At=[];function Pr(e){let t=[...new Set(e.notices.map(n=>n.kind))].sort();return t.length!==At.length&&(At=t),At}var $e=e=>e.currentTarget.value;function Ir(e,t,n){let s=o=>t===o?"":"display:none",r=e?Rr(e):[],i=e?Pr(e):[];return a`<div class="controls">\n <select id="f-lane" style=${e&&s("dispatches")} value=${n.lane} onChange=${o=>N({lane:$e(o)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${n.repo} onChange=${o=>N({repo:$e(o)})}>\n <option value="">all repos</option>\n ${r.map(o=>a`<option key=${o}>${o}</option>`)}\n </select>\n <select id="f-verb" style=${e&&s("dispatches")} value=${n.verb} onChange=${o=>N({verb:$e(o)})}>\n <option value="">all verbs</option>\n ${Dr.map(o=>a`<option key=${o}>${o}</option>`)}\n </select>\n <label id="chain-control" style=${e&&s("dispatches")}><input id="f-chain" type="checkbox" checked=${!!n.chain} onChange=${o=>N({chain:o.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&s("notices")} value=${n.noticeKind} onChange=${o=>N({noticeKind:$e(o)})}>\n <option value="">all notice kinds</option>\n ${i.map(o=>a`<option key=${o}>${o}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${n.q} onInput=${o=>N({q:$e(o)})} />\n </div>`}function vs({d:e,inp:t,tab:n,st:s,stale:r}){return[a`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||r?r?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>L("settings","browser")}>\u2699</button></span></div>`,a`<div class="chips"><span id="chips" style="display:contents">${e&&t&&Ar(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,a`<nav class="tabs" id="tabs" aria-label="Spyglass views">${ce.map(i=>a`<a href=${"#"+i} data-tab=${i} class=${e?i===n?"on":"":void 0}>${Cr[i]}</a>`)}</nav>`,Ir(e,n,s)]}var _s=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",a`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function $s(e,t){if(!t.lobs)return[];let n={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready",landed:"landed",watch:"watch",report:"report"},s=t.hidden??{},r=e.filter(o=>!o.acked&&!(o.key!==void 0&&s[o.key]===o.stateHash)).map(o=>{let c=n[o.kind??""]??"",u=o.key!==void 0?{hideKey:o.key,hideHash:o.stateHash??""}:{};return o.kind==="decision"&&o.key?{key:"decision:"+o.key,text:o.note||o.verb,label:c,hash:rt(o.key),...u}:(o.kind??"question")==="question"&&o.key?{key:"question:"+o.key,text:o.note||o.verb,label:c,hash:rt(o.key),...u}:o.kind==="report"&&o.key?{key:"report:"+o.key,text:o.note||o.verb,label:c,href:D(o.key),...u}:typeof o.kind=="string"&&o.kind.startsWith("pr:")&&o.prUrl?{key:o.kind+":"+(o.key??o.prUrl),text:o.note||o.kind,href:o.prUrl,label:c,...u}:{key:(o.kind??"question")+":"+(o.key??o.lane+":"+o.id),text:o.note||o.verb,label:c,...o.kind==="watch"?{}:{open:{type:"dispatch",key:o.lane+":"+o.id}},...u}});!r.length&&t.preview&&(r=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let i=r.length>4?r.length-4:0;return r=r.slice(0,4),i&&(r[3]={...r[3],text:"\\u2026and "+i+" more \\u2014 see attention",label:""}),r}var bs=!0;function Mr(e,t,n){let s="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",r=()=>{e.hideKey&&An(e.hideKey,e.hideHash??"")},i=[a`<div class="bub">${e.label&&[a`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,n===!1?a`<span class="fallback">\u{1F99E}</span>`:a`<div class="sprite"></div>`,bs&&a`<img\n class="star"\n src="/star.png"\n alt=""\n onError=${c=>{bs=!1,c.currentTarget.remove()}}\n />`],o=e.open;return e.hash?a`<a key=${e.key} class="lob" style=${s} title="open the decision" href=${e.hash} onClick=${r}>${i}</a>`:e.href?a`<a key=${e.key} class="lob" style=${s} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${r}>${i}</a>`:a`<div\n key=${e.key}\n class="lob"\n style=${s}\n title="click to open"\n onClick=${()=>{r(),o&&L(o.type,o.key)}}\n >\n ${i}\n </div>`}function ks({state:e}){let t=e.snapshot,n=$s(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),s=n.map(r=>r.key).join("|")+(e.spriteOk===null?"?":e.spriteOk?"s":"e");return Vn(()=>n.map((r,i)=>Mr(r,i,e.spriteOk)),[s])}function Ke({box:e}){if(!e)return null;let t=n=>{n.target===n.currentTarget&&ee()};return a`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${ee}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var be=a`<span class="x" onClick=${te}>\xD7</span>`,ys=(e,t,n)=>a`<span class="seg">${t.map(([s,r])=>a`<button class=${s===n?"on":""} onClick=${()=>e(s)}>${r}</button>`)}</span>`;function Lr(e,t){let n=ys(Cn,[["table","table"],["cards","cards"]],t.view),s=ys(Dn,[["on","on"],["off","off"]],t.lobs?"on":"off"),r=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[be,a`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,a`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${n}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${s}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${r}</span></div></div>`]}function Er(e,t){let n=oe(t.numbers,new Map(t.numbers.map((r,i)=>[r,t.titles[i]])),e.number),s=t.nextMergeable?[" \\xB7 ",a`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[n," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,s]}var Nr=e=>t=>{t.preventDefault(),L("dispatch",e)};function Hr(e,t){let n=un(e,t),s=n.pr,r=n.stack,i=n.watch,o=je(s),c=n.chain.length?n.chain.flatMap((l,f)=>[f?" \\u2192 ":"",l.culled?a`<span class="dim">${l.id.slice(0,8)} (culled)</span>`:[a`<a href="#" onClick=${Nr(l.modalKey)}>${l.id.slice(0,8)}</a>`," ",a`<span class="dim">${l.verb}</span>`]]):a`<span class="dim">none</span>`,u=i?[a`<div>${i.key} \xB7 owner ${i.owner||"?"} \xB7 ${i.lastCheckedAt?["checked ",v(i.lastCheckedAt)," ago"]:"never checked"}</div>`,i.lastError&&a`<div class="bad">${i.lastError}</div>`,a`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,C(i.cursor)]:a`<div class="dim">no watch \u2014 <code>lobstah watch add ${s.url}</code> registers one</div>`;return[be,a`<h3>${He(s)} ${s.title||""} <span class=${"badge "+M(s.badge)}>${s.badge.text}</span>${s.draft&&s.badge.text!=="draft"&&[" ",a`<span class="badge pr-draft">draft</span>`]}</h3>`,a`<div class="sub">${s.repo} \xB7 ${s.state} \xB7 observed ${v(s.observedAt)} ago${s.gate&&" \\xB7 gate "+s.gate}</div>`,a`<div class="sec">checks</div><div>${me(s)}</div><div class="sec">review</div><div>${o||a`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${he(s)}</div>${s.repairWait&&a`<div class="sec">repair waits</div><div>${s.repairWait.heldBy}: ${s.repairWait.reason}${s.repairWait.until?" \\xB7 until "+s.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${s.headRefName||"?"} \u2192 ${s.baseRefName||"?"}</div><div class="sec">stack</div><div>${r?Er(s,r):a`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${c}</div><div class="sec">watch</div>`,u]}var ws=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function Ur(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[be,a`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.harness??"?"} \xB7 ${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,e.sessionId&&[a`<div class="sec">open this session</div>`,C(ws(e.harness,e.sessionId))],e.transcript&&[a`<div class="sec">transcript</div>`,C(e.transcript)]]}function jr(e){return[a`<div class="sec">report \xB7 ${e.title}</div>`,a`<div class="sub">${Ct(e)} \xB7 <a href=${D(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function Br(e,t){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[a`<div class="sec">worked by trap</div>`,a`<div class="claimedby">claimed by ${Ne(e.claimedBy.slice(3))}</div>`,C(j(e.claimedBy)),a`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${j(e.claimedBy)} "\u2026"</div>`]:[a`<div class="sec">open this session</div>`,C("lobstah attach "+e.id)];return[be,a`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,a`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",fe(e)]} ${re(e)}</div>`,n,e.worktree&&[a`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,C(e.worktree),e.worktreeKept&&a`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[a`<div class="sec">transcript</div>`,C(e.transcript)],t&&jr(t),Qn(e)]}function Or(e){let t=ge(e),n=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm reserves it and starts its session."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",s=e.notices.length?e.notices.map(o=>a`<div key=${o.seq} class="loglines">${v(o.at)} ago \xB7 <b>${o.kind}</b> \u2014 ${o.text}</div>`):a`<div class="empty">none recorded</div>`,r=e.messages.length?e.messages.map(o=>a`<div key=${o.file} class=${"msg"+(o.from==="helm"?" from-helm":"")}><div class="hdr">from ${o.from} \xB7 ${o.at&&[v(o.at)," ago"]} \xB7 ${o.state==="pending"?a`<span class="warn">pending</span>`:a`<span class="ok">delivered</span>`}</div>${o.text}${o.attachments?.length?se(o.attachments,c=>sn(e.trapId,c)):""}</div>`):a`<div class="empty">none</div>`,i=e.catches.length?e.catches.map(o=>a`<div key=${o.lane+":"+o.id} class="catch"><div class="hdr"><b>${o.id.slice(0,8)}</b><span class=${"badge v-"+o.verb}>${o.verb}</span><span class="dim">${v(o.verbAt)}</span>${re(o)}</div><div class="loglines">${_t(o)}</div></div>`):a`<div class="empty">none yet</div>`;return[be,a`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??"signed off"}</span></h3>`,e.worktree&&a`<div class="sub">${e.worktree}</div>`,a`<div class="sub">${n}</div>`,a`<div class="sec">window</div>`,z(e),J(e),e.live&&e.sessionId&&[a`<div class="sec">open this session</div>`,C(ws(e.harness,e.sessionId)),a`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],a`<div class="sec">lifecycle (${e.notices.length})</div>`,s,a`<div class="sec">messages (${e.messages.length})</div>`,r,a`<div class="sec">catches (${e.catches.length})</div>`,i]}function Gs({snapshot:e,modal:t,prefs:n}){if(!e||!t)return null;let s=Y(e,t);if(!s)return null;if(t.type==="settings")return Lr(s,n);if(t.type==="pr")return Hr(e,t.key);if(t.type==="helm")return Ur(s);if(t.type==="dispatch"){let r=an(e,s);return Br(s,r)}return Or(s)}var qr=e=>a`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,Ts=({inp:e})=>R(["at","kind","text","repo"],e.list.map(qr),"no notices");function Ss(e){let t=new Map;for(let n of e.prs){let s=t.get(n.stackId)||[];s.push(n),t.set(n.stackId,s)}return e.stacks.map(n=>({s:n,prs:t.get(n.id)||[]})).filter(n=>n.prs.length)}var xs=(e,t)=>oe(e.numbers,new Map(t.map(n=>[n.number,n.title]))),Rt=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Cs(e){let t=s=>{let r=String(s??"");return r.length>24?r.slice(0,23)+"\\u2026":r},n=e.watches.map(s=>a`<tr key=${s.key}><td>${s.key}</td><td>${s.owner}</td><td title=${s.cursor}>${t(s.cursor)}</td><td>${s.lastCheckedAt&&v(s.lastCheckedAt)}</td><td>${s.lastError||""}</td></tr>`);return[a`<h2>other watches</h2>`,R(["key","owner","cursor","last check","error"],n,"no other watches")]}function Fr(e){let t=Ss(e).flatMap(({s:n,prs:s})=>[a`<tr key=${"stack:"+n.id}><th colspan="8">${xs(n,s)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}</th></tr>`,...s.map(r=>a`<tr key=${r.key} class="rowhead" onClick=${w("pr",r.key)}><td>${He(r)}</td><td class="grow">${r.title||""}</td><td>${Rt(r)}</td><td>${me(r)}</td><td>${je(r)}</td><td>${he(r)}</td><td>${bt(r.watch)}</td><td>${r.gate||""}</td></tr>`)]);return[R(["PR","title","state","checks","review","merge","watch","gate"],t,"no PR evidence"),Cs(e)]}function Wr(e){let t=Ss(e),n=r=>a`<div key=${r.key} class="card" onClick=${w("pr",r.key)}><div class="top">${Ue(r)}<span class=${"badge "+M(r.badge)+U(r.badge.text)} title=${H(r.badge.text)}>${r.badge.text}</span></div><div class="meta" title=${`${r.repo} \\xB7 ${Rt(r)}`}>${r.repo} \xB7 ${Rt(r)} \xB7 ${he(r)}</div><div class="foot"><span>${me(r)}</span><span>${bt(r.watch)}</span>${r.gate&&a`<span>gate ${r.gate}</span>`}</div></div>`;return[t.length?t.map(({s:r,prs:i})=>a`<${Z} key=${"stack:"+r.id}><h2>${xs(r,i)} \xB7 floor ${r.floor}${r.open?"":" \\xB7 history"}</h2><div class="cards">${i.map(n)}</div><//>`):a`<div class="empty">no PR evidence</div>`,Cs(e)]}var Ds=({inp:e})=>e.view==="cards"?Wr(e):Fr(e);var Pt=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),As=e=>e.live||e.starting?"":" dim";function Vr(e){let t=ge(e);return a`<tr key=${e.trapId} class=${"rowhead"+As(e)} onClick=${w("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness??""}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${ve(e)}${J(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${yt(e)}</td><td>${e.catches.length}</td><td>${z(e)}</td></tr>`}function Kr(e){let t=ge(e),n=yt(e),s=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return a`<div key=${e.trapId} class=${"card"+As(e)} onClick=${w("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b><span class=${"badge"+U(Pt(e))} title=${H(Pt(e))}>${Pt(e)}</span></div><div class="meta" title=${s}>${s}</div>${e.worktree&&a`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${Oe(e)}>${ve(e)}</div>${J(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 ${e.catches.length} catch${e.catches.length===1?"":"es"}${n&&[" \\xB7 ",n]}</span><span class="footact">${z(e)}</span></div></div>`}function Rs({inp:e}){let t=e.list.map(s=>s.x),n=a`<div class="tabhead"><${Be} /></div>`;return e.view==="cards"?[n,t.length?a`<div class="cards">${t.map(Kr)}</div>`:a`<div class="empty">no traps soaking</div>`]:[n,R(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(Vr),"no traps soaking")]}function Ps({state:e}){let t=e.snapshot,n=e.route,s=t&&pn(t,{st:e.prefs,modal:e.modal},Date.now()),r=o=>!s||o!==n?null:o==="deck"?a`<${hs} inp=${s.deck} drafts=${e.drafts} focus=${e.focusDecision} extensions=${t?.answerLimits?.extensions??[]} />`:o==="dispatches"?a`<${gs} inp=${s.dispatches} />`:o==="traps"?a`<${Rs} inp=${s.traps} />`:o==="prs"?a`<${Ds} inp=${s.prs} />`:o==="reports"?a`<${ns} inp=${s.reports} />`:a`<${Ts} inp=${s.notices} />`,i=o=>{o.target===o.currentTarget&&te()};return[a`<${vs} d=${t} inp=${s} tab=${n} st=${e.prefs} stale=${e.stale} />`,ce.map(o=>a`<main key=${o} id=${"page-"+o} class=${t&&o===n?"tabpage on":"tabpage"}><div id=${o}>${r(o)}</div></main>`),a`<footer id="foot"><${_s} d=${t} /></footer>`,a`<div id="lobs"><${ks} state=${e} /></div>`,a`<${Ke} box=${e.lightbox} />`,a`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${i}><div class="modal" id="modalbox"><${Gs} snapshot=${t} modal=${e.modal} prefs=${e.prefs} /></div></div>`]}function zr({view:e}){if(e.state==="loading")return a`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return a`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return a`<main class="reportview">\n <h1>${t.title}</h1>\n <${us} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Is({view:e,box:t}){return[a`<${zr} view=${e} />`,a`<${Ke} box=${t} />`]}async function Ms(e){let t=encodeURIComponent(e),n;try{let s=await fetch(`/report/${t}/meta`);if(!s.ok)return{state:"error",error:`report not found (${s.status})`};n=JSON.parse(await s.text())}catch{return{state:"error",error:"report could not be read"}}try{let s=await fetch(dn(e));return s.ok?{state:"ready",report:n,text:await s.text()}:{state:"ready",report:n,error:`report not found (${s.status})`}}catch{return{state:"ready",report:n,error:"report could not be read"}}}var It=!1,ke=null;async function Mt(){if(!It){It=!0;try{let e=await fetch("/data");if(!e.ok)throw new Error(`/data answered ${e.status}`);ut(await e.json())}catch{Gn()}finally{It=!1}}}var Ls=()=>{ke||(ke=setInterval(Mt,2e3))},Jr=()=>{ke&&clearInterval(ke),ke=null};function Es(){document.addEventListener("visibilitychange",()=>{document.hidden?Jr():(Mt(),Ls())}),Mt(),document.hidden||Ls()}var Lt=()=>nn(location.hash),Ns=e=>window.addEventListener("hashchange",()=>e(Lt()));function Us(){let e=ln(location.hash);return e===null?!1:(location.replace(D(e)),!0)}var js=()=>({snapshot:void 0,route:Lt(),prefs:mn(),modal:null,stale:!1,lobHidden:gn(),spriteOk:null,focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:ot(location.hash),lightbox:null}),Hs=cn(location.pathname);Hs!==null?Xr(Hs):Us()||Qr();function Xr(e){dt(js());let t={state:"loading"},n=()=>nt(Q(Is,{view:t,box:_().lightbox}),document.body);document.title="report \\xB7 lobstah glass",pt(n),n(),Ms(e).then(s=>{t=s,s.state==="ready"&&(document.title=`${s.report.title} \\xB7 lobstah glass`),n()}),document.addEventListener("keydown",s=>{s.key==="Escape"&&_().lightbox&&ee()})}function Qr(){dt(js());let e,t=!1,n=()=>{let o=_(),c=o.route!==e||!t&&!!o.snapshot;nt(Q(Ps,{state:o}),document.body),e=o.route,t||(t=!!o.snapshot),c&&window.scrollTo(0,0),o.focusDecision||(s=null),r(o.focusDecision)},s=null;function r(o){if(!o||o===s)return;let c=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===o);c&&(s=o,c.scrollIntoView?.({block:"center"}))}pt(n),n();let i=new Image;i.onload=()=>S({spriteOk:!0}),i.onerror=()=>S({spriteOk:!1}),i.src="/lob-sprite.png",Ns(o=>{if(Us())return;let c=ot(location.hash);c!==_().focusDecision&&(s=null),S({route:o,focusDecision:c})}),document.addEventListener("keydown",o=>{o.key==="Escape"&&(_().lightbox?ee():te())}),Es()}})();</script>\n </body>\n</html>\n';
14133
+ var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}.older{padding:8px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:10;padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch.click{cursor:pointer}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.lob .sprite{width:72px;height:56px;background:url(/lob-sprite.png) 0 0 no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .fallback{display:inline-block;animation:waddle 0.45s ease-in-out infinite alternate}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.lob .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}@keyframes waddle{from{transform:rotate(-8deg) translateY(0)}to{transform:rotate(8deg) translateY(-3px)}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dcards{display:flex;flex-direction:column;gap:10px}.dcard{width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.dcard.focus{animation:dflash 1.6s ease-out 1}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dcard.answered{opacity:0.7}.dcard .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2)}.dcard .dtop .badge{margin-top:0}.dcard .dtitle{white-space:pre-wrap;overflow-wrap:anywhere}.dcard .dmeta{font-size:11px;margin-left:auto}.dcard .dlink{color:var(--link)}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;max-width:100%;overflow-x:auto;white-space:pre;overflow-wrap:normal}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{max-width:100%;overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left;white-space:normal;overflow-wrap:anywhere;min-width:12ch}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{width:100%;max-width:1100px;margin:0 auto;padding:20px 16px 48px;overflow-wrap:anywhere}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}@media(max-width:600px){.reportview{padding:12px 0 32px}.reportview .mdpage{padding:8px 12px}}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var Ie,b,Vt,Zn,H,Ht,Kt,zt,et,De,de,Jt,nt,tt,st,Yn,Pe={},Re=[],er=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Me=Array.isArray;function O(e,t){for(var s in t)e[s]=t[s];return e}function rt(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Q(e,t,s){var n,a,r,o={};for(r in t)r=="key"?n=t[r]:r=="ref"?a=t[r]:o[r]=t[r];if(arguments.length>2&&(o.children=arguments.length>3?Ie.call(arguments,2):s),typeof e=="function"&&e.defaultProps!=null)for(r in e.defaultProps)o[r]===void 0&&(o[r]=e.defaultProps[r]);return Ce(e,o,n,a,null)}function Ce(e,t,s,n,a){var r={type:e,props:t,key:s,ref:n,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:a??++Vt,__i:-1,__u:0};return a==null&&b.vnode!=null&&b.vnode(r),r}function Z(e){return e.children}function Te(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var s;t<e.__k.length;t++)if((s=e.__k[t])!=null&&s.__e!=null)return s.__e;return typeof e.type=="function"?V(e):null}function tr(e){if(e.__P&&e.__d){var t=e.__v,s=t.__e,n=[],a=[],r=O({},t);r.__v=t.__v+1,b.vnode&&b.vnode(r),at(e.__P,r,t,e.__n,e.__P.namespaceURI,32&t.__u?[s]:null,n,s??V(t),!!(32&t.__u),a),r.__v=t.__v,r.__.__k[r.__i]=r,es(n,r,a),t.__e=t.__=null,r.__e!=s&&Xt(r)}}function Xt(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Xt(e)}function qt(e){(!e.__d&&(e.__d=!0)&&H.push(e)&&!Ae.__r++||Ht!=b.debounceRendering)&&((Ht=b.debounceRendering)||Kt)(Ae)}function Ae(){try{for(var e,t=1;H.length;)H.length>t&&H.sort(zt),e=H.shift(),t=H.length,tr(e)}finally{H.length=Ae.__r=0}}function Qt(e,t,s,n,a,r,o,l,u,c,f){var h,d,m,p,g,x,S=n&&n.__k||Re,_=t.length;for(u=sr(s,t,S,u,_),h=0;h<_;h++)(m=s.__k[h])!=null&&(d=m.__i!=-1&&S[m.__i]||Pe,m.__i=h,x=at(e,m,d,a,r,o,l,u,c,f),p=m.__e,m.ref&&d.ref!=m.ref&&(d.ref&&ot(d.ref,null,m),f.push(m.ref,m.__c||p,m)),g==null&&p!=null&&(g=p),4&m.__u?(u=Zt(m,u,e),d.__e&&(d.__e=null)):typeof m.type=="function"&&x!==void 0?u=x:p&&(u=p.nextSibling),m.__u&=-7);return s.__e=g,u}function sr(e,t,s,n,a){var r,o,l,u,c,f=s.length,h=f,d=0;for(e.__k=new Array(a),r=0;r<a;r++)(o=t[r])!=null&&typeof o!="boolean"&&typeof o!="function"?(typeof o=="string"||typeof o=="number"||typeof o=="bigint"||o.constructor==String?o=e.__k[r]=Ce(null,o,null,null,null):Me(o)?o=e.__k[r]=Ce(Z,{children:o},null,null,null):o.constructor===void 0&&o.__b>0?o=e.__k[r]=Ce(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):e.__k[r]=o,u=r+d,o.__=e,o.__b=e.__b+1,l=null,(c=o.__i=nr(o,s,u,h))!=-1&&(h--,(l=s[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(a>f?d--:a<f&&d++),typeof o.type!="function"&&(o.__u|=4)):c!=u&&(c==u-1?d--:c==u+1?d++:(c>u?d--:d++,o.__u|=4))):e.__k[r]=null;if(h)for(r=0;r<f;r++)(l=s[r])!=null&&(2&l.__u)==0&&(l.__e==n&&(n=V(l)),ss(l,l));return n}function Zt(e,t,s){var n,a;if(typeof e.type=="function"){for(n=e.__k,a=0;n&&a<n.length;a++)n[a]&&(n[a].__=e,t=Zt(n[a],t,s));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=s.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function nr(e,t,s,n){var a,r,o,l=e.key,u=e.type,c=t[s],f=c!=null&&(2&c.__u)==0;if(c===null&&l==null||f&&l==c.key&&u==c.type)return s;if(n>(f?1:0)){for(a=s-1,r=s+1;a>=0||r<t.length;)if((c=t[o=a>=0?a--:r++])!=null&&(2&c.__u)==0&&l==c.key&&u==c.type)return o}return-1}function Ft(e,t,s){t[0]=="-"?e.setProperty(t,s??""):e[t]=s==null?"":typeof s!="number"||er.test(t)?s:s+"px"}function xe(e,t,s,n,a){var r,o;e:if(t=="style")if(typeof s=="string")e.style.cssText=s;else{if(typeof n=="string"&&(e.style.cssText=n=""),n)for(t in n)s&&t in s||Ft(e.style,t,"");if(s)for(t in s)n&&s[t]==n[t]||Ft(e.style,t,s[t])}else if(t[0]=="o"&&t[1]=="n")r=t!=(t=t.replace(Jt,"$1")),o=t.toLowerCase(),t=o in e||t=="onFocusOut"||t=="onFocusIn"?o.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+r]=s,s?n?s[de]=n[de]:(s[de]=nt,e.addEventListener(t,r?st:tt,r)):e.removeEventListener(t,r?st:tt,r);else{if(a=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=s??"";break e}catch{}typeof s=="function"||(s==null||s===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&s==1?"":s))}}function Wt(e){return function(t){if(this.l){var s=this.l[t.type+e];if(t[De]==null)t[De]=nt++;else if(t[De]<s[de])return;return s(b.event?b.event(t):t)}}}function at(e,t,s,n,a,r,o,l,u,c){var f,h,d,m,p,g,x,S,_,I,le,W,ce,jt,Se,Ye,M=t.type;if(t.constructor!==void 0)return null;128&s.__u&&(u=!!(32&s.__u),r=[l=t.__e=s.__e]),(f=b.__b)&&f(t);e:if(typeof M=="function"){h=o.length;try{if(_=t.props,I=M.prototype&&M.prototype.render,le=(f=M.contextType)&&n[f.__c],W=f?le?le.props.value:f.__:n,s.__c?S=(d=t.__c=s.__c).__=d.__E:(I?t.__c=d=new M(_,W):(t.__c=d=new Te(_,W),d.constructor=M,d.render=ar),le&&le.sub(d),d.state||(d.state={}),d.__n=n,m=d.__d=!0,d.__h=[],d._sb=[]),I&&d.__s==null&&(d.__s=d.state),I&&M.getDerivedStateFromProps!=null&&(d.__s==d.state&&(d.__s=O({},d.__s)),O(d.__s,M.getDerivedStateFromProps(_,d.__s))),p=d.props,g=d.state,d.__v=t,m)I&&M.getDerivedStateFromProps==null&&d.componentWillMount!=null&&d.componentWillMount(),I&&d.componentDidMount!=null&&d.__h.push(d.componentDidMount);else{if(I&&M.getDerivedStateFromProps==null&&_!==p&&d.componentWillReceiveProps!=null&&d.componentWillReceiveProps(_,W),t.__v==s.__v||!d.__e&&d.shouldComponentUpdate!=null&&d.shouldComponentUpdate(_,d.__s,W)===!1){t.__v!=s.__v&&(d.props=_,d.state=d.__s,d.__d=!1),t.__e=s.__e,t.__k=s.__k,t.__k.some(function(X){X&&(X.__=t)}),Re.push.apply(d.__h,d._sb),d._sb=[],d.__h.length&&o.push(d),l=V(s);break e}d.componentWillUpdate!=null&&d.componentWillUpdate(_,d.__s,W),I&&d.componentDidUpdate!=null&&d.__h.push(function(){d.componentDidUpdate(p,g,x)})}if(d.context=W,d.props=_,d.__P=e,d.__e=!1,ce=b.__r,jt=0,I)d.state=d.__s,d.__d=!1,ce&&ce(t),f=d.render(d.props,d.state,d.context),Re.push.apply(d.__h,d._sb),d._sb=[];else do d.__d=!1,ce&&ce(t),f=d.render(d.props,d.state,d.context),d.state=d.__s;while(d.__d&&++jt<25);d.state=d.__s,d.getChildContext!=null&&(n=O(O({},n),d.getChildContext())),I&&!m&&d.getSnapshotBeforeUpdate!=null&&(x=d.getSnapshotBeforeUpdate(p,g)),Se=f!=null&&f.type===Z&&f.key==null?ts(f.props.children):f,l=Qt(e,Me(Se)?Se:[Se],t,s,n,a,r,o,l,u,c),d.base=t.__e,t.__u&=-161,d.__h.length&&o.push(d),S&&(d.__E=d.__=null)}catch(X){if(o.length=h,t.__v=null,u||r!=null){if(X.then){for(t.__u|=u?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;r!=null&&(r[r.indexOf(l)]=null),t.__e=l}else if(r!=null)for(Ye=r.length;Ye--;)rt(r[Ye])}else t.__e=s.__e;t.__k==null&&(t.__k=s.__k||[]),X.then||Yt(t),b.__e(X,t,s)}}else r==null&&t.__v==s.__v?(t.__k=s.__k,t.__e=s.__e):l=t.__e=rr(s.__e,t,s,n,a,r,o,u,c);return(f=b.diffed)&&f(t),128&t.__u?void 0:l}function Yt(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Yt))}function es(e,t,s){for(var n=0;n<s.length;n++)ot(s[n],s[++n],s[++n]);b.__c&&b.__c(t,e),e.some(function(a){try{e=a.__h,a.__h=[],e.some(function(r){r.call(a)})}catch(r){b.__e(r,a.__v)}})}function ts(e){return typeof e!="object"||e==null||e.__b>0?e:Me(e)?e.map(ts):e.constructor!==void 0?null:O({},e)}function rr(e,t,s,n,a,r,o,l,u){var c,f,h,d,m,p,g,x=s.props||Pe,S=t.props,_=t.type;if(_=="svg"?a="http://www.w3.org/2000/svg":_=="math"?a="http://www.w3.org/1998/Math/MathML":a||(a="http://www.w3.org/1999/xhtml"),r!=null){for(c=0;c<r.length;c++)if((m=r[c])&&"setAttribute"in m==!!_&&(_?m.localName==_:m.nodeType==3)){e=m,r[c]=null;break}}if(e==null){if(_==null)return document.createTextNode(S);e=document.createElementNS(a,_,S.is&&S),l&&(b.__m&&b.__m(t,r),l=!1),r=null}if(_==null)x===S||l&&e.data==S||(e.data=S);else{if(r=_=="textarea"&&S.defaultValue!=null?null:r&&Ie.call(e.childNodes),!l&&r!=null)for(x={},c=0;c<e.attributes.length;c++)x[(m=e.attributes[c]).name]=m.value;for(c in x)m=x[c],c=="dangerouslySetInnerHTML"?h=m:c=="children"||c in S||c=="value"&&"defaultValue"in S||c=="checked"&&"defaultChecked"in S||xe(e,c,null,m,a);for(c in S)m=S[c],c=="children"?d=m:c=="dangerouslySetInnerHTML"?f=m:c=="value"?p=m:c=="checked"?g=m:l&&typeof m!="function"||x[c]===m||xe(e,c,m,x[c],a);if(f)l||h&&(f.__html==h.__html||f.__html==e.innerHTML)||(e.innerHTML=f.__html),t.__k=[];else if(h&&(e.innerHTML=""),Qt(t.type=="template"?e.content:e,Me(d)?d:[d],t,s,n,_=="foreignObject"?"http://www.w3.org/1999/xhtml":a,r,o,r?r[0]:s.__k&&V(s,0),l,u),r!=null)for(c=r.length;c--;)rt(r[c]);l&&_!="textarea"||(c="value",_=="progress"&&p==null?e.removeAttribute("value"):p!=null&&(p!==e[c]||_=="progress"&&!p||_=="option"&&p!=x[c])&&xe(e,c,p,x[c],a),c="checked",g!=null&&g!=e[c]&&xe(e,c,g,x[c],a))}return e}function ot(e,t,s){try{if(typeof e=="function"){var n=typeof e.__u=="function";n&&e.__u(),n&&t==null||(e.__u=e(t))}else e.current=t}catch(a){b.__e(a,s)}}function ss(e,t,s){var n,a;if(b.unmount&&b.unmount(e),(n=e.ref)&&(n.current&&n.current!=e.__e||ot(n,null,t)),(n=e.__c)!=null){if(n.componentWillUnmount)try{n.componentWillUnmount()}catch(r){b.__e(r,t)}n.base=n.__P=n.__n=null}if(n=e.__k)for(a=0;a<n.length;a++)n[a]&&ss(n[a],t,s||typeof e.type!="function");s||rt(e.__e),e.__c=e.__=e.__e=void 0}function ar(e,t,s){return this.constructor(e,s)}function it(e,t,s){var n,a,r,o;t==document&&(t=document.documentElement),b.__&&b.__(e,t),a=(n=typeof s=="function")?null:s&&s.__k||t.__k,r=[],o=[],at(t,e=(!n&&s||t).__k=Q(Z,null,[e]),a||Pe,Pe,t.namespaceURI,!n&&s?[s]:a?null:t.firstChild?Ie.call(t.childNodes):null,r,!n&&s?s:a?a.__e:t.firstChild,n,o),es(r,e,o),e.props.children=null}Ie=Re.slice,b={__e:function(e,t,s,n){for(var a,r,o;t=t.__;)if((a=t.__c)&&!a.__)try{if((r=a.constructor)&&r.getDerivedStateFromError!=null&&(a.setState(r.getDerivedStateFromError(e)),o=a.__d),a.componentDidCatch!=null&&(a.componentDidCatch(e,n||{}),o=a.__d),o)return a.__E=a}catch(l){e=l}throw e}},Vt=0,Zn=function(e){return e!=null&&e.constructor===void 0},Te.prototype.setState=function(e,t){var s;s=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=O({},this.state),typeof e=="function"&&(e=e(O({},s),this.props)),e&&O(s,e),e!=null&&this.__v&&(t&&this._sb.push(t),qt(this))},Te.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),qt(this))},Te.prototype.render=Z,H=[],Kt=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,zt=function(e,t){return e.__v.__b-t.__v.__b},Ae.__r=0,et=Math.random().toString(8),De="__d"+et,de="__a"+et,Jt=/(PointerCapture)$|Capture$/i,nt=0,tt=Wt(!1),st=Wt(!0),Yn=0;function or(e){return{id:e,lane:"work",bucket:"done",repo:"",title:"",verb:"unknown",sort:0}}var ns=new WeakMap;function lt(e,t){let s=e.dispatches||[],n=ns.get(s);return n||ns.set(s,n=new Map(s.map(a=>[a.id,a]))),{...t,catches:(t.catches||[]).map(a=>n.get(a)??or(a))}}function L(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var pe=["deck","dispatches","traps","prs","reports","notices"];function is(e){let t=String(e||"").replace(/^#/,"");return pe.includes(t)?t:"deck"}var ct=e=>`#decision/${encodeURIComponent(e)}`;function dt(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var pt=(e,t,s)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,ls=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,cs=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),ut=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function ft(e,t){let s=new Map(t.map(a=>[a.key,a])),n=[];for(let a of e)if(a.kind==="decision"){let r=s.get(a.key);if(!r)continue;n.push({kind:"decision",key:r.key,title:r.title,detail:r.detail,options:r.options,attachments:r.attachments,...r.dispatch?{dispatch:r.dispatch,lane:r.lane??"work"}:{},...r.repo?{repo:r.repo}:{},at:r.askedAt})}else a.kind==="question"&&n.push({kind:"question",key:a.key,verb:a.verb,note:a.note||a.verb,dispatch:a.id,lane:a.lane,...a.repo?{repo:a.repo}:{},at:a.at??""});return n.sort((a,r)=>r.at.localeCompare(a.at)||a.key.localeCompare(r.key))}function ds(e){let t=(e.text??"").split(`\n`).map(s=>s.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function ps(e,t){return(e.reports||[]).find(s=>s.key===`report:${t.lane}:${t.id}`)}function ue(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var rs=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),T=e=>`/report/${encodeURIComponent(e)}`;function us(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function fs(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var ms=e=>`/report/${encodeURIComponent(e)}/md`,mt=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`,ht={dispatches:[],notices:[],prs:[],stacks:[]};function hs(e,t){if(t.kind==="dispatches"){let a=new Set(e.dispatches.map(r=>r.lane+":"+r.id));return{...e,dispatches:[...e.dispatches,...t.items.filter(r=>!a.has(r.lane+":"+r.id))]}}if(t.kind==="notices"){let a=new Set(e.notices.map(r=>r.seq));return{...e,notices:[...e.notices,...t.items.filter(r=>!a.has(r.seq))]}}let s=new Set(e.prs.map(a=>a.key)),n=new Set(e.stacks.map(a=>a.id));return{...e,prs:[...e.prs,...t.items.filter(a=>!s.has(a.key))],stacks:[...e.stacks,...t.stacks.filter(a=>!n.has(a.id))]}}function gs(e,t){if(t===ht||!t.dispatches.length&&!t.notices.length&&!t.prs.length)return e;let s=new Set(e.dispatches.map(o=>o.lane+":"+o.id)),n=new Set(e.notices.map(o=>o.seq)),a=new Set(e.prs.map(o=>o.key)),r=new Set(e.stacks.map(o=>o.id));return{...e,dispatches:[...e.dispatches,...t.dispatches.filter(o=>!s.has(o.lane+":"+o.id))],notices:[...e.notices,...t.notices.filter(o=>!n.has(o.seq))],prs:[...e.prs,...t.prs.filter(o=>!a.has(o.key))],stacks:[...e.stacks,...t.stacks.filter(o=>!r.has(o.id))]}}function vs(e,t,s){return Math.max(0,(e.older?.[s]??0)-t[s].length)}function $s(e,t){return{...e,now:t.now,daemon:e.daemon&&t.daemon!==void 0?{...e.daemon,heartbeat:t.daemon}:e.daemon,helms:e.helms.map(s=>t.helms[s.grounds]!==void 0?{...s,heartbeatAt:t.helms[s.grounds]}:s),traps:e.traps.map(s=>t.traps[s.trapId]?{...s,...t.traps[s.trapId]}:s)}}function as(e,t,s){return!!e&&s-Date.parse(e)>t}function os(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let s=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.title||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(s))return!1}return!0}function Y(e,t,s){if(!t)return null;if(t.type==="helm")return e.helms.find(a=>a.grounds===t.key)||null;if(t.type==="dispatch"){let a=e.dispatches.find(o=>o.lane+":"+o.id===t.key),r=s&&s.key===t.key?s.data:void 0;return a&&r?{...a,...r}:a??r??null}if(t.type==="pr")return(e.prs||[]).find(a=>a.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(a=>a.trapId===t.key);return n?lt(e,n):null}function _s(e,t,s){let n=t.st,a=String(n.q||"").toLowerCase(),r=(...p)=>!a||p.join(" ").toLowerCase().includes(a),o=p=>({x:p,stale:as(p.heartbeatAt,18e5,s)}),l=Y(e,t.modal,t.detail),u=(p,g)=>!!p&&s-Date.parse(p)<=g,c=p=>Math.max(0,...(p.notices||[]).filter(g=>g.kind==="trap-stowed"||g.kind==="trap-ghosted").map(g=>Date.parse(g.at)||0)),f=p=>p.live||!!p.starting||!!p.requested,h=[...e.traps||[]].sort((p,g)=>Number(f(g))-Number(f(p))||Number(g.live)-Number(p.live)||(p.live?(Date.parse(p.signedOnAt||"")||0)-(Date.parse(g.signedOnAt||"")||0):c(g)-c(p))||(p.name??p.trapId).localeCompare(g.name??g.trapId)),d=h.filter(p=>(f(p)||(p.notices||[]).some(g=>(g.kind==="trap-stowed"||g.kind==="trap-ghosted")&&u(g.at,36e5)))&&r(p.name,p.trapId,p.repo,p.worktree)),m=({ageSecs:p,...g})=>g;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&as(e.daemon.heartbeat,9e4,s),helms:e.helms.map(o)},deck:{view:n.view,decisions:ft(e.attention||[],e.decisions||[]).filter(p=>p.kind==="decision"?r(p.kind,p.repo,p.title,p.dispatch,p.detail):r(p.kind,p.repo,p.note,p.dispatch)),prAttention:(e.attention||[]).filter(p=>p.kind&&p.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(p=>u(p.at,864e5)&&r(p.repo,p.note,p.id)).sort((p,g)=>Date.parse(g.at)-Date.parse(p.at)).slice(0,8),reports:(e.reports||[]).filter(p=>r(p.title,p.author,p.dispatch,p.repo)).sort(rs),inflight:e.dispatches.filter(p=>p.bucket!=="done"&&os(p,{...n,lane:"",repo:"",verb:""})),traps:d.map(p=>o(lt(e,p))),stacks:(e.stacks||[]).filter(p=>p.open&&r(p.repo,p.numbers.join(" "))),prs:(e.prs||[]).filter(p=>p.state==="OPEN"),error:e.attentionError},dispatches:{view:n.view,chain:n.chain,list:e.dispatches.filter(p=>os(p,n))},traps:{view:n.view,list:h.filter(p=>(!n.repo||p.repo===n.repo)&&r(p.name,p.trapId,p.repo,p.worktree,p.harness)).map(p=>o(lt(e,p)))},prs:{view:n.view,stacks:(e.stacks||[]).filter(p=>!n.repo||p.repo===n.repo),prs:(e.prs||[]).filter(p=>(!n.repo||p.repo===n.repo)&&r(p.number,p.title,p.url,p.state,p.baseRefName,p.headRefName)),watches:(e.watches||[]).filter(p=>!String(p.key).startsWith("pr:"))},reports:{view:n.view,list:(e.reports||[]).filter(p=>(!n.repo||p.repo===n.repo)&&r(p.title,p.author,p.dispatch,p.repo)).sort(rs)},notices:{list:(e.notices||[]).filter(p=>(!n.repo||!p.repo||p.repo===n.repo)&&(!n.noticeKind||p.kind===n.noticeKind)&&r(p.kind,p.text,p.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:l&&o(l),prefs:t.modal&&t.modal.type==="settings"?{view:n.view,lobs:n.lobs}:void 0}}}function bs(e,t){let s=(e.prs||[]).find(l=>l.key===t);if(!s)return null;let n=(e.stacks||[]).find(l=>l.id===s.stackId),a=new Map((e.dispatches||[]).map(l=>[l.id,l])),r=(s.dispatchIds||[]).map(l=>{let u=a.get(l);return u?{id:l,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:l,culled:!0}}),o=s.watch;return{pr:s,stack:n?{numbers:n.numbers,titles:n.numbers.map(l=>(e.prs||[]).find(u=>u.stackId===n.id&&u.number===l)?.title||""),position:s.position+1,size:n.numbers.length,floor:n.floor,nextNumber:n.nextNumber,nextMergeable:!!s.nextMergeable,blockedBy:s.blockedBy}:null,chain:r,watch:o?{key:o.key,owner:o.owner||"",lastCheckedAt:o.lastCheckedAt||null,lastError:o.lastError||null,cursor:o.cursor}:null}}function ks(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function ys(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function ws(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function Gs(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function Ss(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var fe,Le;function me(e){if(e.snapshot)return(Le?.snapshot!==e.snapshot||Le.older!==e.older)&&(Le={snapshot:e.snapshot,older:e.older,view:gs(e.snapshot,e.older)}),Le.view}var xs=new Set,$=()=>fe;function gt(e){fe=e}function k(e){fe={...fe,...e};for(let t of xs)t(fe)}var vt=e=>{xs.add(e)};function ir(e,t){let s=me(e);return s&&Y(s,t,e.detail)?!0:t.type==="dispatch"&&e.detail?.key===t.key&&!e.detail.error}function Ee(e){let{modal:t,drafts:s,older:n,detail:a}=$(),r=new Set(ft(e.attention||[],e.decisions||[]).map(l=>l.key)),o=Object.fromEntries(Object.entries(s).filter(([l])=>r.has(l)));k({snapshot:e,stale:!1,modal:t&&ir({snapshot:e,older:n,detail:a},t)?t:null,...Object.keys(o).length!==Object.keys(s).length?{drafts:o}:{}})}function Ds(e){let{snapshot:t}=$();k({stale:!1,...t?{snapshot:$s(t,e)}:{}})}async function $t(){let{modal:e}=$();if(e?.type!=="dispatch")return;let t=e.key,s=t.slice(t.indexOf(":")+1),n;try{let r=await fetch(`/data/dispatch/${encodeURIComponent(s)}`,{cache:"no-store"});n=r.ok?{data:await r.json()}:{error:r.status===404?"this dispatch is gone":`the detail answered ${r.status}`}}catch{n={error:"the detail could not be read"}}let a=$();a.modal?.type!=="dispatch"||a.modal.key!==t||n.error&&a.detail?.key===t&&a.detail.data||k({detail:{key:t,...n}})}async function Cs(e){let{olderLoading:t,older:s}=$();if(!t){k({olderLoading:e,olderError:null});try{let n=await fetch(`/data/older?kind=${e}&offset=${s[e].length}&limit=50`,{cache:"no-store"});if(!n.ok)throw new Error(`/data/older answered ${n.status}`);let a=await n.json();k({older:hs($().older,a),olderLoading:null})}catch{k({olderLoading:null,olderError:{kind:e,text:"older records could not be read"}})}}}var Ts=()=>({text:"",files:[]});function R(e,t){let s=$().drafts;k({drafts:{...s,[e]:{...s[e]??Ts(),...t}}})}function Ps(e,t){let s=$().drafts[e]?.option;R(e,{option:s===t?void 0:t,error:void 0})}var lr=e=>new Promise((t,s)=>{let n=new FileReader;n.onload=()=>t(String(n.result).replace(/^data:[^,]*,/,"")),n.onerror=()=>s(n.error),n.readAsDataURL(e)});async function _t(e,t){let s=$().snapshot?.answerLimits,n=[...t??[]],a=$().drafts[e]?.files??[],r=[],o=[];for(let l of n){let u=(/\\.[^.]+$/.exec(l.name)?.[0]??"").toLowerCase();if(s&&!s.extensions.includes(u))r.push(`${l.name}: type not accepted`);else if(s&&l.size>s.maxBytes)r.push(`${l.name}: larger than ${s.maxBytes} bytes`);else if(s&&a.length+o.length>=s.maxFiles)r.push(`${l.name}: at most ${s.maxFiles} files`);else try{o.push({name:l.name,bytes:l.size,data:await lr(l)})}catch{r.push(`${l.name}: could not be read`)}}R(e,{files:[...$().drafts[e]?.files??[],...o],error:r.length?r.join("; "):void 0})}var cr={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function Rs(e,t){let s=[...t.clipboardData?.items??[]],n=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),a=s.filter(r=>r.kind==="file"&&r.type.startsWith("image/")).map(r=>r.getAsFile()).filter(r=>r!==null).map((r,o,l)=>{let u=`pasted-${n}${l.length>1?`-${o+1}`:""}${cr[r.type]??".png"}`;return new File([r],u,{type:r.type})});a.length&&await _t(e,a)}function As(e,t){let s=($().drafts[e]?.files??[]).filter((n,a)=>a!==t);R(e,{files:s})}async function Is(e){let t=$().snapshot?.focusToken,s=$().drafts[e]??Ts();if(!(s.sending||s.sent)){if(!s.option&&!s.text.trim()&&s.files.length===0){R(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){R(e,{error:"This page has no answer token; reload it."});return}R(e,{sending:!0,error:void 0});try{let n=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...s.option?{option:s.option}:{},...s.text.trim()?{text:s.text}:{},files:s.files.map(r=>({name:r.name,data:r.data}))}})}),a=await n.json();if(!n.ok||!a.ok){R(e,{sending:!1,error:a.reason??`The answer was refused (${n.status}).`});return}R(e,{sending:!1,sent:ds({option:s.option,text:s.text,files:s.files.length})})}catch{R(e,{sending:!1,error:"The answer could not be sent."})}}}var Ms=()=>k({stale:!0}),Ls=(e,t)=>k({lightbox:{src:e,name:t}}),ee=()=>k({lightbox:null});function E(e,t){let s=$(),n={type:e,key:t};if(e==="dispatch"){k({modal:n,detail:s.detail?.key===t?s.detail:{key:t}}),$t();return}let a=me(s);k({modal:!a||Y(a,n)?n:null,detail:null})}function te(){k({modal:null,detail:null})}async function Es(e){let t=$().snapshot?.focusToken;if(t)try{let n=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();k({focusResults:{...$().focusResults,[e]:n.message??n.reason??"Window focus failed."}})}catch{k({focusResults:{...$().focusResults,[e]:"Window focus failed."}})}}async function Os(e,t){let s=$().snapshot?.focusToken;if(!s)return"The glass has no page token yet.";try{let a=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!a.ok)return a.reason??"The request was refused."}catch{return"The request could not be sent."}try{Ee(await(await fetch("/data")).json())}catch{}}function N(e){let t={...$().prefs,...e};ws(t),k({prefs:t})}function Ns(e){e!=="table"&&e!=="cards"||N({view:e})}var Bs=e=>N({lobs:e==="on"});function Us(e,t){let s={...$().lobHidden,[e]:t};Ss(s),setTimeout(()=>k({lobHidden:s}),0)}async function js(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var qs=function(e,t,s,n){var a;t[0]=0;for(var r=1;r<t.length;r++){var o=t[r++],l=t[r]?(t[0]|=o?1:2,s[t[r++]]):t[++r];o===3?n[0]=l:o===4?n[1]=Object.assign(n[1]||{},l):o===5?(n[1]=n[1]||{})[t[++r]]=l:o===6?n[1][t[++r]]+=l+"":o?(a=e.apply(l,qs(e,l,s,["",null])),n.push(a),l[0]?t[0]|=2:(t[r-2]=0,t[r]=a)):n.push(l)}return n},Hs=new Map;function Fs(e){var t=Hs.get(this);return t||(t=new Map,Hs.set(this,t)),(t=qs(this,t.get(e)||(t.set(e,t=(function(s){for(var n,a,r=1,o="",l="",u=[0],c=function(d){r===1&&(d||(o=o.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,d,o):r===3&&(d||o)?(u.push(3,d,o),r=2):r===2&&o==="..."&&d?u.push(4,d,0):r===2&&o&&!d?u.push(5,0,!0,o):r>=5&&((o||!d&&r===5)&&(u.push(r,0,o,a),r=6),d&&(u.push(r,d,0,a),r=6)),o=""},f=0;f<s.length;f++){f&&(r===1&&c(),c(f));for(var h=0;h<s[f].length;h++)n=s[f][h],r===1?n==="<"?(c(),u=[u],r=3):o+=n:r===4?o==="--"&&n===">"?(r=1,o=""):o=n+o[0]:l?n===l?l="":o+=n:n===\'"\'||n==="\'"?l=n:n===">"?(c(),r=1):r&&(n==="="?(r=5,a=o,o=""):n==="/"&&(r<5||s[f][h+1]===">")?(c(),r===3&&(u=u[0]),r=u,(u=u[0]).push(2,0,r),r=0):n===" "||n===" "||n===`\n`||n==="\\r"?(c(),r=2):o+=n),r===3&&o==="!--"&&(r=4,u=u[0])}return c(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Ws=e=>Array.isArray(e)?e.map(Ws):e===""?null:e,dr=(e,t,...s)=>Q(e,t,...s.map(Ws)),i=Fs.bind(dr);var Ne,w,bt,Vs,kt=0,en=[],G=b,Ks=G.__b,zs=G.__r,Js=G.diffed,Xs=G.__c,Qs=G.unmount,Zs=G.__;function tn(e,t){G.__h&&G.__h(w,e,kt||t),kt=0;var s=w.__H||(w.__H={__:[],__h:[]});return e>=s.__.length&&s.__.push({}),s.__[e]}function se(e){return kt=1,pr(nn,e)}function pr(e,t,s){var n=tn(Ne++,2);if(n.t=e,!n.__c&&(n.__=[s?s(t):nn(void 0,t),function(l){var u=n.__N?n.__N[0]:n.__[0],c=n.t(u,l);u!==c&&(n.__N=[c,n.__[1]],n.__c.setState({}))}],n.__c=w,!w.__f)){var a=function(l,u,c){if(!n.__c.__H)return!0;var f=!1,h=n.__c.props!==l;if(n.__c.__H.__.some(function(m){if(m.__N){f=!0;var p=m.__[0];m.__=m.__N,m.__N=void 0,p!==m.__[0]&&(h=!0)}}),r){var d=r.call(this,l,u,c);return f?d||h:d}return!f||h};w.__f=!0;var r=w.shouldComponentUpdate,o=w.componentWillUpdate;w.componentWillUpdate=function(l,u,c){if(this.__e){var f=r;r=void 0,a(l,u,c),r=f}o&&o.call(this,l,u,c)},w.shouldComponentUpdate=a}return n.__N||n.__}function sn(e,t){var s=tn(Ne++,7);return mr(s.__H,t)&&(s.__=e(),s.__H=t,s.__h=e),s.__}function ur(){for(var e;e=en.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(Oe),t.__h.some(yt),t.__h=[]}catch(s){t.__h=[],G.__e(s,e.__v)}}}G.__b=function(e){w=null,Ks&&Ks(e)},G.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Zs&&Zs(e,t)},G.__r=function(e){zs&&zs(e),Ne=0;var t=(w=e.__c).__H;t&&(bt===w?(t.__h=[],w.__h=[],t.__.some(function(s){s.__N&&(s.__=s.__N),s.u=s.__N=void 0})):(t.__h.some(Oe),t.__h.some(yt),t.__h=[],Ne=0)),bt=w},G.diffed=function(e){Js&&Js(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(en.push(t)!==1&&Vs===G.requestAnimationFrame||((Vs=G.requestAnimationFrame)||fr)(ur)),t.__H.__.some(function(s){s.u&&(s.__H=s.u,s.u=void 0)})),bt=w=null},G.__c=function(e,t){t.some(function(s){try{s.__h.some(Oe),s.__h=s.__h.filter(function(n){return!n.__||yt(n)})}catch(n){t.some(function(a){a.__h&&(a.__h=[])}),t=[],G.__e(n,s.__v)}}),Xs&&Xs(e,t)},G.unmount=function(e){Qs&&Qs(e);var t,s=e.__c;s&&s.__H&&(s.__H.__.some(function(n){try{Oe(n)}catch(a){t=a}}),s.__H=void 0,t&&G.__e(t,s.__v))};var Ys=typeof requestAnimationFrame=="function";function fr(e){var t,s=function(){clearTimeout(n),Ys&&cancelAnimationFrame(t),setTimeout(e)},n=setTimeout(s,35);Ys&&(t=requestAnimationFrame(s))}function Oe(e){var t=w,s=e.__c;typeof s=="function"&&(e.__c=void 0,s()),w=t}function yt(e){var t=w;e.__c=e.__(),w=t}function mr(e,t){return!e||e.length!==t.length||t.some(function(s,n){return s!==e[n]})}function nn(e,t){return typeof t=="function"?t(e):t}var K=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>i`<span data-age=${e??""}>${K(e)}</span>`,Ue=e=>e.activity&&i`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,hr=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,je=e=>{let t=e.waiting;if(!t)return;let s=hr(t.link),n=s;try{s&&(n=new URL(s).host+new URL(s).pathname.replace(/\\/$/,""))}catch{n=s}return i`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${s&&[" \\xB7 ",i`<a href=${s} target="_blank" rel="noopener noreferrer" onClick=${D}>${n}</a>`]}</div>`},gr=12,rn=e=>e!==void 0&&e.length>gr,B=e=>rn(e)?e:void 0,U=e=>rn(e)?" long":"",y=(e,t)=>()=>E(e,t),D=e=>e.stopPropagation(),vr=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function z(e){if(e.requested)return i`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return i`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let n=vr(e);return n?i`<span class="dim">Resume: <code onClick=${D}>${n}</code></span>`:i`<span class="dim">Resume command unavailable</span>`}let t=$();return!t.snapshot?.focusSupported&&!e.link?i`<span class="dim">Window focus is not supported here</span>`:i`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${n=>{D(n),Es(e.trapId)}}>\u2197 open</button>${e.link&&[" ",i`<a href=${e.link} onClick=${D}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",i`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function A(e,t,s){return t.length?i`<div class="wrap"><table><tbody><tr>${e.map(n=>i`<th>${n}</th>`)}</tr>${t}</tbody></table></div>`:i`<div class="empty">${s}</div>`}function re({kind:e,more:t}){return!t.left&&!t.error?null:i`<div class="older">${t.left>0&&i`<button class="btn" disabled=${t.loading} onClick=${()=>{Cs(e)}}>${t.loading?"loading\\u2026":`show ${Math.min(t.left,50)} older (of ${t.left})`}</button>`}${t.error&&i` <span class="bad">${t.error}</span>`}</div>`}function $r({text:e}){let[t,s]=se(null),n=a=>async()=>{await js(e)&&(s(a),setTimeout(()=>s(null),1e3))};return i`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${n("code")}>${e}</code><button title="copy" onClick=${n("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var C=(e,t)=>i`<${$r} key=${t} text=${e} />`,he=(e,t,s="thumb")=>i`<button\n type="button"\n class=${s}\n title=${"view "+t}\n onClick=${n=>{D(n),Ls(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,ne=(e,t)=>e.map(s=>[i`<div class="sub">${s.name} \xB7 ${s.type} \xB7 ${s.bytes} bytes</div>`,t&&cs(s.name)&&he(t(s.name),s.name),C(s.path)]),an=/\\bwt:([a-z0-9][a-z0-9-]*)/g,wt=e=>$().snapshot?.trapNames?.[e],j=e=>e.replace(an,(t,s,n)=>{let a=wt(s);return a&&!on(e,n,t.length,a)?a:t}),on=(e,t,s,n)=>e.slice(Math.max(0,t-n.length-2),t)===`${n} (`&&e[t+s]===")";function He(e){let t="wt:"+e,s=wt(e)??t;return $().snapshot?.traps.some(a=>a.trapId===e)?i`<a class="trapname" href="#traps" title=${t} onClick=${a=>{a.preventDefault(),D(a),E("trap",e)}}>${s}</a>`:i`<span class="trapname" title=${t}>${s}</span>`}var Be=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?He(t[1]):e};function P(e,t=1/0){let s=[],n=0;for(let l of e.matchAll(an)){l.index>n&&s.push({text:e.slice(n,l.index)});let u=wt(l[1]);s.push(u&&on(e,l.index,l[0].length,u)?{text:l[0]}:{text:u??l[0],trapId:l[1]}),n=l.index+l[0].length}n<e.length&&s.push({text:e.slice(n)});let a=s.reduce((l,u)=>l+u.text.length,0),r=a>t?t-1:1/0,o=[];for(let l of s){if(r<=0)break;l.text.length<=r?o.push(l.trapId?He(l.trapId):l.text):o.push(l.text.slice(0,r)),r-=l.text.length}return a>t&&o.push("\\u2026"),o}var _r=e=>e.log.flatMap((t,s)=>[s?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...P(t.note)]:[]]),br=e=>"brief"in e;function ln(e,t){let s=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`),n=i`${e.waiting&&[i`<div class="sec">waiting</div>`,je(e)]}${e.activity&&[i`<div class="sec">activity</div>`,Ue(e)]}${s&&[i`<div class="sec">progress</div>`,i`<pre>${s}</pre>`]}`;return br(e)?i`${n}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[i`<div class="sec">attachments (${e.attachments.length})</div>`,ne(e.attachments,a=>pt(e.lane,e.id,a))]}${e.messageAttachments.length>0&&[i`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,ne(e.messageAttachments,a=>pt(e.lane,e.id,a))]}${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?_r(e):"no entries yet"}</div>${e.inbox.length>0&&[i`<div class="sec">inbox</div>`,i`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[i`<div class="sec">awaiting reply</div>`,i`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${e.evidence&&[i`<div class="sec">evidence</div>`,i`<div class="loglines">${JSON.stringify(e.evidence)}</div>`]}`:i`${n}<div class="sec">brief</div><div class=${t?.error?"bad":"dim"}>${t?.error??"loading\\u2026"}</div>${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}${e.note&&[i`<div class="sec">last note</div>`,i`<div class="loglines">${P(e.note)}</div>`]}`}function ge(e){return e.for?[Be(e.for),e.evidence&&e.evidence.deliveredTo?[" ",i`<span class="ok">\u2713delivered</span>`]:[" ",i`<span class="warn">waiting</span>`]]:e.claimedBy?Be(e.claimedBy):""}function ae(e){if(e.prList&&e.prList.length>1)return[e.prList.map((a,r)=>[r?" \\u2192 ":"",i`<a href=${a.url} target="_blank" rel="noopener" onClick=${D}>#${a.number}</a>`,a.badge&&[" ",i`<span class=${"badge "+L(a.badge)}>${a.badge.text}</span>`]]),e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let s=e.prBadge,n=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[n?n.map((a,r)=>[r?" ":"",i`<a href=${a} target="_blank" rel="noopener" onClick=${D}>#${a.split("/").pop()}</a>`]):i`<a href=${t} target="_blank" rel="noopener" onClick=${D}>PR</a>`,s&&[" ",i`<span class=${"badge "+L(s)} title=${"observed "+s.observedAt}>${s.text}</span>`],e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var kr={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready",landed:"landed",watch:"watch"},Gt={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","pr:draft":"dim",watch:"warn"},cn=e=>kr[e]||"";var qe=e=>i`<a href=${e.url} target="_blank" rel="noopener" onClick=${D}>#${e.number}</a>`,Fe=e=>i`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function oe(e,t,s){return e.flatMap((n,a)=>{let r=t.get(n)||void 0,o=n===s?i`<b title=${r}>#${n}</b>`:i`<span title=${r}>#${n}</span>`;return a?[" \\u2192 ",o]:[o]})}function ve(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function We(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function $e(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function St(e){let t=ks(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:i`<span class="dim">${t.text}</span>`}function _e(e){if(e.requested)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">requested</span>`],hb:i`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[i`<span class="dot bad"></span>`,i`<span class="bad">start failed</span>`]:[i`<span class="dot warn"></span>`,"starting"],hb:i`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">signed off</span>`],hb:i`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,s=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:s?[i`<span class="dot ok"></span>`,"listening"]:[i`<span class="dot warn"></span>`,"not listening"],hb:i`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function xt(e){if(e.requested)return{kind:"requested",helmOn:!!$().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(s=>s.id===e.claimed&&s.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function yr(e){let t=xt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var dn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",pn=e=>e.title.slice(0,40)||"(no title)";function be(e){let t=xt(e),s=i`<span class=${"dot trapdot "+yr(e)}></span>`;if(t.kind==="requested")return[s,i`<span class="dim">${un(e,t.helmOn)}</span>`];if(t.kind==="starting")return[s,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[s,i`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[s,i`<span class="dim">signed off</span>`];if(t.kind==="idle")return[s,"idle \\xB7 ",t.listening?"listening":"not listening"];let n=t.current;return t.kind==="parked"?[s,"parked \\xB7 ",dn(n)]:[s,"working \\xB7 ",i`<a href="#dispatches" onClick=${r=>{r.preventDefault(),D(r),E("dispatch",`${n.lane}:${n.id}`)}}>${n.id.slice(0,8)}</a>`," \\xB7 ",pn(n),n.activity&&[" \\xB7 ",n.activity.summary," ",v(n.activity.at)," ago"]]}var un=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function J(e){let t=e.starting?.commands;return t?.length?i`<div class="startcmds" onClick=${D}><div class="dim">start it: run one in a terminal</div>${t.map(s=>C(s.command,s.harness))}</div>`:null}function Ve(){let t=$().snapshot?.repoKeys??[],[s,n]=se(!1),[a,r]=se(""),[o,l]=se("claude"),[u,c]=se(null),f=a||t[0]||"",h=async d=>{d.preventDefault();let m=await Os(f,o);c(m??null),m||n(!1)};return s?i`<form class="newtrap" onClick=${D} onSubmit=${h}>\n <label>repo <select name="repo" value=${f} onChange=${d=>r(d.target.value)}>${t.map(d=>i`<option value=${d}>${d}</option>`)}</select></label>\n <label>harness <select name="harness" value=${o} onChange=${d=>l(d.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!f}>Request</button>\n <button class="btn" type="button" onClick=${()=>n(!1)}>Cancel</button>\n ${u&&i`<span class="bad">${u}</span>`}\n </form>`:i`<span class="newtrap"><button class="btn" onClick=${d=>(D(d),n(!0),c(null))}>+ New trap</button></span>`}function Ke(e){let t=xt(e);if(t.kind==="requested")return un(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${dn(t.current)}`;let s=t.current;return[`working \\xB7 ${s.id.slice(0,8)} \\xB7 ${pn(s)}`,s.activity&&`${s.activity.summary} ${K(s.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function Dt(e){let t=e.messages.filter(s=>s.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",i`<span class="warn">(${t} pending)</span>`]]:null}var wr=e=>()=>{window.open(T(e.key),"_blank","noopener")};function Ct(e){let t=ue(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${K(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function Gr(e){let t=Ct(e);return i`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${T(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function Sr(e){return i`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${wr(e)}><td class="grow"><a href=${T(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${ue(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function fn({inp:e}){return e.view==="cards"?e.list.length?i`<div class="cards">${e.list.map(Gr)}</div>`:i`<div class="empty">no reports match</div>`:A(["title","from","filed","acked"],e.list.map(Sr),"no reports match")}function gn(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function vn(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function mn(e,t,s,n){let a=0;for(let r=t;r<e.length;r++){if(e[r]==="\\\\"){r++;continue}if(e[r]===s)a++;else if(e[r]===n){if(a===0)return r;a--}}return-1}function hn(e,t){let s=mn(e,t+1,"[","]");if(s<0||e[s+1]!=="(")return;let n=mn(e,s+2,"(",")");if(n<0)return;let a=e.slice(s+2,n).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,s),target:a,end:n+1}}function q(e){let t=[],s="",n=()=>{s&&t.push({t:"text",v:s}),s=""};for(let a=0;a<e.length;){let r=e[a];if(r==="\\\\"&&a+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[a+1])){s+=e[a+1],a+=2;continue}if(r==="`"){let o=/^`+/.exec(e.slice(a))[0],l=e.indexOf(o,a+o.length);if(l>0){n(),t.push({t:"code",v:e.slice(a+o.length,l).replace(/^ (.*) $/,"$1")}),a=l+o.length;continue}}if(r==="!"&&e[a+1]==="["){let o=hn(e,a+1);if(o){n(),t.push({t:"img",alt:o.text,src:o.target}),a=o.end;continue}}if(r==="["){let o=hn(e,a);if(o){n(),t.push({t:"a",href:o.target,c:q(o.text)}),a=o.end;continue}}if((r==="*"||r==="_")&&e[a+1]===r){let o=e.indexOf(r+r,a+2);if(o>a+2){n(),t.push({t:"b",c:q(e.slice(a+2,o))}),a=o+2;continue}}if(r==="*"||r==="_"&&(a===0||!/\\w/.test(e[a-1]))){let o=a+1;for(;(o=e.indexOf(r,o))>0&&(e[o+1]===r||r==="_"&&/\\w/.test(e[o+1]??""));)o+=2;if(o>a+1&&e[a+1]!==" "){n(),t.push({t:"i",c:q(e.slice(a+1,o))}),a=o+1;continue}}if(r===`\n`){/ {2,}$/.test(s)||s.endsWith("\\\\")?(s=s.replace(/( {2,}|\\\\)$/,""),n(),t.push({t:"br"})):s+=" ",a++;continue}s+=r,a++}return n(),t}var $n=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,_n=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,bn=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,Rt=/^( {0,3})([-*+])\\s+(.*)$/,At=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,ze=/^ {0,3}>\\s?(.*)$/,xr=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Tt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let s=[],n="";for(let a=0;a<t.length;a++)t[a]==="\\\\"&&t[a+1]==="|"?(n+="|",a++):t[a]==="|"?(s.push(n.trim()),n=""):n+=t[a];return s.push(n.trim()),s}var Pt=e=>$n.test(e)||_n.test(e)||bn.test(e)||Rt.test(e)||At.test(e)||ze.test(e);function Je(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),s=[],n=0;for(;n<t.length;){let a=t[n];if(!a.trim()){n++;continue}let r=$n.exec(a);if(r){let f=r[1],h=[];for(n++;n<t.length&&!new RegExp(`^ {0,3}${f[0]}{${f.length},}\\\\s*$`).test(t[n]);)h.push(t[n++]);n++,s.push({t:"code",lang:r[2]??"",v:h.join(`\n`)});continue}let o=_n.exec(a);if(o){s.push({t:"h",level:o[1].length,c:q(o[2])}),n++;continue}if(bn.test(a)){s.push({t:"hr"}),n++;continue}if(ze.test(a)){let f=[];for(;n<t.length&&t[n].trim()&&(ze.test(t[n])||!Pt(t[n]));)f.push(ze.exec(t[n])?.[1]??t[n]),n++;s.push({t:"quote",c:Je(f.join(`\n`))});continue}let l=Rt.exec(a),u=At.exec(a);if(l||u){let f=!l,h=f?At:Rt,d=[];for(;n<t.length;){let m=t[n],p=h.exec(m);if(p&&p[1].length<=3){d.push([p[3]]),n++;continue}if(!m.trim()){if(n+1<t.length&&/^ {2,}\\S/.test(t[n+1])){d.at(-1).push(""),n++;continue}break}if(/^ {2,}\\S/.test(m)){d.at(-1).push(m.replace(/^ {2,4}/,"")),n++;continue}if(Pt(m))break;d.at(-1).push(m.trim()),n++}s.push({t:"list",ordered:f,start:f?Number(u[2]):1,items:d.map(m=>Je(m.join(`\n`)))});continue}if(a.includes("|")&&n+1<t.length&&xr.test(t[n+1])&&t[n+1].includes("-")){let f=Tt(a),h=Tt(t[n+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");n+=2;let d=[];for(;n<t.length&&t[n].trim()&&t[n].includes("|");){let m=Tt(t[n++]);d.push(f.map((p,g)=>q(m[g]??"")))}s.push({t:"table",align:f.map((m,p)=>h[p]??""),head:f.map(q),rows:d});continue}let c=[];for(;n<t.length&&t[n].trim()&&!Pt(t[n])&&!(c.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[n]));)c.push(t[n++].replace(/^ +/,""));if(c.length===0){s.push({t:"p",c:q(a.trim())}),n++;continue}s.push({t:"p",c:q(c.join(`\n`))})}return s}function F(e,t){return t.map(s=>{switch(s.t){case"text":return s.v;case"code":return i`<code>${s.v}</code>`;case"b":return i`<strong>${F(e,s.c)}</strong>`;case"i":return i`<em>${F(e,s.c)}</em>`;case"br":return i`<br />`;case"a":{let n=gn(s.href);return n?i`<a href=${n} target="_blank" rel="noopener noreferrer">${F(e,s.c)}</a>`:F(e,s.c)}case"img":{let n=vn(s.src);return n?he(e(n),s.alt||n,"mdimg"):i`<span class="dim">[image not shown: ${s.alt||s.src}]</span>`}}})}function Xe(e,t){return t.map(s=>{switch(s.t){case"h":{let n=F(e,s.c);return s.level===1?i`<h1>${n}</h1>`:s.level===2?i`<h2>${n}</h2>`:s.level===3?i`<h3>${n}</h3>`:i`<h4>${n}</h4>`}case"p":return i`<p>${F(e,s.c)}</p>`;case"code":return i`<pre class="mdcode"><code>${s.v}</code></pre>`;case"hr":return i`<hr />`;case"quote":return i`<blockquote>${Xe(e,s.c)}</blockquote>`;case"list":{let n=s.items.map(a=>i`<li>${Xe(e,a)}</li>`);return s.ordered?i`<ol start=${s.start}>${n}</ol>`:i`<ul>${n}</ul>`}case"table":return i`<div class="mdtable"><table><thead><tr>${s.head.map((n,a)=>i`<th style=${s.align[a]?"text-align:"+s.align[a]:void 0}>${F(e,n)}</th>`)}</tr></thead><tbody>${s.rows.map(n=>i`<tr>${n.map((a,r)=>i`<td style=${s.align[r]?"text-align:"+s.align[r]:void 0}>${F(e,a)}</td>`)}</tr>`)}</tbody></table></div>`}})}var ke=new Map;function kn(e){let t=ke.get(e);return t||(t=Je(e),ke.set(e,t),ke.size>8&&ke.delete(ke.keys().next().value)),t}function Dr({r:e,text:t}){let s=t?.text!==void 0?Xe(n=>mt(e.key,n),kn(t.text)):t?.error?i`<div class="bad">${t.error}</div>`:i`<div class="dim">loading\u2026</div>`;return i`<div class="mdpage" data-report=${e.key}>${s}</div>`}function yn({text:e,fileUrl:t}){return i`<div class="mdpage">${Xe(t,kn(e))}</div>`}function It(e){let t=ue(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",i`<span class="ok">acked</span>`]]}function wn({r:e,text:t,heading:s}){return[s&&i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${It(e)}</div>`,i`<${Dr} r=${e} text=${t} />`,e.attachments.length>0&&[i`<div class="sec">report attachments (${e.attachments.length})</div>`,ne(e.attachments,n=>mt(e.key,n))],!e.acked&&[i`<div class="sec">ack</div>`,C("lobstah attention ack "+e.key)]]}var Cr=/\\.(png|jpe?g|gif|webp)$/i,Tr=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function Pr(e,t){return t.length?i`<div class="dfiles">${t.map(s=>Cr.test(s.name)?i`<span key=${s.name}>${he(ut(e,s.name),s.name,"dimg")}</span>`:i`<div key=${s.name} class="dfile"><span>${s.name}</span> <span class="dim">\xB7 ${s.type} \xB7 ${s.bytes} bytes \xB7 </span><code>${s.path}</code></div>`)}</div>`:null}function Rr(e,t,s){let n=e.key,a=e.kind==="decision"?e.options:[],r=t?.text??"",o=t?.files??[],l=!!t?.sending;return[a.length>0&&i`<div class="doptions">${a.map(u=>i`<button key=${u} type="button" class=${"btn dopt"+(t?.option===u?" on":"")} aria-pressed=${t?.option===u?"true":"false"} disabled=${l} onClick=${()=>Ps(n,u)}>${u}</button>`)}</div>`,i`<textarea\n class="danswer"\n rows="2"\n placeholder=${a.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${r}\n disabled=${l}\n onInput=${u=>{let c=u.currentTarget;Tr(c),R(n,{text:c.value,error:void 0})}}\n onPaste=${u=>{Rs(n,u)}}\n ></textarea>`,i`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${s} disabled=${l} onChange=${u=>{let c=u.currentTarget;_t(n,c.files).then(()=>{c.value=""})}} /></label>\n ${o.map((u,c)=>i`<span key=${u.name+c} class="dchip">${u.name}<button type="button" class="dx" title="remove" disabled=${l} onClick=${()=>As(n,c)}>\xD7</button></span>`)}\n ${t?.error&&i`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${l} onClick=${()=>{Is(n)}}>${l?"Sending\\u2026":"Send"}</button>\n </div>`]}function Ar(e,t,s,n){let a=e.dispatch,r=e.lane??"work",l=[a&&i`<a class="dlink" href="#" onClick=${c=>{c.preventDefault(),y("dispatch",r+":"+a)()}}>${a.slice(0,8)}</a>`,e.repo,[v(e.at)," ago"]].filter(Boolean).flatMap((c,f)=>f?[" \\xB7 ",c]:[c]),u="dcard"+(e.kind==="question"?" plain":"")+(t?.sent?" answered":"")+(s?" focus":"");return i`<div key=${e.key} class=${u} data-decision=${e.key}>\n <div class="dtop">\n ${e.kind==="question"&&i`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${e.kind==="decision"?e.title:P(e.note)}</b>\n <span class="dmeta dim">${l}</span>\n </div>\n ${e.kind==="decision"&&e.detail.trim()&&i`<${yn} text=${e.detail} fileUrl=${c=>ut(e.key,c)} />`}\n ${e.kind==="decision"&&Pr(e.key,e.attachments)}\n ${t?.sent?i`<div class="danswered ok">answered \xB7 ${t.sent}</div>`:Rr(e,t,n)}\n </div>`}function Gn({cards:e,drafts:t,focus:s,extensions:n}){let a=n.join(","),r=e.length?i`<div class="dcards">${e.map(o=>Ar(o,t[o.key],o.key===s,a))}</div>`:i`<div class="empty">none</div>`;return i`<section class="decisions"><h2>decisions</h2>${r}</section>`}function Er(e,t){let s=e.badge&&i`<span class=${"badge "+(e.badge.tone||"dim")+U(e.badge.text)} title=${B(e.badge.text)}>${e.badge.text}</span>`;if(e.href){let n=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?i`<a key=${e.key} class=${n} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:i`<a key=${e.key} class=${n} href=${e.href} target="_blank" rel="noopener">${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?i`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&i`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:i`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",i`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var Sn=(e,t)=>e>0&&i`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function Qe(e,t,s,n,a,r){let o=t.slice(0,n),l=o.map(c=>Er(c,a)),u=o.length?a==="cards"?i`<div class="cards">${l}</div>`:l:i`<div class="empty">none</div>`;return i`<section><h2><a href=${"#"+s}>${e} \u2192</a></h2>${r&&i`<div class="tabhead">${r}</div>`}${u}${Sn(t.length-o.length,s)}</section>`}function Or(e,t,s,n){let a=t.find(c=>c.number===e.nextNumber)||t[0],r=oe(e.numbers,new Map(t.map(c=>[c.number,c.title]))),o=a?"next #"+a.number:"nothing mergeable";if(n==="cards"){let c=f=>{let h=s.get(f.key)||[],d=h.length>0&&h.every(g=>g.acked),m=h.length?h.map(g=>i`<span class=${"badge "+(Gt[g.kind]||"dim")}>${cn(g.kind)}</span>`):i`<span class=${"badge "+L(f.badge)+U(f.badge.text)} title=${B(f.badge.text)}>${f.badge.text}</span>`,p=`${f.repo} \\xB7 ${f.badge.text}${d?" \\xB7 acked":""}`;return i`<div key=${f.key} class=${"card"+(d?" acked":"")} onClick=${y("pr",f.key)}><div class="top">${Fe(f)}${m}</div><div class="meta" title=${p}>${p}</div></div>`};return i`<div key=${e.id} class="deckstack"><div class="dim">${r} \xB7 ${o}</div><div class="cards">${t.map(c)}</div></div>`}let l=t.flatMap(c=>(s.get(c.key)||[]).map(f=>i`<span class=${"badge "+(Gt[f.kind]||L(c.badge))+(f.acked?" acked":"")}>#${c.number} ${c.badge.text}</span>`)),u=l.flatMap((c,f)=>f?[" ",c]:[c]);return i`<div key=${e.id} class=${"deckline"+(a?" click":"")} onClick=${a&&y("pr",a.key)}><b>${r}</b> <span class="dim">\xB7 ${o}</span>${l.length>0&&[" \\xB7 ",u]}</div>`}function Nr(e,t){let s=e.stacks.slice(0,3),n=new Map;for(let r of e.prAttention){let o=n.get(r.key)||[];o.push(r),n.set(r.key,o)}let a=s.length?s.map(r=>Or(r,e.prs.filter(o=>o.stackId===r.id).sort((o,l)=>o.position-l.position),n,t)):i`<div class="empty">none</div>`;return i`<section><h2><a href="#prs">PRs \u2192</a></h2>${a}${Sn(e.stacks.length-s.length,"prs")}</section>`}function xn({inp:e,drafts:t,focus:s,extensions:n}){let a=e.view,r=e.inflight.map(c=>({key:c.lane+":"+c.id,title:[c.id.slice(0,8)," ",c.repo||""],badge:{text:c.verb,tone:c.verb==="needs-decision"||c.verb==="blocked"?"bad":"dim"},meta:[P(c.note||"",90),c.verbAt&&[" \\xB7 ",v(c.verbAt)," ago"],(c.for||c.claimedBy)&&[" \\xB7 ",Be(c.for||c.claimedBy)]],metaText:[j(c.note||""),c.verbAt&&`${K(c.verbAt)} ago`,j(c.for||c.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:y("dispatch",c.lane+":"+c.id)})),o=e.landed.map(c=>({key:c.key,title:[c.id.slice(0,8)," ",c.repo||"",c.unreported&&[" ",i`<span class="badge warn unreported">unreported</span>`]],badge:{text:c.verb,tone:c.verb==="failed"?"bad":"ok"},meta:[P(c.note||"",90)," \\xB7 ",v(c.at)," ago"],metaText:`${j(c.note||"")} \\xB7 ${K(c.at)} ago`,open:y("dispatch",c.lane+":"+c.id)})),l=e.reports.map(c=>{let f=Ct(c);return{key:c.key,title:c.title,meta:f.nodes,metaText:f.text,href:T(c.key),acked:!!c.acked}}),u=e.traps.map(({x:c})=>({key:c.trapId,title:"\\u{1FAA4} "+(c.label??`wt:${c.trapId}`),badge:c.requested?{text:"requested",tone:"dim"}:c.starting?{text:c.starting.failedAt?"start failed":"starting",tone:c.starting.failedAt?"bad":"warn"}:{text:c.live?c.harness||"live":"signed off",tone:c.live?"ok":"dim"},meta:[c.repo||""," \\xB7 ",be(c)],metaText:`${c.repo||""} \\xB7 ${Ke(c)}`,action:z(c),extra:J(c),open:y("trap",c.trapId)}));return i`<div class="deckgrid"><${Gn} cards=${e.decisions} drafts=${t} focus=${s} extensions=${n} />${Qe("in flight",r,"dispatches",4,a)}${Qe("Landed \\xB7 24h",o,"dispatches",8,a)}${Qe("reports",l,"reports",8,a)}${Qe("traps",u,"traps",8,a,i`<${Ve} />`)}${Nr(e,a)}</div>`}function Br(e,t){if(!t)return e;let s=new Map(e.map(r=>[r.id,r])),n=r=>{let o=r,l=new Set;for(;o.followUp&&s.has(o.followUp)&&!l.has(o.id);)l.add(o.id),o=s.get(o.followUp);return o.id},a=new Map;for(let r of e){let o=n(r),l=a.get(o)||[];l.push(r),a.set(o,l)}return[...a.values()].sort((r,o)=>o[0].sort-r[0].sort).flatMap(r=>r.sort((o,l)=>o.id===n(o)?-1:l.id===n(l)?1:o.sort-l.sort))}function Ur(e,t){let s=e.lane+":"+e.id;return i`<tr key=${s} class="rowhead" onClick=${y("dispatch",s)}><td>${t&&e.followUp&&i`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${P(e.note??"",90)}${je(e)}${Ue(e)}</td><td>${v(e.verbAt)}</td><td>${ge(e)}</td><td>${ae(e)}</td></tr>`}function jr(e){let t=e.lane+":"+e.id;return i`<div key=${t} class="card" onClick=${y("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b><span class=${"badge v-"+e.verb+U(e.verb)} title=${B(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&i`<div class="note" title=${j(e.note)}>${P(e.note)}</div>`}${je(e)}${Ue(e)}<div class="foot">${ge(e)} ${ae(e)}</div></div>`}function Dn({inp:e,more:t}){let s=e.list,n=t&&i`<${re} kind="dispatches" more=${t} />`;return e.view==="cards"?[s.length?i`<div class="cards">${s.map(jr)}</div>`:i`<div class="empty">no dispatches match</div>`,n]:[A(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Br(s,e.chain).map(a=>Ur(a,e.chain)),"no dispatches match"),n]}var Hr={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices"},qr=["working","needs-decision","blocked","paused","done","failed","unknown"];function Fr(e,t){let s=t.daemonStale,n=e.daemon?[i`<span class=${s?"bad":"ok"}>${s&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",i`<span class="dim">v${e.daemon.version}</span>`]:i`<span class="bad">down</span>`;return[i`<span class="chip">daemon ${n}</span>`,e.slots&&i`<span class="chip">active headless: ${e.slots.headless} of ${e.slots.limit}; traps: ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:a,stale:r})=>i`<span key=${a.grounds} class="chip click" onClick=${y("helm",a.grounds)}>\u26F5 <b>${a.man}</b> <span class="dim">helm ${a.grounds}</span> <span class=${r?"warn":"ok"}>${r&&"stale "}${v(a.heartbeatAt)} ago</span></span>`)]}var Mt=[];function Wr(e){let t=[...new Set([...e.dispatches.map(s=>s.repo),...e.notices.map(s=>s.repo),...e.prs.map(s=>s.repo)].filter(Boolean))].sort();return t.length!==Mt.length&&(Mt=t),Mt}var Lt=[];function Vr(e){let t=[...new Set(e.notices.map(s=>s.kind))].sort();return t.length!==Lt.length&&(Lt=t),Lt}var ye=e=>e.currentTarget.value;function Kr(e,t,s){let n=o=>t===o?"":"display:none",a=e?Wr(e):[],r=e?Vr(e):[];return i`<div class="controls">\n <select id="f-lane" style=${e&&n("dispatches")} value=${s.lane} onChange=${o=>N({lane:ye(o)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${s.repo} onChange=${o=>N({repo:ye(o)})}>\n <option value="">all repos</option>\n ${a.map(o=>i`<option key=${o}>${o}</option>`)}\n </select>\n <select id="f-verb" style=${e&&n("dispatches")} value=${s.verb} onChange=${o=>N({verb:ye(o)})}>\n <option value="">all verbs</option>\n ${qr.map(o=>i`<option key=${o}>${o}</option>`)}\n </select>\n <label id="chain-control" style=${e&&n("dispatches")}><input id="f-chain" type="checkbox" checked=${!!s.chain} onChange=${o=>N({chain:o.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&n("notices")} value=${s.noticeKind} onChange=${o=>N({noticeKind:ye(o)})}>\n <option value="">all notice kinds</option>\n ${r.map(o=>i`<option key=${o}>${o}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${s.q} onInput=${o=>N({q:ye(o)})} />\n </div>`}function Cn({d:e,inp:t,tab:s,st:n,stale:a}){return[i`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||a?a?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>E("settings","browser")}>\u2699</button></span></div>`,i`<div class="chips"><span id="chips" style="display:contents">${e&&t&&Fr(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,i`<nav class="tabs" id="tabs" aria-label="Spyglass views">${pe.map(r=>i`<a href=${"#"+r} data-tab=${r} class=${e?r===s?"on":"":void 0}>${Hr[r]}</a>`)}</nav>`,Kr(e,s,n)]}var Tn=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",i`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function Pn(e,t){if(!t.lobs)return[];let s={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready",landed:"landed",watch:"watch",report:"report"},n=t.hidden??{},a=e.filter(o=>!o.acked&&!(o.key!==void 0&&n[o.key]===o.stateHash)).map(o=>{let l=s[o.kind??""]??"",u=o.key!==void 0?{hideKey:o.key,hideHash:o.stateHash??""}:{};return o.kind==="decision"&&o.key?{key:"decision:"+o.key,text:o.note||o.verb,label:l,hash:ct(o.key),...u}:(o.kind??"question")==="question"&&o.key?{key:"question:"+o.key,text:o.note||o.verb,label:l,hash:ct(o.key),...u}:o.kind==="report"&&o.key?{key:"report:"+o.key,text:o.note||o.verb,label:l,href:T(o.key),...u}:typeof o.kind=="string"&&o.kind.startsWith("pr:")&&o.prUrl?{key:o.kind+":"+(o.key??o.prUrl),text:o.note||o.kind,href:o.prUrl,label:l,...u}:{key:(o.kind??"question")+":"+(o.key??o.lane+":"+o.id),text:o.note||o.verb,label:l,...o.kind==="watch"?{}:{open:{type:"dispatch",key:o.lane+":"+o.id}},...u}});!a.length&&t.preview&&(a=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let r=a.length>4?a.length-4:0;return a=a.slice(0,4),r&&(a[3]={...a[3],text:"\\u2026and "+r+" more \\u2014 see attention",label:""}),a}var Rn=!0;function zr(e,t,s){let n="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",a=()=>{e.hideKey&&Us(e.hideKey,e.hideHash??"")},r=[i`<div class="bub">${e.label&&[i`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,s===!1?i`<span class="fallback">\u{1F99E}</span>`:i`<div class="sprite"></div>`,Rn&&i`<img\n class="star"\n src="/star.png"\n alt=""\n onError=${l=>{Rn=!1,l.currentTarget.remove()}}\n />`],o=e.open;return e.hash?i`<a key=${e.key} class="lob" style=${n} title="open the decision" href=${e.hash} onClick=${a}>${r}</a>`:e.href?i`<a key=${e.key} class="lob" style=${n} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${a}>${r}</a>`:i`<div\n key=${e.key}\n class="lob"\n style=${n}\n title="click to open"\n onClick=${()=>{a(),o&&E(o.type,o.key)}}\n >\n ${r}\n </div>`}function An({state:e}){let t=e.snapshot,s=Pn(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),n=s.map(a=>a.key).join("|")+(e.spriteOk===null?"?":e.spriteOk?"s":"e");return sn(()=>s.map((a,r)=>zr(a,r,e.spriteOk)),[n])}function Ze({box:e}){if(!e)return null;let t=s=>{s.target===s.currentTarget&&ee()};return i`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${ee}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var ie=i`<span class="x" onClick=${te}>\xD7</span>`,In=(e,t,s)=>i`<span class="seg">${t.map(([n,a])=>i`<button class=${n===s?"on":""} onClick=${()=>e(n)}>${a}</button>`)}</span>`;function Jr(e,t){let s=In(Ns,[["table","table"],["cards","cards"]],t.view),n=In(Bs,[["on","on"],["off","off"]],t.lobs?"on":"off"),a=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[ie,i`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,i`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${s}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${n}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${a}</span></div></div>`]}function Xr(e,t){let s=oe(t.numbers,new Map(t.numbers.map((a,r)=>[a,t.titles[r]])),e.number),n=t.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[s," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,n]}var Qr=e=>t=>{t.preventDefault(),E("dispatch",e)};function Zr(e,t){let s=bs(e,t),n=s.pr,a=s.stack,r=s.watch,o=We(n),l=s.chain.length?s.chain.flatMap((c,f)=>[f?" \\u2192 ":"",c.culled?i`<span class="dim">${c.id.slice(0,8)} (culled)</span>`:[i`<a href="#" onClick=${Qr(c.modalKey)}>${c.id.slice(0,8)}</a>`," ",i`<span class="dim">${c.verb}</span>`]]):i`<span class="dim">none</span>`,u=r?[i`<div>${r.key} \xB7 owner ${r.owner||"?"} \xB7 ${r.lastCheckedAt?["checked ",v(r.lastCheckedAt)," ago"]:"never checked"}</div>`,r.lastError&&i`<div class="bad">${r.lastError}</div>`,i`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,C(r.cursor)]:i`<div class="dim">no watch \u2014 <code>lobstah watch add ${n.url}</code> registers one</div>`;return[ie,i`<h3>${qe(n)} ${n.title||""} <span class=${"badge "+L(n.badge)}>${n.badge.text}</span>${n.draft&&n.badge.text!=="draft"&&[" ",i`<span class="badge pr-draft">draft</span>`]}</h3>`,i`<div class="sub">${n.repo} \xB7 ${n.state} \xB7 observed ${v(n.observedAt)} ago${n.gate&&" \\xB7 gate "+n.gate}</div>`,i`<div class="sec">checks</div><div>${ve(n)}</div><div class="sec">review</div><div>${o||i`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${$e(n)}</div>${n.repairWait&&i`<div class="sec">repair waits</div><div>${n.repairWait.heldBy}: ${n.repairWait.reason}${n.repairWait.until?" \\xB7 until "+n.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${n.headRefName||"?"} \u2192 ${n.baseRefName||"?"}</div><div class="sec">stack</div><div>${a?Xr(n,a):i`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${l}</div><div class="sec">watch</div>`,u]}var Mn=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function Yr(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[ie,i`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.harness??"?"} \xB7 ${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,e.sessionId&&[i`<div class="sec">open this session</div>`,C(Mn(e.harness,e.sessionId))],e.transcript&&[i`<div class="sec">transcript</div>`,C(e.transcript)]]}function ea(e){return[i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${It(e)} \xB7 <a href=${T(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function ta(e,t,s){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[i`<div class="sec">worked by trap</div>`,i`<div class="claimedby">claimed by ${He(e.claimedBy.slice(3))}</div>`,C(j(e.claimedBy)),i`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${j(e.claimedBy)} "\u2026"</div>`]:[i`<div class="sec">open this session</div>`,C("lobstah attach "+e.id)];return[ie,i`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,i`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",ge(e)]} ${ae(e)}</div>`,n,e.worktree&&[i`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,C(e.worktree),e.worktreeKept&&i`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[i`<div class="sec">transcript</div>`,C(e.transcript)],t&&ea(t),ln(e,s)]}function sa(e){let t=_e(e),s=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm reserves it and starts its session."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",n=e.notices.length?e.notices.map(o=>i`<div key=${o.seq} class="loglines">${v(o.at)} ago \xB7 <b>${o.kind}</b> \u2014 ${o.text}</div>`):i`<div class="empty">none recorded</div>`,a=e.messages.length?e.messages.map(o=>i`<div key=${o.file} class=${"msg"+(o.from==="helm"?" from-helm":"")}><div class="hdr">from ${o.from} \xB7 ${o.at&&[v(o.at)," ago"]} \xB7 ${o.state==="pending"?i`<span class="warn">pending</span>`:i`<span class="ok">delivered</span>`}</div>${o.text}${o.attachments?.length?ne(o.attachments,l=>ls(e.trapId,l)):""}</div>`):i`<div class="empty">none</div>`,r=e.catches.length?e.catches.map(o=>i`<div key=${o.lane+":"+o.id} class="catch click" onClick=${y("dispatch",o.lane+":"+o.id)}><div class="hdr"><b>${o.id.slice(0,8)}</b><span class=${"badge v-"+o.verb}>${o.verb}</span><span class="dim">${v(o.verbAt)}</span>${ae(o)}</div>${o.note&&i`<div class="loglines">${P(o.note)}</div>`}</div>`):i`<div class="empty">none yet</div>`;return[ie,i`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??"signed off"}</span></h3>`,e.worktree&&i`<div class="sub">${e.worktree}</div>`,i`<div class="sub">${s}</div>`,i`<div class="sec">window</div>`,z(e),J(e),e.live&&e.sessionId&&[i`<div class="sec">open this session</div>`,C(Mn(e.harness,e.sessionId)),i`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],i`<div class="sec">lifecycle (${e.notices.length})</div>`,n,i`<div class="sec">messages (${e.messages.length})</div>`,a,i`<div class="sec">catches (${e.catches.length})</div>`,r]}function Ln({snapshot:e,modal:t,prefs:s,detail:n=null}){if(!e||!t)return null;let a=Y(e,t,n);if(!a&&t.type==="dispatch"&&n?.key===t.key)return[ie,i`<h3>${t.key.slice(t.key.indexOf(":")+1,t.key.indexOf(":")+9)}</h3>`,i`<div class=${n.error?"bad":"dim"}>${n.error??"loading\\u2026"}</div>`];if(!a)return null;if(t.type==="settings")return Jr(a,s);if(t.type==="pr")return Zr(e,t.key);if(t.type==="helm")return Yr(a);if(t.type==="dispatch"){let r=ps(e,a);return ta(a,r,n)}return sa(a)}var na=e=>i`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,En=({inp:e,more:t})=>[A(["at","kind","text","repo"],e.list.map(na),"no notices"),t&&i`<${re} kind="notices" more=${t} />`];function On(e){let t=new Map;for(let s of e.prs){let n=t.get(s.stackId)||[];n.push(s),t.set(s.stackId,n)}return e.stacks.map(s=>({s,prs:t.get(s.id)||[]})).filter(s=>s.prs.length)}var Nn=(e,t)=>oe(e.numbers,new Map(t.map(s=>[s.number,s.title]))),Et=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Bn(e){let t=n=>{let a=String(n??"");return a.length>24?a.slice(0,23)+"\\u2026":a},s=e.watches.map(n=>i`<tr key=${n.key}><td>${n.key}</td><td>${n.owner}</td><td title=${n.cursor}>${t(n.cursor)}</td><td>${n.lastCheckedAt&&v(n.lastCheckedAt)}</td><td>${n.lastError||""}</td></tr>`);return[i`<h2>other watches</h2>`,A(["key","owner","cursor","last check","error"],s,"no other watches")]}function ra(e,t){let s=On(e).flatMap(({s:n,prs:a})=>[i`<tr key=${"stack:"+n.id}><th colspan="8">${Nn(n,a)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}</th></tr>`,...a.map(r=>i`<tr key=${r.key} class="rowhead" onClick=${y("pr",r.key)}><td>${qe(r)}</td><td class="grow">${r.title||""}</td><td>${Et(r)}</td><td>${ve(r)}</td><td>${We(r)}</td><td>${$e(r)}</td><td>${St(r.watch)}</td><td>${r.gate||""}</td></tr>`)]);return[A(["PR","title","state","checks","review","merge","watch","gate"],s,"no PR evidence"),t,Bn(e)]}function aa(e,t){let s=On(e),n=r=>i`<div key=${r.key} class="card" onClick=${y("pr",r.key)}><div class="top">${Fe(r)}<span class=${"badge "+L(r.badge)+U(r.badge.text)} title=${B(r.badge.text)}>${r.badge.text}</span></div><div class="meta" title=${`${r.repo} \\xB7 ${Et(r)}`}>${r.repo} \xB7 ${Et(r)} \xB7 ${$e(r)}</div><div class="foot"><span>${ve(r)}</span><span>${St(r.watch)}</span>${r.gate&&i`<span>gate ${r.gate}</span>`}</div></div>`;return[s.length?s.map(({s:r,prs:o})=>i`<${Z} key=${"stack:"+r.id}><h2>${Nn(r,o)} \xB7 floor ${r.floor}${r.open?"":" \\xB7 history"}</h2><div class="cards">${o.map(n)}</div><//>`):i`<div class="empty">no PR evidence</div>`,t,Bn(e)]}function Un({inp:e,more:t}){let s=t&&i`<${re} kind="prs" more=${t} />`;return e.view==="cards"?aa(e,s):ra(e,s)}var Ot=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),jn=e=>e.live||e.starting?"":" dim";function oa(e){let t=_e(e);return i`<tr key=${e.trapId} class=${"rowhead"+jn(e)} onClick=${y("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness??""}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${be(e)}${J(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${Dt(e)}</td><td>${e.catches.length}</td><td>${z(e)}</td></tr>`}function ia(e){let t=_e(e),s=Dt(e),n=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return i`<div key=${e.trapId} class=${"card"+jn(e)} onClick=${y("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b><span class=${"badge"+U(Ot(e))} title=${B(Ot(e))}>${Ot(e)}</span></div><div class="meta" title=${n}>${n}</div>${e.worktree&&i`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${Ke(e)}>${be(e)}</div>${J(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 ${e.catches.length} catch${e.catches.length===1?"":"es"}${s&&[" \\xB7 ",s]}</span><span class="footact">${z(e)}</span></div></div>`}function Hn({inp:e}){let t=e.list.map(n=>n.x),s=i`<div class="tabhead"><${Ve} /></div>`;return e.view==="cards"?[s,t.length?i`<div class="cards">${t.map(ia)}</div>`:i`<div class="empty">no traps soaking</div>`]:[s,A(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(oa),"no traps soaking")]}function qn({state:e}){let t=me(e),s=e.route,n=t&&_s(t,{st:e.prefs,modal:e.modal,detail:e.detail},Date.now()),a=l=>({left:e.snapshot?vs(e.snapshot,e.older,l):0,loading:e.olderLoading===l,error:e.olderError?.kind===l?e.olderError.text:void 0}),r=l=>!n||l!==s?null:l==="deck"?i`<${xn} inp=${n.deck} drafts=${e.drafts} focus=${e.focusDecision} extensions=${t?.answerLimits?.extensions??[]} />`:l==="dispatches"?i`<${Dn} inp=${n.dispatches} more=${a("dispatches")} />`:l==="traps"?i`<${Hn} inp=${n.traps} />`:l==="prs"?i`<${Un} inp=${n.prs} more=${a("prs")} />`:l==="reports"?i`<${fn} inp=${n.reports} />`:i`<${En} inp=${n.notices} more=${a("notices")} />`,o=l=>{l.target===l.currentTarget&&te()};return[i`<${Cn} d=${t} inp=${n} tab=${s} st=${e.prefs} stale=${e.stale} />`,pe.map(l=>i`<main key=${l} id=${"page-"+l} class=${t&&l===s?"tabpage on":"tabpage"}><div id=${l}>${r(l)}</div></main>`),i`<footer id="foot"><${Tn} d=${t} /></footer>`,i`<div id="lobs"><${An} state=${e} /></div>`,i`<${Ze} box=${e.lightbox} />`,i`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${o}><div class="modal" id="modalbox"><${Ln} snapshot=${t} modal=${e.modal} prefs=${e.prefs} detail=${e.detail} /></div></div>`]}function la({view:e}){if(e.state==="loading")return i`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return i`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return i`<main class="reportview">\n <h1>${t.title}</h1>\n <${wn} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Fn({view:e,box:t}){return[i`<${la} view=${e} />`,i`<${Ze} box=${t} />`]}async function Wn(e){let t=encodeURIComponent(e),s;try{let n=await fetch(`/report/${t}/meta`);if(!n.ok)return{state:"error",error:`report not found (${n.status})`};s=JSON.parse(await n.text())}catch{return{state:"error",error:"report could not be read"}}try{let n=await fetch(ms(e));return n.ok?{state:"ready",report:s,text:await n.text()}:{state:"ready",report:s,error:`report not found (${n.status})`}}catch{return{state:"ready",report:s,error:"report could not be read"}}}var Nt=!1,we=null,Ge=null;function ca(e){let t=e.headers?.get("x-lobstah-beats");if(t)try{return JSON.parse(decodeURIComponent(t))}catch{return}}async function Bt(){if(!Nt){Nt=!0;try{let e=await fetch("/data",{cache:"no-store",headers:Ge?{"if-none-match":Ge}:{}});if(e.status===304){let t=ca(e);t?Ds(t):Ee(await da())}else{if(!e.ok)throw new Error(`/data answered ${e.status}`);Ge=e.headers?.get("etag")??null,Ee(await e.json())}$t()}catch{Ms()}finally{Nt=!1}}}async function da(){Ge=null;let e=await fetch("/data",{cache:"no-store"});if(!e.ok)throw new Error(`/data answered ${e.status}`);return Ge=e.headers?.get("etag")??null,await e.json()}var Vn=()=>{we||(we=setInterval(Bt,2e3))},pa=()=>{we&&clearInterval(we),we=null};function Kn(){document.addEventListener("visibilitychange",()=>{document.hidden?pa():(Bt(),Vn())}),Bt(),document.hidden||Vn()}var Ut=()=>is(location.hash),zn=e=>window.addEventListener("hashchange",()=>e(Ut()));function Xn(){let e=us(location.hash);return e===null?!1:(location.replace(T(e)),!0)}var Qn=()=>({snapshot:void 0,route:Ut(),prefs:ys(),modal:null,detail:null,older:ht,olderLoading:null,olderError:null,stale:!1,lobHidden:Gs(),spriteOk:null,focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:dt(location.hash),lightbox:null}),Jn=fs(location.pathname);Jn!==null?ua(Jn):Xn()||fa();function ua(e){gt(Qn());let t={state:"loading"},s=()=>it(Q(Fn,{view:t,box:$().lightbox}),document.body);document.title="report \\xB7 lobstah glass",vt(s),s(),Wn(e).then(n=>{t=n,n.state==="ready"&&(document.title=`${n.report.title} \\xB7 lobstah glass`),s()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&$().lightbox&&ee()})}function fa(){gt(Qn());let e,t=!1,s=()=>{let o=$(),l=o.route!==e||!t&&!!o.snapshot;it(Q(qn,{state:o}),document.body),e=o.route,t||(t=!!o.snapshot),l&&window.scrollTo(0,0),o.focusDecision||(n=null),a(o.focusDecision)},n=null;function a(o){if(!o||o===n)return;let l=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===o);l&&(n=o,l.scrollIntoView?.({block:"center"}))}vt(s),s();let r=new Image;r.onload=()=>k({spriteOk:!0}),r.onerror=()=>k({spriteOk:!1}),r.src="/lob-sprite.png",zn(o=>{if(Xn())return;let l=dt(location.hash);l!==$().focusDecision&&(n=null),k({route:o,focusDecision:l})}),document.addEventListener("keydown",o=>{o.key==="Escape"&&($().lightbox?ee():te())}),Kn()}})();</script>\n </body>\n</html>\n';
14086
14134
 
14087
14135
  // apps/cli/src/glass-snapshot-thread.ts
14088
14136
  init_dist2();
@@ -14118,24 +14166,33 @@ function startSnapshotThread() {
14118
14166
  const w = waiting.get(reply.id);
14119
14167
  if (!w) return;
14120
14168
  waiting.delete(reply.id);
14121
- if ("body" in reply) w.resolve(reply.body);
14122
- else w.reject(new Error(reply.error));
14169
+ if ("error" in reply) w.reject(new Error(reply.error));
14170
+ else w.resolve(reply);
14123
14171
  });
14124
14172
  worker.on("error", fail);
14125
14173
  worker.on("exit", (code) => fail(new Error(`snapshot thread exited (${code})`)));
14174
+ const ask = (a) => {
14175
+ if (dead) return Promise.reject(dead);
14176
+ const id = ++next;
14177
+ return new Promise((resolve14, reject) => {
14178
+ waiting.set(id, { resolve: resolve14, reject });
14179
+ worker.postMessage({ ...a, id });
14180
+ });
14181
+ };
14182
+ const json = (reply) => "json" in reply ? reply.json : null;
14126
14183
  return {
14127
- build(local) {
14128
- if (dead) return Promise.reject(dead);
14184
+ poll(local) {
14129
14185
  const pending = inFlight.get(local);
14130
14186
  if (pending) return pending;
14131
- const id = ++next;
14132
- const promise = new Promise((resolve14, reject) => {
14133
- waiting.set(id, { resolve: resolve14, reject });
14134
- worker.postMessage({ id, local });
14187
+ const promise = ask({ want: "poll", local }).then((reply) => {
14188
+ if (!("poll" in reply)) throw new Error("snapshot thread answered a poll without a body");
14189
+ return reply.poll;
14135
14190
  }).finally(() => inFlight.delete(local));
14136
14191
  inFlight.set(local, promise);
14137
14192
  return promise;
14138
14193
  },
14194
+ older: (kind, offset, limit) => ask({ want: "older", kind, offset, limit }).then((reply) => json(reply) ?? "{}"),
14195
+ dispatch: (id) => ask({ want: "dispatch", dispatch: id }).then(json),
14139
14196
  async close() {
14140
14197
  fail(new Error("snapshot thread closed"));
14141
14198
  await worker.terminate();
@@ -14143,6 +14200,106 @@ function startSnapshotThread() {
14143
14200
  };
14144
14201
  }
14145
14202
 
14203
+ // apps/cli/src/glass-poll.ts
14204
+ import { createHash as createHash9 } from "node:crypto";
14205
+
14206
+ // apps/cli/src/glass-diff.ts
14207
+ var LANDED_WINDOW_MS = 864e5;
14208
+
14209
+ // apps/cli/src/glass-poll.ts
14210
+ var POLL_WINDOW_MS = LANDED_WINDOW_MS;
14211
+ var POLL_MIN = { dispatches: 20, notices: 50, prs: 20 };
14212
+ var OLDER_PAGE = 50;
14213
+ var OLDER_PAGE_MAX = 200;
14214
+ var NOTE_MAX = 280;
14215
+ var TITLE_MAX = 120;
14216
+ function briefTitle2(brief) {
14217
+ return (brief.split(/\r?\n/, 1)[0] ?? "").trim().slice(0, TITLE_MAX);
14218
+ }
14219
+ var cutNote = (note) => note.length > NOTE_MAX ? `${note.slice(0, NOTE_MAX)}\u2026` : note;
14220
+ function dispatchSummary(x) {
14221
+ const { brief, attachments, messageAttachments: messageAttachments2, log, inbox, evidence, note, noteCut, ...rest } = x;
14222
+ const cut = note !== void 0 && note.length > NOTE_MAX;
14223
+ const ev = evidence && {
14224
+ ...evidence.deliveredTo !== void 0 ? { deliveredTo: evidence.deliveredTo } : {},
14225
+ ...evidence.prUrl !== void 0 ? { prUrl: evidence.prUrl } : {},
14226
+ ...evidence.prUrls !== void 0 ? { prUrls: evidence.prUrls } : {},
14227
+ ...evidence.pr?.url !== void 0 ? { pr: { url: evidence.pr.url } } : {}
14228
+ };
14229
+ return {
14230
+ ...rest,
14231
+ title: rest.title ?? briefTitle2(brief ?? ""),
14232
+ ...note !== void 0 ? { note: cutNote(note) } : {},
14233
+ ...cut || noteCut ? { noteCut: true } : {},
14234
+ ...ev && Object.keys(ev).length > 0 ? { evidence: ev } : {}
14235
+ };
14236
+ }
14237
+ var time = (iso) => iso ? Date.parse(iso) || 0 : 0;
14238
+ var prAt = (p) => time(p.mergedAt ?? p.closedAt ?? p.updatedAt ?? p.observedAt);
14239
+ function split(items, always, at, min, now2) {
14240
+ const kept = [];
14241
+ const older = [];
14242
+ let finished2 = 0;
14243
+ for (const x of items) {
14244
+ if (always(x)) kept.push(x);
14245
+ else if (finished2++ < min || at(x) >= now2 - POLL_WINDOW_MS) kept.push(x);
14246
+ else older.push(x);
14247
+ }
14248
+ return { kept, older };
14249
+ }
14250
+ function histories(full, now2) {
14251
+ return {
14252
+ dispatches: split(full.dispatches, (x) => x.bucket !== "done", (x) => x.sort, POLL_MIN.dispatches, now2),
14253
+ notices: split(full.notices, () => false, (n) => time(n.at), POLL_MIN.notices, now2),
14254
+ prs: split(full.prs, (p) => p.state === "OPEN", prAt, POLL_MIN.prs, now2)
14255
+ };
14256
+ }
14257
+ function pollSnapshot(full, now2) {
14258
+ const h = histories(full, now2);
14259
+ const kept = new Set(h.prs.kept.map((p) => p.stackId));
14260
+ return {
14261
+ ...full,
14262
+ dispatches: h.dispatches.kept.map(dispatchSummary),
14263
+ notices: h.notices.kept,
14264
+ prs: h.prs.kept,
14265
+ stacks: full.stacks.filter((s) => s.open || kept.has(s.id)),
14266
+ // The deck shows the last day's landed catches, each by its note's start;
14267
+ // nothing shows older ones.
14268
+ landed: full.landed.filter((l) => time(l.at) >= now2 - LANDED_WINDOW_MS).map((l) => l.note !== void 0 ? { ...l, note: cutNote(l.note) } : l),
14269
+ older: { dispatches: h.dispatches.older.length, notices: h.notices.older.length, prs: h.prs.older.length }
14270
+ };
14271
+ }
14272
+ function olderPage(full, kind, offset, limit, now2) {
14273
+ const older = histories(full, now2)[kind].older;
14274
+ const start = Math.max(0, Math.floor(offset) || 0);
14275
+ const size = Math.min(OLDER_PAGE_MAX, Math.max(1, Math.floor(limit) || OLDER_PAGE));
14276
+ const page = older.slice(start, start + size);
14277
+ if (kind === "dispatches") return { kind, offset: start, total: older.length, items: page.map(dispatchSummary) };
14278
+ if (kind === "notices") return { kind, offset: start, total: older.length, items: page };
14279
+ const ids = new Set(page.map((p) => p.stackId));
14280
+ return { kind, offset: start, total: older.length, items: page, stacks: full.stacks.filter((s) => ids.has(s.id)) };
14281
+ }
14282
+ function beatsOf(d) {
14283
+ const traps = {};
14284
+ for (const t of d.traps) {
14285
+ if (t.heartbeatAt !== void 0 || t.parkedAt !== void 0) {
14286
+ traps[t.trapId] = { ...t.heartbeatAt !== void 0 ? { heartbeatAt: t.heartbeatAt } : {}, ...t.parkedAt !== void 0 ? { parkedAt: t.parkedAt } : {} };
14287
+ }
14288
+ }
14289
+ return {
14290
+ now: d.now,
14291
+ ...d.daemon?.heartbeat !== void 0 ? { daemon: d.daemon.heartbeat } : {},
14292
+ helms: Object.fromEntries(d.helms.map((h) => [h.grounds, h.heartbeatAt])),
14293
+ traps
14294
+ };
14295
+ }
14296
+ var TICKING = /* @__PURE__ */ new Set(["now", "heartbeat", "heartbeatAt", "parkedAt", "ageSecs", "waitedSecs"]);
14297
+ function pollBody(full, now2) {
14298
+ const snap = pollSnapshot(full, now2);
14299
+ const stable = JSON.stringify(snap, (key, value) => TICKING.has(key) ? void 0 : value);
14300
+ return { body: JSON.stringify(snap), hash: createHash9("sha256").update(stable).digest("base64url").slice(0, 22), beats: beatsOf(snap) };
14301
+ }
14302
+
14146
14303
  // apps/cli/src/focus.ts
14147
14304
  init_dist2();
14148
14305
  import * as fs63 from "node:fs";
@@ -14353,7 +14510,7 @@ function wedgeSecs() {
14353
14510
  return DEFAULT_LIMITS.wedgeThresholdSecs;
14354
14511
  }
14355
14512
  }
14356
- function dispatchRows() {
14513
+ function dispatchesOnDisk() {
14357
14514
  const rows = [];
14358
14515
  for (const lane of ["work", "chore"]) {
14359
14516
  const dirs = laneDirs(lane);
@@ -14371,49 +14528,71 @@ function dispatchRows() {
14371
14528
  if (d) rows.push({ lane, bucket: "done", d, sort: at });
14372
14529
  }
14373
14530
  }
14531
+ return rows;
14532
+ }
14533
+ function dispatchOnDisk(id) {
14534
+ for (const lane of ["work", "chore"]) {
14535
+ const dirs = laneDirs(lane);
14536
+ const queued = path54.join(dirs.queue, `${id}.json`);
14537
+ const q = readJson4(queued);
14538
+ if (q) return { lane, bucket: "queued", d: q, sort: mtime(queued) };
14539
+ const a = readJson4(path54.join(dirs.active, id, "descriptor.json"));
14540
+ if (a) return { lane, bucket: "active", d: a, sort: mtime(path54.join(dirs.active, id)) };
14541
+ const d = readJson4(path54.join(dirs.done, id, "descriptor.json"));
14542
+ if (d) return { lane, bucket: "done", d, sort: mtime(path54.join(dirs.done, id)) };
14543
+ }
14544
+ return void 0;
14545
+ }
14546
+ function dispatchRow(r, hold, staleSecs) {
14547
+ const id = r.d.id;
14548
+ const log = readStatusLog(id, r.lane);
14549
+ const last = log.at(-1);
14550
+ const claim = r.bucket === "active" ? readSessionClaim(id, r.lane) : void 0;
14551
+ const evidence = readEvidence(id, r.lane);
14552
+ const inboxDir2 = path54.join(laneDirs(r.lane).inbox, id);
14553
+ return {
14554
+ id,
14555
+ lane: r.lane,
14556
+ bucket: r.bucket,
14557
+ repo: r.d.repo,
14558
+ for: r.d.for,
14559
+ followUp: r.d.followUp,
14560
+ title: briefTitle2(r.d.brief),
14561
+ brief: r.d.brief,
14562
+ attachments: r.d.attachments ?? [],
14563
+ messageAttachments: messageAttachments(inboxDir2),
14564
+ // A queued descriptor with no log is waiting, not unknown; its time
14565
+ // is the queue time.
14566
+ // An active dispatch a trap claimed, with no log, is working since
14567
+ // the claim.
14568
+ verb: r.bucket === "queued" && log.length === 0 ? "queued" : last?.verb ?? (claim ? "working" : "unknown"),
14569
+ ...last?.verb === "failed" && last.note?.startsWith("budget:") ? { outOfTimeWorkSaved: true } : {},
14570
+ // Held for free space: the note carries the reason.
14571
+ note: (r.bucket === "queued" && r.d.systemRepair?.trapWaitUntil && r.d.for ? `repair chore waits for ${r.d.for} until ${r.d.systemRepair.trapWaitUntil}` : void 0) ?? (r.bucket === "queued" && hold && r.d.for === void 0 ? holdReason(hold) : void 0) ?? last?.note,
14572
+ verbAt: last?.at ?? (r.bucket === "queued" ? queuedAt(id, r.lane) : claim?.at),
14573
+ activity: r.bucket === "active" ? activityView(readActivity(id, r.lane), staleSecs) : void 0,
14574
+ waiting: r.bucket === "active" ? waitingView(last) : void 0,
14575
+ claimedBy: claim?.by,
14576
+ log,
14577
+ inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) => fs64.readFileSync(path54.join(inboxDir2, f), "utf8").trim()),
14578
+ ...awaitingOf(id),
14579
+ evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
14580
+ ...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
14581
+ ...r.bucket === "queued" ? {} : livenessView(id, r.lane),
14582
+ // A trap's catch: the trap's own checkout. A headless dispatch: the
14583
+ // worktree it ran in, the origin's for a follow-up that reused it.
14584
+ transcript: claim ? transcriptPath(claim.harness, claim.worktree, claim.sessionId) : transcriptPath(evidence.harness, evidence.sessionId ? dispatchWorktree(id, r.lane).path : void 0, evidence.sessionId),
14585
+ sort: r.sort
14586
+ };
14587
+ }
14588
+ function dispatchRows() {
14374
14589
  const hold = readHold();
14375
14590
  const staleSecs = wedgeSecs();
14376
- return rows.map((r) => {
14377
- const id = r.d.id;
14378
- const log = readStatusLog(id, r.lane);
14379
- const last = log.at(-1);
14380
- const claim = r.bucket === "active" ? readSessionClaim(id, r.lane) : void 0;
14381
- const evidence = readEvidence(id, r.lane);
14382
- const inboxDir2 = path54.join(laneDirs(r.lane).inbox, id);
14383
- return {
14384
- id,
14385
- lane: r.lane,
14386
- bucket: r.bucket,
14387
- repo: r.d.repo,
14388
- for: r.d.for,
14389
- followUp: r.d.followUp,
14390
- brief: r.d.brief,
14391
- attachments: r.d.attachments ?? [],
14392
- messageAttachments: messageAttachments(inboxDir2),
14393
- // A queued descriptor with no log is waiting, not unknown; its time
14394
- // is the queue time.
14395
- // An active dispatch a trap claimed, with no log, is working since
14396
- // the claim.
14397
- verb: r.bucket === "queued" && log.length === 0 ? "queued" : last?.verb ?? (claim ? "working" : "unknown"),
14398
- ...last?.verb === "failed" && last.note?.startsWith("budget:") ? { outOfTimeWorkSaved: true } : {},
14399
- // Held for free space: the note carries the reason.
14400
- note: (r.bucket === "queued" && r.d.systemRepair?.trapWaitUntil && r.d.for ? `repair chore waits for ${r.d.for} until ${r.d.systemRepair.trapWaitUntil}` : void 0) ?? (r.bucket === "queued" && hold && r.d.for === void 0 ? holdReason(hold) : void 0) ?? last?.note,
14401
- verbAt: last?.at ?? (r.bucket === "queued" ? queuedAt(id, r.lane) : claim?.at),
14402
- activity: r.bucket === "active" ? activityView(readActivity(id, r.lane), staleSecs) : void 0,
14403
- waiting: r.bucket === "active" ? waitingView(last) : void 0,
14404
- claimedBy: claim?.by,
14405
- log,
14406
- inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) => fs64.readFileSync(path54.join(inboxDir2, f), "utf8").trim()),
14407
- ...awaitingOf(id),
14408
- evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
14409
- ...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
14410
- ...r.bucket === "queued" ? {} : livenessView(id, r.lane),
14411
- // A trap's catch: the trap's own checkout. A headless dispatch: the
14412
- // worktree it ran in, the origin's for a follow-up that reused it.
14413
- transcript: claim ? transcriptPath(claim.harness, claim.worktree, claim.sessionId) : transcriptPath(evidence.harness, evidence.sessionId ? dispatchWorktree(id, r.lane).path : void 0, evidence.sessionId),
14414
- sort: r.sort
14415
- };
14416
- }).sort((a, b) => b.sort - a.sort);
14591
+ return dispatchesOnDisk().map((r) => dispatchRow(r, hold, staleSecs)).sort((a, b) => b.sort - a.sort);
14592
+ }
14593
+ function dispatchDetail(id) {
14594
+ const r = dispatchOnDisk(id);
14595
+ return r && dispatchRow(r, readHold(), wedgeSecs());
14417
14596
  }
14418
14597
  function awaitingOf(id) {
14419
14598
  const e = awaitingReply(id);
@@ -14668,6 +14847,18 @@ function buildGlassSnapshot(options = {}) {
14668
14847
  }
14669
14848
  };
14670
14849
  }
14850
+ function glassPoll(local, snapshot = buildGlassSnapshot) {
14851
+ return pollBody(snapshot({ local }), Date.now());
14852
+ }
14853
+ function glassOlderJson(kind, offset, limit, snapshot = buildGlassSnapshot) {
14854
+ return JSON.stringify(olderPage(snapshot({ local: false }), kind, offset, limit, Date.now()));
14855
+ }
14856
+ function glassDispatchJson(id, snapshot) {
14857
+ const x = snapshot ? snapshot({ local: false }).dispatches.find((d) => d.id === id) : dispatchDetail(id);
14858
+ return x ? JSON.stringify(x) : null;
14859
+ }
14860
+ var DISPATCH_DETAIL_RE = /^\/data\/dispatch\/([A-Za-z0-9-]{1,64})$/;
14861
+ var OLDER_KINDS = ["dispatches", "notices", "prs"];
14671
14862
  var BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
14672
14863
  function decisionAnswerRequest(raw) {
14673
14864
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new DecisionError("payload must be an object");
@@ -14699,6 +14890,7 @@ function serveGlass(port, options = {}) {
14699
14890
  const thread = options.snapshot ? void 0 : startSnapshotThread();
14700
14891
  const logged = /* @__PURE__ */ new Set();
14701
14892
  const focusToken = randomBytes5(32).toString("hex");
14893
+ const instance = randomBytes5(6).toString("base64url");
14702
14894
  const icon = assetPath("favicon.png") ?? assetPath("lob-star.png");
14703
14895
  const lob = assetPath("lob.png");
14704
14896
  const sprite = assetPath("lob-sprite.png");
@@ -14849,19 +15041,64 @@ function serveGlass(port, options = {}) {
14849
15041
  } else if (req.url === "/data") {
14850
15042
  const local = req.headers.host === ownHost();
14851
15043
  const page = `"focusToken":${JSON.stringify(focusToken)},"focusSupported":${process.platform === "darwin"}`;
14852
- const send = (snap) => {
14853
- res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
14854
- res.end(snap === "{}" ? `{${page}}` : `${snap.slice(0, -1)},${page}}`);
15044
+ const send = (p) => {
15045
+ const etag = `W/"${p.hash}.${instance}.${local ? "l" : "r"}"`;
15046
+ const headers = {
15047
+ "content-type": "application/json",
15048
+ "cache-control": "no-store",
15049
+ etag,
15050
+ "x-lobstah-beats": encodeURIComponent(JSON.stringify(p.beats))
15051
+ };
15052
+ if (req.headers["if-none-match"] === etag) {
15053
+ res.writeHead(304, headers);
15054
+ res.end();
15055
+ return;
15056
+ }
15057
+ res.writeHead(200, headers);
15058
+ res.end(p.body === "{}" ? `{${page}}` : `${p.body.slice(0, -1)},${page}}`);
14855
15059
  };
14856
- const onThisThread = () => send(JSON.stringify(snapshot({ local })));
15060
+ const onThisThread = () => send(glassPoll(local, snapshot));
14857
15061
  if (!thread) return onThisThread();
14858
- void thread.build(local).then(send, () => {
15062
+ void thread.poll(local).then(send, () => {
14859
15063
  try {
14860
15064
  onThisThread();
14861
15065
  } catch (e) {
14862
15066
  fail(req, res, e);
14863
15067
  }
14864
15068
  });
15069
+ } else if (req.url?.startsWith("/data/")) {
15070
+ const reply = (json) => {
15071
+ res.writeHead(json === null ? 404 : 200, { "content-type": "application/json", "cache-control": "no-store" });
15072
+ res.end(json ?? JSON.stringify({ error: "not found" }));
15073
+ };
15074
+ if (req.method !== "GET" || !(req.url.startsWith("/data/older?") || DISPATCH_DETAIL_RE.test(req.url))) return reply(null);
15075
+ const detail = DISPATCH_DETAIL_RE.exec(req.url)?.[1];
15076
+ let build;
15077
+ let onThisThread;
15078
+ if (detail !== void 0) {
15079
+ build = () => thread.dispatch(detail);
15080
+ onThisThread = () => glassDispatchJson(detail, options.snapshot ? snapshot : void 0);
15081
+ } else {
15082
+ const q = new URL(req.url, "http://glass").searchParams;
15083
+ const kind = q.get("kind");
15084
+ if (!OLDER_KINDS.includes(kind)) {
15085
+ res.writeHead(400, { "content-type": "application/json", "cache-control": "no-store" });
15086
+ res.end(JSON.stringify({ error: `kind must be one of ${OLDER_KINDS.join(", ")}` }));
15087
+ return;
15088
+ }
15089
+ const offset = Number(q.get("offset") ?? 0);
15090
+ const limit = Number(q.get("limit") ?? 0);
15091
+ build = () => thread.older(kind, offset, limit);
15092
+ onThisThread = () => glassOlderJson(kind, offset, limit, snapshot);
15093
+ }
15094
+ if (!thread) return reply(onThisThread());
15095
+ void build().then(reply, () => {
15096
+ try {
15097
+ reply(onThisThread());
15098
+ } catch (e) {
15099
+ fail(req, res, e);
15100
+ }
15101
+ });
14865
15102
  } else {
14866
15103
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
14867
15104
  res.end(PAGE);
@@ -15808,7 +16045,7 @@ var COMMANDS = {
15808
16045
  positionals: "[<key>]"
15809
16046
  },
15810
16047
  soak: {
15811
- subverbs: ["beat"],
16048
+ subverbs: ["beat", "title-set"],
15812
16049
  flags: {
15813
16050
  "--session": { value: "<id>" },
15814
16051
  "--one": {},
@@ -15962,7 +16199,7 @@ on as a reserved trap. Names the Terminal.app/iTerm2 tab. \`soak beat\`: hook.`,
15962
16199
  start command. dispatch --for works at once; soak --ticket <t> redeems it. Past
15963
16200
  --deadline (default 180s) trap-start-failed posts; work stays queued. stow --wt
15964
16201
  <name> withdraws it. --request <id> reserves what a glass request asks for and
15965
- closes it; \`trap requests\` lists them. \`trap title-set\` confirms the sign-on title.`,
16202
+ closes it; \`trap requests\` lists them. \`trap title-set\` = \`soak title-set\`.`,
15966
16203
  stow: `Sign off (worktree or --wt/--session): unfinished catches requeue; done/failed
15967
16204
  finalizes; unread messages bounce. Removes only worktrees soak created, kept
15968
16205
  when dirty, untracked, unpushed to upstream, or without upstream (with reason).
@@ -16319,7 +16556,7 @@ soaking (interactive sessions volunteering as workers):
16319
16556
  trap reserve --request <id> reserve what a glass trap request asks for
16320
16557
  (repo, harness), and close the request.
16321
16558
  trap requests open trap requests from the glass.
16322
- trap title-set [--session <id>] confirm this session applied the title
16559
+ soak title-set [--session <id>] confirm this session applied the title
16323
16560
  sign-on printed. Sign-on is complete
16324
16561
  after it; until then SessionStart and
16325
16562
  Stop remind the session once per turn.
@@ -16362,6 +16599,13 @@ function armGraceNote(graceSecs) {
16362
16599
  function hookParkMode(configured, harness) {
16363
16600
  return configured ?? (harness === "claude" || !harness && !!process.env.CLAUDE_CODE_SESSION_ID ? "arm" : "block");
16364
16601
  }
16602
+ function confirmTitle(sessionFlag) {
16603
+ const who = callerSession(sessionFlag);
16604
+ const trapId = trapIdAbove(process.cwd()) ?? (who ? trapBySession(who.id)?.trapId : void 0);
16605
+ const reg = trapId ? confirmTrapTitle(trapId) : void 0;
16606
+ if (!reg) throw new Error("no trap here \u2014 run from the trap's worktree, or pass --session <id>");
16607
+ console.log(toonKV({ trap: trapLabel(reg), title: reg.titleSet, signOn: "complete" }));
16608
+ }
16365
16609
  async function soakPark(trapId, timeout, plain = false) {
16366
16610
  const timeoutSecs = Number(timeout ?? "14000");
16367
16611
  const deadline = Date.now() + timeoutSecs * 1e3;
@@ -16684,11 +16928,7 @@ ${usageFor(cmd)}`);
16684
16928
  switch (cmd) {
16685
16929
  case "trap": {
16686
16930
  if (pos[0] === "title-set") {
16687
- const who = callerSession(opt("--session"));
16688
- const trapId = trapIdAbove(process.cwd()) ?? (who ? trapBySession(who.id)?.trapId : void 0);
16689
- const reg = trapId ? confirmTrapTitle(trapId) : void 0;
16690
- if (!reg) throw new Error("no trap here \u2014 run from the trap's worktree, or pass --session <id>");
16691
- console.log(toonKV({ trap: trapLabel(reg), title: reg.titleSet, signOn: "complete" }));
16931
+ confirmTitle(opt("--session"));
16692
16932
  break;
16693
16933
  }
16694
16934
  if (pos[0] === "requests") {
@@ -17718,9 +17958,12 @@ ${usageFor("man:wait")}`);
17718
17958
  try {
17719
17959
  const hook = readHookStdin();
17720
17960
  const trapReg = hook?.session_id ? trapBySession(hook.session_id) : void 0;
17721
- if (trapReg?.titlePending && !hook?.stop_hook_active) {
17722
- console.log(JSON.stringify({ decision: "block", reason: titleReminder(trapReg.titlePending) }));
17723
- break;
17961
+ if (trapReg?.titlePending && !hook?.stop_hook_active && (trapReg.titleReminders ?? 0) < TITLE_REMINDERS) {
17962
+ const n = countTitleReminder(trapReg.trapId);
17963
+ if (n !== void 0) {
17964
+ console.log(JSON.stringify({ decision: "block", reason: titleReminder(trapReg.titlePending, n >= TITLE_REMINDERS) }));
17965
+ break;
17966
+ }
17724
17967
  }
17725
17968
  if (trapReg) {
17726
17969
  const cfg = loadConfig();
@@ -17896,6 +18139,10 @@ ${behind}` : "");
17896
18139
  break;
17897
18140
  }
17898
18141
  case "soak": {
18142
+ if (pos[0] === "title-set") {
18143
+ confirmTitle(opt("--session"));
18144
+ break;
18145
+ }
17899
18146
  if (opt("--link") !== void 0 && !validSessionLink(opt("--link"))) {
17900
18147
  throw new UsageError("invalid --link: use a supported claude://, vscode://, or codex:// session URL");
17901
18148
  }
@@ -18076,7 +18323,9 @@ ${usageFor("soak")}`
18076
18323
  ...titled?.named ? { terminal: `tab named ${reg.name}` } : {},
18077
18324
  ...reg.one ? { one: true } : {},
18078
18325
  ...inside ? {} : { instruction: `cd ${reg.worktree} and work in that directory from now on` },
18079
- ...reg.titlePending ? { step: `Apply this title: ${reg.titlePending}. Then run \`lobstah trap title-set${sessionFlag}\`. Sign-on is complete after that.` } : {},
18326
+ ...reg.titlePending ? {
18327
+ step: `Apply this title: ${reg.titlePending} (skip this if you have no tool that sets the session title). Then run \`lobstah soak title-set${sessionFlag}\`. Sign-on is complete after that.`
18328
+ } : {},
18080
18329
  note: "this session now takes assigned work: run `lobstah soak --wait --timeout 900` as a background task when the Stop hook asks for an arm, or in the foreground to listen. Never `man wait` (that is the orchestrator's command, not yours)."
18081
18330
  })
18082
18331
  );