@loompad/cli 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +20 -5
  3. package/dist/adapters/antigravity-cli.d.ts +2 -0
  4. package/dist/adapters/antigravity-cli.js +20 -1
  5. package/dist/adapters/antigravity-cli.js.map +1 -1
  6. package/dist/adapters/model.js +1 -1
  7. package/dist/adapters/model.js.map +1 -1
  8. package/dist/cli/doctor.d.ts +30 -1
  9. package/dist/cli/doctor.js +117 -1
  10. package/dist/cli/doctor.js.map +1 -1
  11. package/dist/cli/index.js +294 -14
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/cli/ui.js +11 -7
  14. package/dist/cli/ui.js.map +1 -1
  15. package/dist/core/auto-policy.d.ts +46 -0
  16. package/dist/core/auto-policy.js +181 -0
  17. package/dist/core/auto-policy.js.map +1 -0
  18. package/dist/core/council.d.ts +55 -0
  19. package/dist/core/council.js +71 -0
  20. package/dist/core/council.js.map +1 -0
  21. package/dist/core/permissions.js +2 -2
  22. package/dist/core/permissions.js.map +1 -1
  23. package/dist/core/prices.d.ts +55 -0
  24. package/dist/core/prices.js +99 -0
  25. package/dist/core/prices.js.map +1 -0
  26. package/dist/core/proof.d.ts +2 -0
  27. package/dist/core/proof.js +33 -10
  28. package/dist/core/proof.js.map +1 -1
  29. package/dist/core/providers.d.ts +13 -0
  30. package/dist/core/providers.js +17 -0
  31. package/dist/core/providers.js.map +1 -1
  32. package/dist/core/replay.js +14 -0
  33. package/dist/core/replay.js.map +1 -1
  34. package/dist/core/review.d.ts +60 -0
  35. package/dist/core/review.js +121 -0
  36. package/dist/core/review.js.map +1 -0
  37. package/dist/core/setup.d.ts +13 -0
  38. package/dist/core/setup.js +17 -1
  39. package/dist/core/setup.js.map +1 -1
  40. package/dist/core/turn-wait.d.ts +20 -0
  41. package/dist/core/turn-wait.js +39 -0
  42. package/dist/core/turn-wait.js.map +1 -0
  43. package/dist/core/usage.d.ts +7 -1
  44. package/dist/core/usage.js +11 -3
  45. package/dist/core/usage.js.map +1 -1
  46. package/dist/daemon/client.d.ts +46 -0
  47. package/dist/daemon/client.js +34 -0
  48. package/dist/daemon/client.js.map +1 -1
  49. package/dist/daemon/council.d.ts +98 -0
  50. package/dist/daemon/council.js +248 -0
  51. package/dist/daemon/council.js.map +1 -0
  52. package/dist/daemon/proof.d.ts +6 -0
  53. package/dist/daemon/proof.js +22 -3
  54. package/dist/daemon/proof.js.map +1 -1
  55. package/dist/daemon/push.js +7 -0
  56. package/dist/daemon/push.js.map +1 -1
  57. package/dist/daemon/review.d.ts +89 -0
  58. package/dist/daemon/review.js +136 -0
  59. package/dist/daemon/review.js.map +1 -0
  60. package/dist/daemon/routes/council.d.ts +4 -0
  61. package/dist/daemon/routes/council.js +29 -0
  62. package/dist/daemon/routes/council.js.map +1 -0
  63. package/dist/daemon/routes/review.d.ts +4 -0
  64. package/dist/daemon/routes/review.js +40 -0
  65. package/dist/daemon/routes/review.js.map +1 -0
  66. package/dist/daemon/routes/routing.js +2 -1
  67. package/dist/daemon/routes/routing.js.map +1 -1
  68. package/dist/daemon/routes/setup.d.ts +2 -1
  69. package/dist/daemon/routes/setup.js +13 -5
  70. package/dist/daemon/routes/setup.js.map +1 -1
  71. package/dist/daemon/runtime/accounting.d.ts +6 -0
  72. package/dist/daemon/runtime/accounting.js +37 -1
  73. package/dist/daemon/runtime/accounting.js.map +1 -1
  74. package/dist/daemon/runtime/briefings.d.ts +8 -3
  75. package/dist/daemon/runtime/briefings.js +26 -14
  76. package/dist/daemon/runtime/briefings.js.map +1 -1
  77. package/dist/daemon/runtime.d.ts +40 -1
  78. package/dist/daemon/runtime.js +171 -2
  79. package/dist/daemon/runtime.js.map +1 -1
  80. package/dist/daemon/server.js +5 -1
  81. package/dist/daemon/server.js.map +1 -1
  82. package/dist/providers/agent.js +3 -0
  83. package/dist/providers/agent.js.map +1 -1
  84. package/dist/providers/claude/adapter.d.ts +3 -0
  85. package/dist/providers/claude/adapter.js +20 -1
  86. package/dist/providers/claude/adapter.js.map +1 -1
  87. package/dist/types.d.ts +19 -1
  88. package/dist/types.js.map +1 -1
  89. package/dist/web/app.css +48 -1
  90. package/dist/web/app.js +143 -12
  91. package/dist/web/app.js.map +3 -3
  92. package/docs/release-notes/v1.1.2.md +21 -0
  93. package/docs/release-notes/v1.2.0.md +48 -0
  94. package/package.json +1 -1
package/dist/web/app.js CHANGED
@@ -751,8 +751,10 @@
751
751
  return v.length > 24 ? v.slice(0, 23) + "\u2026" : v;
752
752
  }
753
753
  __name(shortModel, "shortModel");
754
- function permBadge(mode, agentId) {
755
- return '<span class="pbdg ' + esc(mode) + '"' + (agentId ? ' data-permof="' + esc(agentId) + '"' : "") + ' title="permissions: ' + esc(PERM_NAMES[mode] || mode) + (agentId ? " \u2014 click to change" : "") + '">' + esc(mode) + "</span>";
754
+ function permBadge(mode, agentId, kind) {
755
+ var cell = kind && permProfile(kind).modes ? permProfile(kind).modes[mode] : null;
756
+ var what = cell && cell.label ? cell.label : "permissions: " + (PERM_NAMES[mode] || mode);
757
+ return '<span class="pbdg ' + esc(mode) + '"' + (agentId ? ' data-permof="' + esc(agentId) + '"' : "") + ' title="' + esc(what) + (agentId ? " \u2014 click to change" : "") + '">' + esc(mode) + "</span>";
756
758
  }
757
759
  __name(permBadge, "permBadge");
758
760
  var KMOD = /Mac|iPhone|iPad/.test(navigator.platform || "") ? "\u2318" : "Ctrl+";
@@ -1845,6 +1847,93 @@
1845
1847
  return '<div class="shiftcard"><div class="shh">\u{1F319} <b>Night shift report</b><span class="shsum">' + esc(String(p.summary || "")) + '</span><span class="shmeta">' + (took ? took + " min" : "") + (p.costUsd ? " \xB7 " + money(p.costUsd) : "") + "</span></div>" + (p.stopped ? '<div class="shstop">Stopped early: ' + esc(String(p.stopped)) + "</div>" : "") + '<ul class="shl">' + rows + "</ul></div>";
1846
1848
  }
1847
1849
  __name(shiftCard, "shiftCard");
1850
+ var SEV_WORD = { high: "high", medium: "medium", low: "low" };
1851
+ function reviewCard(e) {
1852
+ var p = e.payload || {}, t = p.target || {};
1853
+ var what = t.kind === "file" ? t.file : t.kind === "diff" ? "the uncommitted changes" : t.label || "your request";
1854
+ if (p.phase === "started") {
1855
+ return '<div class="sys reviewline">\u{1F50D} ' + esc((p.agents || []).map(labelOf).join(", ")) + " reviewing " + esc(what) + " \u2014 read-only, nobody takes the baton" + (p.focus ? " \xB7 focus: " + esc(p.focus) : "") + "</div>";
1856
+ }
1857
+ if (p.phase !== "done") return "";
1858
+ var rows = (p.findings || []).map(function(f) {
1859
+ var loc = f.file ? '<code class="rvloc">' + esc(f.file + (f.line ? ":" + f.line : "")) + "</code>" : "";
1860
+ return '<li class="rvf" data-finding="' + esc(f.id) + '"><span class="rvsev sev-' + esc(f.severity || "note") + '">' + esc(SEV_WORD[f.severity] || "note") + "</span>" + avatarFor(f.agent) + (f.agreed || []).map(function(a) {
1861
+ return avatarFor(a.agent);
1862
+ }).join("") + (f.agreed && f.agreed.length ? '<span class="rvagree" title="' + esc((f.agreed || []).map(function(a) {
1863
+ return labelOf(a.agent) + ": " + a.text;
1864
+ }).join("\n")) + '">' + (f.agreed.length + 1) + " agree</span>" : "") + loc + '<span class="rvtext">' + esc(f.text) + '</span><span class="rvact"><button type="button" class="btn xs outline" data-finding-decide="' + esc(f.id) + '" data-decision="accepted" title="accept: it becomes a project decision every agent is briefed with">Accept</button><button type="button" class="btn xs ghost" data-finding-decide="' + esc(f.id) + '" data-decision="rejected">Reject</button></span><span class="rvdone"></span></li>';
1865
+ }).join("");
1866
+ var quiet = (p.results || []).map(function(r) {
1867
+ var wrote = r.edited && r.edited.length ? '<div class="rvquiet err">\u26A0 ' + esc(labelOf(r.agent)) + " changed files in a read-only review: " + esc(r.edited.slice(0, 6).join(", ")) + (r.edited.length > 6 ? ", \u2026" : "") + "</div>" : "";
1868
+ return wrote + reviewQuiet(r);
1869
+ }).join("");
1870
+ function reviewQuiet(r) {
1871
+ if (r.status === "failed") return '<div class="rvquiet err">' + esc(labelOf(r.agent)) + " didn\u2019t finish: " + esc(String(r.error || "").slice(0, 200)) + "</div>";
1872
+ if ((p.findings || []).some(function(f) {
1873
+ return f.agent === r.agent || (f.agreed || []).some(function(a) {
1874
+ return a.agent === r.agent;
1875
+ });
1876
+ })) return "";
1877
+ var first = String(r.reply || "").split("\n").filter(function(l) {
1878
+ return l.trim();
1879
+ })[0] || "(no reply)";
1880
+ return '<div class="rvquiet">' + esc(labelOf(r.agent)) + ": " + esc(first.slice(0, 220)) + "</div>";
1881
+ }
1882
+ __name(reviewQuiet, "reviewQuiet");
1883
+ return '<div class="reviewcard" data-review="' + esc(p.reviewId) + '"><div class="rvh">\u{1F50D} <b>Review</b><span class="rvsum">' + esc(p.summary || "") + '</span><span class="rvwhat">' + esc(what) + "</span></div>" + (rows ? '<ul class="rvl">' + rows + "</ul>" : "") + quiet + "</div>";
1884
+ }
1885
+ __name(reviewCard, "reviewCard");
1886
+ var CSTAGE_MARK = { waiting: "\xB7", running: "\u25D0", done: "\u2713", skipped: "\u2013", failed: "\u2717" };
1887
+ function councilCard(e) {
1888
+ var p = e.payload || {};
1889
+ if (p.phase !== "started") return "";
1890
+ var rows = (p.stages || []).map(function(st) {
1891
+ return '<li class="cst cs-waiting" data-cstage="' + esc(st.key) + '"><span class="csm">' + CSTAGE_MARK.waiting + '</span><span class="csn">' + esc(st.name) + '</span><span class="csd" title="' + esc(st.what || "") + '">' + esc(st.what || "") + "</span></li>";
1892
+ }).join("");
1893
+ return '<div class="councilcard cc-running" data-council="' + esc(p.councilId) + '"><div class="cch">\u{1F3DB} <b>Council</b><span class="ccgoal">' + esc(p.goal || "") + '</span><button type="button" class="btn xs outline" data-council-stop="' + esc(p.councilId) + '">Stop</button></div><div class="ccwho">' + avatarFor(p.planner) + " " + esc(labelOf(p.planner)) + " plans and builds \xB7 " + (p.reviewers || []).map(function(r) {
1894
+ return avatarFor(r) + " " + esc(labelOf(r));
1895
+ }).join(" ") + ' review</div><ol class="ccl">' + rows + '</ol><div class="ccsum"></div></div>';
1896
+ }
1897
+ __name(councilCard, "councilCard");
1898
+ function settleCouncil(root2, p) {
1899
+ var cards = (root2 || document).querySelectorAll('.councilcard[data-council="' + String(p.councilId).replace(/"/g, "") + '"]');
1900
+ Array.prototype.forEach.call(cards, function(card) {
1901
+ if (p.phase === "stage") {
1902
+ var row = card.querySelector('[data-cstage="' + String(p.stage).replace(/"/g, "") + '"]');
1903
+ if (!row) return;
1904
+ row.className = "cst cs-" + p.status;
1905
+ row.querySelector(".csm").textContent = CSTAGE_MARK[p.status] || "\xB7";
1906
+ if (p.detail || p.status !== "running") {
1907
+ var d = row.querySelector(".csd");
1908
+ d.textContent = p.detail || (p.status === "running" ? d.textContent : "");
1909
+ }
1910
+ } else if (p.phase === "done") {
1911
+ card.classList.remove("cc-running");
1912
+ card.classList.add("cc-" + p.status);
1913
+ var btn = card.querySelector("[data-council-stop]");
1914
+ if (btn) btn.remove();
1915
+ card.querySelector(".ccsum").textContent = p.summary || "";
1916
+ }
1917
+ });
1918
+ return cards.length > 0;
1919
+ }
1920
+ __name(settleCouncil, "settleCouncil");
1921
+ function settleFinding(root2, findingId, decision) {
1922
+ var done = false;
1923
+ Array.prototype.forEach.call((root2 || document).querySelectorAll('.rvf[data-finding="' + String(findingId).replace(/"/g, "") + '"]'), function(row) {
1924
+ row.classList.remove("fd-accepted", "fd-rejected");
1925
+ row.classList.add("fd-" + decision);
1926
+ var lbl = row.querySelector(".rvdone");
1927
+ if (lbl) lbl.textContent = decision === "accepted" ? "\u2713 accepted \u2014 recorded as a decision" : "\u2715 rejected";
1928
+ Array.prototype.forEach.call(row.querySelectorAll("[data-finding-decide]"), function(b) {
1929
+ b.disabled = false;
1930
+ b.hidden = b.getAttribute("data-decision") === decision;
1931
+ });
1932
+ done = true;
1933
+ });
1934
+ return done;
1935
+ }
1936
+ __name(settleFinding, "settleFinding");
1848
1937
  function avatarFor(id) {
1849
1938
  var s = String(id || "?"), k = kindOf(s) || (AGENT_LABELS[s] ? s : null);
1850
1939
  var mine = state.project && (state.project.agents || []).filter(function(a) {
@@ -2087,6 +2176,8 @@
2087
2176
  }
2088
2177
  if (e.kind === "proof") return proofCard(e);
2089
2178
  if (e.kind === "shift") return shiftCard(e);
2179
+ if (e.kind === "review") return reviewCard(e);
2180
+ if (e.kind === "council") return councilCard(e);
2090
2181
  if (e.kind === "checkpoint") {
2091
2182
  if (p.reason !== "rewound") return tview() === "verbose" ? '<div class="sys" style="opacity:.6">\u21BA checkpoint \xB7 ' + esc(p.label || p.id) + "</div>" : "";
2092
2183
  return '<div class="sys ok">\u21BA Rewound to \u201C' + esc(String(p.label || p.id).slice(0, 80)) + "\u201D \xB7 " + Number(p.files || 0) + " file" + (Number(p.files || 0) === 1 ? "" : "s") + (p.undo ? ' <button class="btn xs outline" type="button" data-rewind="' + esc(p.undo) + '">Undo the rewind</button>' : "") + "</div>";
@@ -10947,7 +11038,8 @@
10947
11038
  else adapters.forEach(function(a) {
10948
11039
  var hh = hue(a.id);
10949
11040
  var curA = a.id === state.selected;
10950
- html += '<div class="frow agentrow' + (curA ? " cur" : "") + '" data-agent="' + esc(a.id) + '" title="click to aim your next message at ' + esc(a.id) + '"><span class="adot' + (a.busy ? " busy" : "") + '"></span>' + brandMark(a.kind) + '<span class="fp" style="color:hsl(' + hh + ',55%,var(--agent-l))">' + esc(a.id) + "</span>" + (a.id === p.holder ? ' <span class="abadge">baton</span>' : "") + // your project decides what jobs exist — click and type
11041
+ html += '<div class="frow agentrow' + (curA ? " cur" : "") + '" data-agent="' + esc(a.id) + '" title="click to aim your next message at ' + esc(a.id) + '"><span class="adot' + (a.busy ? " busy" : "") + '"></span>' + brandMark(a.kind) + '<span class="fp" style="color:hsl(' + hh + ',55%,var(--agent-l))">' + esc(a.id) + "</span>" + (a.id === p.holder ? ' <span class="abadge">baton</span>' : "") + // its effective mode, and on hover what that mode lets this agent do (#261)
11042
+ permBadge(permOf(a), void 0, a.kind) + (a.signedOut ? ' <span class="abadge err" title="' + esc(a.signedOut) + '">signed out</span>' : "") + (a.readOnly ? ' <span class="abadge" title="can answer, can\u2019t change files">read-only</span>' : "") + // your project decides what jobs exist — click and type
10951
11043
  '<span class="role edit' + (!a.role || a.role === a.id || a.role === a.kind ? " same" : "") + '" data-role-p="' + esc(view.pid) + '" data-role-a="' + esc(a.id) + '" title="click to rename this job">' + esc(a.role || "\u2026") + "</span></div>";
10952
11044
  });
10953
11045
  var bridges = p ? p.agents.filter(function(a) {
@@ -11201,7 +11293,7 @@
11201
11293
  var lbl = agentLabel(a.kind, a.id), last = a.last;
11202
11294
  var sub = [a.id !== lbl ? a.id : "", a.role || ""].filter(Boolean).join(" \xB7 ");
11203
11295
  var chatName = a.chatTitle || (a.chat === "main" ? "Main thread" : a.chat);
11204
- return '<div class="frow' + (a.busy ? " busy" : "") + '" data-fagent="' + esc(a.id) + '"><span class="fg">' + agentGlyph(a.kind, a.id) + '</span><span class="fn"><b>' + esc(lbl) + "</b>" + (sub ? "<small>" + esc(sub) + "</small>" : "") + '</span><span class="fs">' + (a.busy ? '<span class="fspin"></span>working' + (a.since ? " \xB7 " + fleetSince(a.since) : "") : '<span class="fidle"></span>idle') + '</span><span class="fa">' + (a.chat ? '<button class="fthr" type="button" data-fchat="' + esc(a.chat) + '" data-fpid="' + esc(info.id) + '" title="open this thread">' + ICONS.thread + "<span>" + esc(chatName) + "</span></button>" : "") + (last && last.line ? '<span class="fline">' + esc(last.line) + '<span class="ft">' + rel(last.ts) + "</span></span>" : a.chat ? "" : '<span class="fline">no activity yet</span>') + '</span><span class="fb">' + (a.holdsBaton ? '<span class="fbaton" title="holds the baton">baton</span>' : "") + permBadge(a.permissions || "auto") + "</span></div>";
11296
+ return '<div class="frow' + (a.busy ? " busy" : "") + '" data-fagent="' + esc(a.id) + '"><span class="fg">' + agentGlyph(a.kind, a.id) + '</span><span class="fn"><b>' + esc(lbl) + "</b>" + (sub ? "<small>" + esc(sub) + "</small>" : "") + '</span><span class="fs">' + (a.busy ? '<span class="fspin"></span>working' + (a.since ? " \xB7 " + fleetSince(a.since) : "") : '<span class="fidle"></span>idle') + '</span><span class="fa">' + (a.chat ? '<button class="fthr" type="button" data-fchat="' + esc(a.chat) + '" data-fpid="' + esc(info.id) + '" title="open this thread">' + ICONS.thread + "<span>" + esc(chatName) + "</span></button>" : "") + (last && last.line ? '<span class="fline">' + esc(last.line) + '<span class="ft">' + rel(last.ts) + "</span></span>" : a.chat ? "" : '<span class="fline">no activity yet</span>') + '</span><span class="fb">' + (a.holdsBaton ? '<span class="fbaton" title="holds the baton">baton</span>' : "") + permBadge(a.permissions || "auto", void 0, a.kind) + "</span></div>";
11205
11297
  }
11206
11298
  __name(fleetAgentRow, "fleetAgentRow");
11207
11299
  function openFleetThread(toPid, chat) {
@@ -11327,7 +11419,7 @@
11327
11419
  canvas: "<b>Right now.</b> Who is running this second, who is idle, and where the " + fx + "baton</b> is \u2014 the ring pulses on whoever holds it. Every agent hangs off the <b>one shared brain</b> in the middle: that is the memory they all read and write, which is the whole point of the fleet. Updates live as turns start and finish. " + drag,
11328
11420
  graph: "<b>What already happened.</b> The baton\u2019s actual route through the fleet, left to right, oldest handoff first. Each " + fx + "\u2192</b> is one real handoff from the event log; <code>19t</code> under a name is turns that agent took. Live view is the <b>Live fleet</b> tab \u2014 this one is history. " + drag,
11329
11421
  timeline: "<b>The run, in the order it happened.</b> Every turn, handoff, route, memory fold, budget pause and self-heal line on one spine \u2014 so \u201Cwhy did it do that?\u201D is answered by scrolling rather than by guessing. The \u{1F4A1} lines are <b>decisions</b>; click one to read the reasoning behind it.",
11330
- usage: "<b>What it cost, and what it used.</b> Spend and tokens split by agent, model, chat, day and tool, beside the turn metrics. Each turn\u2019s dollars are counted once, on the turn that spent them. An agent that reports no price (a subscription CLI, a free model) shows its tokens at $0 and is flagged, so the total is the least you spent, not an estimate.",
11422
+ usage: "<b>What it cost, and what it used.</b> Spend and tokens split by agent, model, chat, day and tool, beside the turn metrics. Each turn\u2019s dollars are counted once, on the turn that spent them. An agent that reports no price (a subscription CLI, a free model) shows its tokens at $0 and is flagged, so the total is the least you spent; beside it, \u201Cest.\u201D is what those tokens would cost at the model\u2019s API price, when Loom knows it \u2014 an estimate, never added to the total.",
11331
11423
  metrics: "<b>Where the run\u2019s time and money actually went.</b> Nothing here is estimated: tokens and cost are what each agent\u2019s own CLI reported for the turn, and the durations are the spans Loom exports. Every agent carries a 0\u2013100 <b>health score</b>, and <b>\u26A0 Triage</b> root-causes a bad one from that agent\u2019s own spans.",
11332
11424
  decisions: "<b>The reasoning, not just the result.</b> Every choice an agent made, with what it weighed and rejected \u2014 mined out of its own prose after each turn, so it survives the agent that made it. A confidence it actually measured and one merely pattern-matched are labelled differently, because a number you cannot source is worse than no number.",
11333
11425
  alerts: "<b>What your monitoring told Loom, and what Loom did about it.</b> A firing alert takes the named agent out of rotation \u2014 it is refused the baton until the alert resolves \u2014 and a resolved one puts it back. This is the part your dashboards cannot show you: they know the alert fired, only Loom knows the fleet reacted.",
@@ -11856,7 +11948,7 @@
11856
11948
  var tile = /* @__PURE__ */ __name(function(label, val2, sub) {
11857
11949
  return '<div class="obminicard"><div class="obcl">' + esc(label) + '</div><div class="obcv sm">' + val2 + "</div>" + (sub ? '<div class="usub">' + sub + "</div>" : "") + "</div>";
11858
11950
  }, "tile");
11859
- var tiles = tile("Spend", money(t.usd || 0), t.unpriced ? t.unpriced + " unpriced turn" + (t.unpriced === 1 ? "" : "s") : "every turn priced") + tile("Per turn", money(t.usdPerTurn || 0), (t.turns || 0) + " turns") + tile("Tokens in", tok(t.tokensIn), pct(t.cachedIn, t.tokensIn) + " from cache") + tile("Tokens out", tok(t.tokensOut), t.reasoning ? tok(t.reasoning) + " thinking" : "") + tile("Avg turn", durfmt(t.avgTurnMs || 0), durfmt(t.ms || 0) + " in all") + tile("Tool calls", String(t.toolCalls || 0), (t.toolFailures || 0) + " failed") + tile("Errors", String(t.errors || 0), (t.questions || 0) + " questions asked") + tile("Changed", String(t.filesChanged || 0) + " files", '<span class="cadd">+' + (t.linesAdded || 0) + '</span> <span class="cdel">\u2212' + (t.linesRemoved || 0) + "</span>");
11951
+ var tiles = tile("Spend", money(t.usd || 0), t.unpriced ? (t.estUsd ? '<span title="tokens at the model\u2019s API price, for agents that report tokens but no cost">+ ~' + money(t.estUsd) + " est.</span> \xB7 " : "") + t.unpriced + " unpriced turn" + (t.unpriced === 1 ? "" : "s") : "every turn priced") + tile("Per turn", money(t.usdPerTurn || 0), (t.turns || 0) + " turns") + tile("Tokens in", tok(t.tokensIn), pct(t.cachedIn, t.tokensIn) + " from cache") + tile("Tokens out", tok(t.tokensOut), t.reasoning ? tok(t.reasoning) + " thinking" : "") + tile("Avg turn", durfmt(t.avgTurnMs || 0), durfmt(t.ms || 0) + " in all") + tile("Tool calls", String(t.toolCalls || 0), (t.toolFailures || 0) + " failed") + tile("Errors", String(t.errors || 0), (t.questions || 0) + " questions asked") + tile("Changed", String(t.filesChanged || 0) + " files", '<span class="cadd">+' + (t.linesAdded || 0) + '</span> <span class="cdel">\u2212' + (t.linesRemoved || 0) + "</span>");
11860
11952
  var seen = {};
11861
11953
  (u.byDay || []).forEach(function(d) {
11862
11954
  seen[d.day] = d;
@@ -11887,11 +11979,14 @@
11887
11979
  var v = t.usd > 0 ? x.usd / t.usd : (x.tokensIn + x.tokensOut) / ((t.tokensIn || 0) + (t.tokensOut || 0) || 1);
11888
11980
  return '<span class="ushare"><i style="width:' + Math.round(v * 100) + '%"></i></span>';
11889
11981
  }, "shareBar");
11982
+ var estNote = /* @__PURE__ */ __name(function(x) {
11983
+ return x.estUsd ? ' <span class="uest" title="tokens at the model\u2019s API price \u2014 this agent reports tokens but no cost">~' + money(x.estUsd) + " est.</span>" : "";
11984
+ }, "estNote");
11890
11985
  var agentRows = (u.byAgent || []).map(function(a) {
11891
- return "<tr><td>" + avatarFor(a.agentId) + " " + esc(labelOf(a.agentId)) + (a.unpriced ? ' <span class="uflag" title="reports tokens but no price">unpriced</span>' : "") + '</td><td class="num">' + money(a.usd) + "</td><td>" + shareBar(a) + " " + share(a) + '</td><td class="num">' + a.turns + '</td><td class="num">' + tok(a.tokensIn) + '</td><td class="num">' + tok(a.tokensOut) + '</td><td class="num">' + pct(a.cachedIn, a.tokensIn) + '</td><td class="num">' + (a.turns ? durfmt(a.ms / a.turns) : "\u2014") + '</td><td class="num">' + a.toolCalls + (a.toolFailures ? ' <span class="cdel">(' + a.toolFailures + ")</span>" : "") + '</td><td class="num">' + a.errors + '</td><td class="umodels">' + esc((a.models || []).join(", ")) + "</td></tr>";
11986
+ return "<tr><td>" + avatarFor(a.agentId) + " " + esc(labelOf(a.agentId)) + (a.unpriced ? ' <span class="uflag" title="reports tokens but no price">unpriced</span>' : "") + '</td><td class="num">' + money(a.usd) + estNote(a) + "</td><td>" + shareBar(a) + " " + share(a) + '</td><td class="num">' + a.turns + '</td><td class="num">' + tok(a.tokensIn) + '</td><td class="num">' + tok(a.tokensOut) + '</td><td class="num">' + pct(a.cachedIn, a.tokensIn) + '</td><td class="num">' + (a.turns ? durfmt(a.ms / a.turns) : "\u2014") + '</td><td class="num">' + a.toolCalls + (a.toolFailures ? ' <span class="cdel">(' + a.toolFailures + ")</span>" : "") + '</td><td class="num">' + a.errors + '</td><td class="umodels">' + esc((a.models || []).join(", ")) + "</td></tr>";
11892
11987
  }).join("");
11893
11988
  var modelRows = (u.byModel || []).map(function(m) {
11894
- return "<tr><td><code>" + esc(m.model) + '</code></td><td class="num">' + money(m.usd) + "</td><td>" + shareBar(m) + " " + share(m) + '</td><td class="num">' + m.turns + '</td><td class="num">' + tok(m.tokensIn) + '</td><td class="num">' + tok(m.tokensOut) + '</td><td class="num">' + money(m.turns ? m.usd / m.turns : 0) + "</td></tr>";
11989
+ return "<tr><td><code>" + esc(m.model) + '</code></td><td class="num">' + money(m.usd) + estNote(m) + "</td><td>" + shareBar(m) + " " + share(m) + '</td><td class="num">' + m.turns + '</td><td class="num">' + tok(m.tokensIn) + '</td><td class="num">' + tok(m.tokensOut) + '</td><td class="num">' + money(m.turns ? m.usd / m.turns : 0) + "</td></tr>";
11895
11990
  }).join("");
11896
11991
  var chatRows = (u.byChat || []).map(function(c) {
11897
11992
  return '<tr class="uclick" data-uchat="' + esc(c.chat) + '"><td>' + esc(c.title || c.chat) + '</td><td class="num">' + money(c.usd) + '</td><td class="num">' + c.turns + '</td><td class="num">' + tok(c.tokensIn + c.tokensOut) + "</td></tr>";
@@ -12898,7 +12993,7 @@
12898
12993
  if (c.race) {
12899
12994
  el.innerHTML = modeSeg + '<span class="colbl" title="each gets the same prompt, in its own worktree">Racers</span><span class="cowk" id="cowk">' + roster.map(function(a) {
12900
12995
  var on = !c.off[a.id];
12901
- return '<button type="button" class="cowchip' + (on ? " on" : "") + '" data-wk="' + esc(a.id) + '" aria-pressed="' + on + '"><span class="cwon">' + (on ? ICONS.check : "") + "</span>" + agentGlyph(a.kind, a.id) + '<span class="cwn">' + esc(agentLabel(a.kind, a.id)) + '</span><span class="cwset">' + modelBadge(a) + permBadge(permOf(a), a.id) + "</span></button>";
12996
+ return '<button type="button" class="cowchip' + (on ? " on" : "") + '" data-wk="' + esc(a.id) + '" aria-pressed="' + on + '"><span class="cwon">' + (on ? ICONS.check : "") + "</span>" + agentGlyph(a.kind, a.id) + '<span class="cwn">' + esc(agentLabel(a.kind, a.id)) + '</span><span class="cwset">' + modelBadge(a) + permBadge(permOf(a), a.id, a.kind) + "</span></button>";
12902
12997
  }).join("") + "</span>";
12903
12998
  Array.prototype.forEach.call(el.querySelectorAll("[data-wk]"), function(b) {
12904
12999
  b.onclick = function() {
@@ -12919,9 +13014,9 @@
12919
13014
  if (osb) osb.title = "start the race";
12920
13015
  return;
12921
13016
  }
12922
- el.innerHTML = modeSeg + '<span class="colbl" title="plans the goal, splits it into tasks, reviews the results">Lead</span><button class="cagent" id="corchpick" type="button" title="who plans the goal and reviews the results">' + agentGlyph(lead.kind, lead.id) + '<span class="can">' + esc(agentLabel(lead.kind, lead.id)) + "</span>" + permBadge(permOf(lead), lead.id) + modelBadge(lead) + '<span class="cchev">' + ICONS.chevron + '</span></button><span class="colbl" title="who can be given tasks \u2014 click to include or leave out">Team</span><span class="cowk" id="cowk">' + roster.map(function(a) {
13017
+ el.innerHTML = modeSeg + '<span class="colbl" title="plans the goal, splits it into tasks, reviews the results">Lead</span><button class="cagent" id="corchpick" type="button" title="who plans the goal and reviews the results">' + agentGlyph(lead.kind, lead.id) + '<span class="can">' + esc(agentLabel(lead.kind, lead.id)) + "</span>" + permBadge(permOf(lead), lead.id, lead.kind) + modelBadge(lead) + '<span class="cchev">' + ICONS.chevron + '</span></button><span class="colbl" title="who can be given tasks \u2014 click to include or leave out">Team</span><span class="cowk" id="cowk">' + roster.map(function(a) {
12923
13018
  var on = !c.off[a.id];
12924
- return '<button type="button" class="cowchip' + (on ? " on" : "") + '" data-wk="' + esc(a.id) + '" aria-pressed="' + on + '" title="' + esc((on ? "included \u2014 click to leave out" : "left out \u2014 click to include") + " \xB7 " + a.id) + '"><span class="cwon">' + (on ? ICONS.check : "") + "</span>" + agentGlyph(a.kind, a.id) + '<span class="cwn">' + esc(agentLabel(a.kind, a.id)) + '</span><span class="cwset">' + modelBadge(a) + permBadge(permOf(a), a.id) + "</span></button>";
13019
+ return '<button type="button" class="cowchip' + (on ? " on" : "") + '" data-wk="' + esc(a.id) + '" aria-pressed="' + on + '" title="' + esc((on ? "included \u2014 click to leave out" : "left out \u2014 click to include") + " \xB7 " + a.id) + '"><span class="cwon">' + (on ? ICONS.check : "") + "</span>" + agentGlyph(a.kind, a.id) + '<span class="cwn">' + esc(agentLabel(a.kind, a.id)) + '</span><span class="cwset">' + modelBadge(a) + permBadge(permOf(a), a.id, a.kind) + "</span></button>";
12925
13020
  }).join("") + // A roster of CLIs is whatever you happen to have installed. An API
12926
13021
  // model is a name off a list, so it can be added here, in the row
12927
13022
  // where you are already deciding who runs the goal.
@@ -13043,7 +13138,7 @@
13043
13138
  m.innerHTML = '<div class="cmhead">who orchestrates</div>' + roster.map(function(a, i) {
13044
13139
  var lbl = agentLabel(a.kind, a.id);
13045
13140
  var sub = agentSub(a, lbl);
13046
- return '<div class="cmi" data-oi="' + i + '"><span class="ic">' + agentGlyph(a.kind, a.id) + "</span><span>" + esc(lbl) + "</span>" + permBadge(permOf(a), a.id) + (a.busy ? '<span class="cmbusy">working</span>' : "") + (a.id === c.orchestrator ? '<span class="tick"' + (a.busy ? ' style="margin-left:6px"' : "") + ">" + ICONS.info + "</span>" : sub && !a.busy ? '<span class="sub">' + esc(sub) + "</span>" : "") + "</div>";
13141
+ return '<div class="cmi" data-oi="' + i + '"><span class="ic">' + agentGlyph(a.kind, a.id) + "</span><span>" + esc(lbl) + "</span>" + permBadge(permOf(a), a.id, a.kind) + (a.busy ? '<span class="cmbusy">working</span>' : "") + (a.id === c.orchestrator ? '<span class="tick"' + (a.busy ? ' style="margin-left:6px"' : "") + ">" + ICONS.info + "</span>" : sub && !a.busy ? '<span class="sub">' + esc(sub) + "</span>" : "") + "</div>";
13047
13142
  }).join("");
13048
13143
  Array.prototype.forEach.call(m.querySelectorAll("[data-oi]"), function(row) {
13049
13144
  row.onmousedown = function(ev) {
@@ -17530,6 +17625,14 @@
17530
17625
  if (c.getAttribute("data-niwho") === e.agentId) settleQuestionCard(c, null);
17531
17626
  });
17532
17627
  }
17628
+ if (e.kind === "review" && pl.phase === "decided") {
17629
+ settleFinding(feed, pl.findingId, pl.decision);
17630
+ return;
17631
+ }
17632
+ if (e.kind === "council" && (pl.phase === "stage" || pl.phase === "done")) {
17633
+ settleCouncil(feed, pl);
17634
+ return;
17635
+ }
17533
17636
  if (e.kind === "approval" && e.payload && e.payload.phase === "decided") {
17534
17637
  if (settleApprovalCards(e.payload.approvalId, e.payload.behavior, e.payload.message)) return;
17535
17638
  }
@@ -19354,6 +19457,34 @@
19354
19457
  });
19355
19458
  return;
19356
19459
  }
19460
+ var cst = ev.target.closest && ev.target.closest("[data-council-stop]");
19461
+ if (cst) {
19462
+ ev.preventDefault();
19463
+ ev.stopPropagation();
19464
+ cst.disabled = true;
19465
+ api("/api/projects/" + pid + "/councils", { method: "DELETE" }).then(function() {
19466
+ cst.textContent = "Stopping\u2026";
19467
+ }).catch(function(err) {
19468
+ cst.disabled = false;
19469
+ toast(err.message);
19470
+ });
19471
+ return;
19472
+ }
19473
+ var fdb = ev.target.closest && ev.target.closest("[data-finding-decide]");
19474
+ if (fdb) {
19475
+ ev.preventDefault();
19476
+ ev.stopPropagation();
19477
+ var fid = fdb.getAttribute("data-finding-decide"), dec = fdb.getAttribute("data-decision");
19478
+ fdb.disabled = true;
19479
+ api("/api/projects/" + pid + "/findings/" + encodeURIComponent(fid), { method: "POST", body: JSON.stringify({ decision: dec }) }).then(function() {
19480
+ settleFinding(document, fid, dec);
19481
+ if (dec === "accepted") toast("recorded as a decision \u2014 every agent is briefed with it");
19482
+ }).catch(function(err) {
19483
+ fdb.disabled = false;
19484
+ toast(err.message);
19485
+ });
19486
+ return;
19487
+ }
19357
19488
  var shs = ev.target.closest && ev.target.closest("[data-shift-stop]");
19358
19489
  if (shs) {
19359
19490
  ev.preventDefault();