@coreplane/switchboard 1.242.0 → 1.244.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.
- package/dist/assets/config/config.example.yaml +20 -1
- package/dist/assets/deploy/cloudflare-memory/worker.ts +234 -29
- package/dist/assets/deploy/cloudflare-resident/gc.ts +8 -1
- package/dist/assets/deploy/cloudflare-resident/worker.ts +483 -75
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +13 -2
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/project.json +3 -3
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +31 -35
- package/dist/assets/src/config/profile.ts +68 -3
- package/dist/assets/src/core/authz/policy.ts +4 -0
- package/dist/assets/src/core/authz/types.ts +10 -0
- package/dist/assets/src/core/budgets.ts +349 -0
- package/dist/assets/src/core/coordinator/contract.ts +32 -0
- package/dist/assets/src/core/coordinator/driver.ts +75 -22
- package/dist/assets/src/core/costs.ts +9 -76
- package/dist/assets/src/core/delivery.ts +1 -1
- package/dist/assets/src/core/modelPricing.ts +212 -0
- package/dist/assets/src/core/prDescriptionTypes.ts +29 -24
- package/dist/assets/src/core/reviewVerdict.ts +7 -0
- package/dist/assets/src/core/runEvents.ts +72 -5
- package/dist/assets/src/core/runFriction.ts +3 -1
- package/dist/assets/src/core/runRecord.ts +78 -0
- package/dist/assets/src/core/runUsage.ts +158 -47
- package/dist/assets/src/core/schedules.ts +3 -0
- package/dist/assets/src/core/ship/contract.ts +2 -1
- package/dist/assets/src/core/ship/coordinator.ts +312 -97
- package/dist/assets/src/core/ship/handoff.ts +9 -0
- package/dist/assets/src/core/ship/renewal.ts +155 -0
- package/dist/assets/src/core/trace/attrs.ts +11 -0
- package/dist/assets/src/core/trace/types.ts +4 -3
- package/dist/assets/src/execution/bashTimeout.ts +8 -5
- package/dist/assets/src/execution/residentRefresh.ts +52 -7
- package/dist/assets/src/execution/residentSteps.ts +1 -1
- package/dist/assets/src/execution/residentText.ts +8 -3
- package/dist/assets/src/mcp/registry.ts +3 -0
- package/dist/assets/web/dist/.vite/manifest.json +432 -429
- package/dist/assets/web/dist/assets/{AppShell-IDMGG6yi.js → AppShell-Ck4hSZDi.js} +1 -1
- package/dist/assets/web/dist/assets/CostsPage-DvhjDMka.js +2 -0
- package/dist/assets/web/dist/assets/DeliveryPage-DBlI_Yq2.js +1 -0
- package/dist/assets/web/dist/assets/HomePage-XFWPxDiJ.js +2 -0
- package/dist/assets/web/dist/assets/{NotFoundPage-C97EhJcQ.js → NotFoundPage-CiGrVOyv.js} +1 -1
- package/dist/assets/web/dist/assets/PendingTurnRow-BHCSpsPf.js +1 -0
- package/dist/assets/web/dist/assets/{ResidentDetailPage-DTMBgnIW.js → ResidentDetailPage-C5USV_8P.js} +1 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-CSKisWnX.js +1 -0
- package/dist/assets/web/dist/assets/RunFoldRow-BPMQxTcl.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-CZe9Yodw.js +9 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-BfA333Qy.js +1 -0
- package/dist/assets/web/dist/assets/{RunsTabs-BUfdk0lH.js → RunsTabs-BWRHzhtT.js} +1 -1
- package/dist/assets/web/dist/assets/ScheduledPage-DBaChcJZ.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-CqVi9-Ac.js +1 -0
- package/dist/assets/web/dist/assets/{StatusDot-ELoXHlFt.js → StatusDot-_Q_bmReh.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-BoeFwYP2.js → Tooltip-Cafsgd4m.js} +1 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-CdoBZwtf.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-oBNfPJR0.js → angular-html-Dail07rr.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-BvNwsyWA.js → angular-ts-BReExio8.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-CNUdRlYf.js → apl-DhNO6Yyx.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-Zb0NriSe.js → astro-BYotTY4C.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-qPRVheqq.js → blade-mUxTtD1X.js} +1 -1
- package/dist/assets/web/dist/assets/{c-D8Awx4YO.js → c-DhuiwcSQ.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-Bt72Mhsx.js → chapel-GD2vFtRi.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-BOBacexg.js → cobol-BhFDwkes.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-E4u0liHW.js → coffee-DWUgbE0r.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-q2sLNlul.js → cpp-CO9XFjGW.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-DyWqUnlb.js → crystal-B9pVurpn.js} +1 -1
- package/dist/assets/web/dist/assets/{css-CQY0hFsD.js → css-CFVVV6lI.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-BU5UivXC.js → dist-BtPs2AkQ.js} +2 -2
- package/dist/assets/web/dist/assets/{edge-C1MwhJkX.js → edge-BDhIDQIb.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-Bb3YbHfn.js → elixir-DG3J8WoD.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-DAN9IGQw.js → elm-FiOdiaOE.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-BEB8Xlsj.js → erb-vxbtW503.js} +1 -1
- package/dist/assets/web/dist/assets/format-BldUwl_R.js +1 -0
- package/dist/assets/web/dist/assets/{git-rebase-tqpjRfxO.js → git-rebase-DINz8M3H.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-Ccbo65zR.js → glimmer-js-yann6r9i.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-B6WBVMpE.js → glimmer-ts-CXTbrZn_.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-tRec3Fcu.js → glsl-DNYC-hq0.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-P8kbxT4F.js → graphql-CwzhHzi9.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-H9Zhkagy.js → hack-BBXF1cwR.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-DtEnpn7Z.js → haml-oEqBcsTy.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-DkgPfoAz.js → handlebars-YE5thkT1.js} +1 -1
- package/dist/assets/web/dist/assets/{html-D30RXpIs.js → html-Dp4jRQ1j.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-DwozLrEx.js → html-derivative-DCT1GYtQ.js} +1 -1
- package/dist/assets/web/dist/assets/{http-DXuzBAPm.js → http-BPcGbmzJ.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-d1UUJIt_.js → hurl-CQnNHwyd.js} +1 -1
- package/dist/assets/web/dist/assets/indexRow-BmK74Vp1.js +1 -0
- package/dist/assets/web/dist/assets/{java-DL0gWf34.js → java-Dm8Ovj6W.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-Cl7vavnS.js → javascript-Btm9oJK9.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-CfQOWMX9.js → jinja-VZYjWF_U.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-KdYirlqm.js → jison-Dcmjxa9x.js} +1 -1
- package/dist/assets/web/dist/assets/{json-C9cDQ-Qj.js → json-DoWiOGZ8.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-CRx5NItd.js → jsx-Dvmo55ma.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-CmsQsZQl.js → julia-d8AtfV26.js} +1 -1
- package/dist/assets/web/dist/assets/{just-CsM3Q8TE.js → just-Cc5EStA-.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-BXCh5YRX.js → latex-B5fKKIH7.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-BF2vwK8p.js → liquid-DUYI9JND.js} +1 -1
- package/dist/assets/web/dist/assets/{lua-DcMBATrl.js → lua-BhMwKWpw.js} +1 -1
- package/dist/assets/web/dist/assets/main-C4366PZF.css +1 -0
- package/dist/assets/web/dist/assets/main-DlTNAC6c.js +28 -0
- package/dist/assets/web/dist/assets/{marko-88MndKvG.js → marko-Cnf2cPAi.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-DJ4kVAd8.js → mdc-FDDXoir9.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-CP6mRgtV.js → nginx-q-8U_1jR.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-QQfj3fpF.js → nim-DIA0TUWs.js} +1 -1
- package/dist/assets/web/dist/assets/{org-Bke3eBzc.js → org-CR88hy7S.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-Bkh0N7Kz.js → perl-Bkd8N7ja.js} +1 -1
- package/dist/assets/web/dist/assets/{php-Dnv1Piya.js → php-BOs84mvy.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-D6peFFI7.js → pug-BbpZBb3w.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-D8PEs-C-.js → qml-D2jQpjFE.js} +1 -1
- package/dist/assets/web/dist/assets/{r-B1EL9b_j.js → r-DKzcAQ22.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-BsMTIh2b.js → razor-SOQMWcC9.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-HhvC8spD.js → regexp-B6KK4W8d.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-D5paAxpg.js → rst-kPSg-NPf.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-BrQwhLrl.js → ruby-cKS-JYLH.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-afot2B1o.js → sas-DNxzE5gs.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-Efm-mwuG.js → scss-DGcHQ5OO.js} +1 -1
- package/dist/assets/web/dist/assets/{shellscript-BK0Vv5fT.js → shellscript-CRZeZBa5.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-BGqlMC7N.js → shellsession-DSNEL0HX.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-M0b4UwGM.js → soy-zisNbkDO.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-sxe6IE9j.js → sql-MG06KMMb.js} +1 -1
- package/dist/assets/web/dist/assets/{sseReplay-g7ml86LM.js → sseReplay-DaRtc7nI.js} +5 -5
- package/dist/assets/web/dist/assets/{stata-nPF_ddLP.js → stata-CB_wKwv9.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-D_GrC6u7.js → surrealql-DYdSt7re.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-DwL1AtNP.js → svelte-DHO-F7c7.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-7s7LTDkc.js → templ-CUL5L4Lr.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-Bx-5fMxe.js → tex-_JNEKjVW.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-DUMJnke_.js → ts-tags--9EpEV_8.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-BN8biPDe.js → tsx-BcNq2x2u.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-8nIu84TN.js → twig-EsY5Dk_D.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-BooSPq_S.js → typescript-CyA_HiUH.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-CTBiBsem.js → typst-SzaHlDt4.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-C6Ft4Lea.js → vue-D3cn5acV.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-Ba36dD5D.js → vue-html-6MxhUxOa.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-NkFexVo2.js → vue-vine-qJnqhgo9.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-C_THnHXZ.js → xml-DyRpC7xv.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-D8G5xqjY.js → xsl-BXMRPAsg.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-jAMIJzge.js → yaml-BNmPCtAN.js} +1 -1
- package/dist/cli.js +5843 -2246
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/CostsPage-CtmKhhOF.js +0 -2
- package/dist/assets/web/dist/assets/DeliveryPage-n1tz5I_v.js +0 -1
- package/dist/assets/web/dist/assets/HomePage-QtH1EwYF.js +0 -2
- package/dist/assets/web/dist/assets/PendingTurnRow-BZA_vQt3.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-CaDvXhzJ.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-mgyLW0oV.js +0 -1
- package/dist/assets/web/dist/assets/RunRoutePage-DypJYMQa.js +0 -6
- package/dist/assets/web/dist/assets/RunsIndexPage-DYraPoWD.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-DXD2gLJk.js +0 -1
- package/dist/assets/web/dist/assets/SettingsPage-BIGio8Y0.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-jhCrWW3i.js +0 -1
- package/dist/assets/web/dist/assets/indexRow-BD1VT8o8.js +0 -1
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +0 -1
- package/dist/assets/web/dist/assets/main-B2fX10aW.css +0 -1
- package/dist/assets/web/dist/assets/main-D4EA1g6n.js +0 -28
|
@@ -196,6 +196,15 @@ function entryLines(h: Handoff): Array<{ list: ListKey; bullet: string; row: str
|
|
|
196
196
|
];
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/** Each entry as one line, list by list — a deviation as `Deviation: from → to
|
|
200
|
+
* — why`, a follow-up as `what — where`, an unproven criterion as `Unproven:
|
|
201
|
+
* criterion — why` — the same words the ledger rows carry, for a reader that
|
|
202
|
+
* wants the handoff as plain lines (the thread's artifacts block). Empty for
|
|
203
|
+
* an empty handoff. */
|
|
204
|
+
export function handoffLines(h: Handoff): string[] {
|
|
205
|
+
return entryLines(h).map((e) => e.row);
|
|
206
|
+
}
|
|
207
|
+
|
|
199
208
|
function prLink(pr: HandoffRenderContext["pr"]): string | undefined {
|
|
200
209
|
return pr ? `[#${pr.number}](${pr.url})` : undefined;
|
|
201
210
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The renewal decision (decision 0046, Renewal: the grant decides, the lease
|
|
2
|
+
// continues). A lease is sized by the infrastructure; the problem is bounded by
|
|
3
|
+
// the grant. When a segment ends with its unit unfinished, the runner reads
|
|
4
|
+
// progress off the row — never off a model's word — and renews only when three
|
|
5
|
+
// things hold: the row shows progress, the grant has a renewal left and the
|
|
6
|
+
// spend is under its cap, and the pipeline's ask still passes the fit.
|
|
7
|
+
// Otherwise it stops and names the clause that failed; a person's reply spends
|
|
8
|
+
// a renewal by hand. Pure and node-free: the unit machine (a Worker) and the
|
|
9
|
+
// bot's routes compute the same decision from the same facts.
|
|
10
|
+
|
|
11
|
+
import { fit, type Grant, type Pipeline } from "../budgets.js";
|
|
12
|
+
import type { Handoff } from "./handoff.js";
|
|
13
|
+
|
|
14
|
+
/** A head the run pushed, as the record carries it (run-history item 2). */
|
|
15
|
+
export interface PushedHeadFact {
|
|
16
|
+
ref: string;
|
|
17
|
+
sha: string;
|
|
18
|
+
at?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProgressInput {
|
|
22
|
+
/** The unit's branch: a push to any other ref is not the unit's progress. */
|
|
23
|
+
branch: string;
|
|
24
|
+
/** The heads the segment's coding run pushed (`RunRecord.pushed`). */
|
|
25
|
+
pushed: readonly PushedHeadFact[];
|
|
26
|
+
/** The head the segment started from — the previous segment's recorded sha.
|
|
27
|
+
* Absent on a fresh branch, where any push is progress. */
|
|
28
|
+
startHead?: string;
|
|
29
|
+
/** When the segment's lease began; a push stamped before it is not this segment's. */
|
|
30
|
+
leaseStartedAt?: number;
|
|
31
|
+
/** The previous segment's handoff and this one's, when both exist. */
|
|
32
|
+
handoff?: { previous: Handoff; current: Handoff };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Progress =
|
|
36
|
+
| { progressed: true; by: "push"; sha: string }
|
|
37
|
+
| { progressed: true; by: "handoff" }
|
|
38
|
+
| { progressed: false; why: string };
|
|
39
|
+
|
|
40
|
+
/** Two shas name one commit when the shorter is a prefix of the longer. */
|
|
41
|
+
const sameSha = (a: string, b: string): boolean => {
|
|
42
|
+
const x = a.toLowerCase();
|
|
43
|
+
const y = b.toLowerCase();
|
|
44
|
+
return x.length <= y.length ? y.startsWith(x) : x.startsWith(y);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Progress is a fact the row records after unit eight and the runner reads
|
|
48
|
+
* without a model: the last head pushed to the unit's branch that differs from
|
|
49
|
+
* the head the segment started at and is not older than the lease; failing
|
|
50
|
+
* that, a handoff whose follow-ups shrank or whose deviations grew. */
|
|
51
|
+
export function progressOf(input: ProgressInput): Progress {
|
|
52
|
+
const last = [...input.pushed].reverse().find((h) => h.ref === input.branch);
|
|
53
|
+
if (last !== undefined) {
|
|
54
|
+
const newerThanLease =
|
|
55
|
+
input.leaseStartedAt === undefined || last.at === undefined || last.at >= input.leaseStartedAt;
|
|
56
|
+
const moved = input.startHead === undefined || !sameSha(last.sha, input.startHead);
|
|
57
|
+
if (newerThanLease && moved) return { progressed: true, by: "push", sha: last.sha };
|
|
58
|
+
}
|
|
59
|
+
const pushWhy = `no head newer than the lease's start was pushed to \`${input.branch}\``;
|
|
60
|
+
if (input.handoff === undefined) return { progressed: false, why: pushWhy };
|
|
61
|
+
const { previous, current } = input.handoff;
|
|
62
|
+
if (current.followUps.length < previous.followUps.length || current.deviations.length > previous.deviations.length)
|
|
63
|
+
return { progressed: true, by: "handoff" };
|
|
64
|
+
return { progressed: false, why: `${pushWhy} and the handoff is unchanged` };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RenewalInput {
|
|
68
|
+
grant: Grant;
|
|
69
|
+
/** Renewals already spent under this request: the segments beyond the first. */
|
|
70
|
+
renewalsSpent: number;
|
|
71
|
+
/** Dollars spent under this request so far; null when a run's model had no
|
|
72
|
+
* price, since a total that left a model's tokens out would understate it. */
|
|
73
|
+
spendUsd: number | null;
|
|
74
|
+
progress: Progress;
|
|
75
|
+
/** The pipeline the next segment would run: its fit is re-asserted before every segment. */
|
|
76
|
+
pipeline: Pipeline;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type RenewalWhy = "no_progress" | "grant_exhausted" | "cost_cap" | "unfit";
|
|
80
|
+
|
|
81
|
+
export type RenewalDecision =
|
|
82
|
+
| { renew: true; segment: number; from?: string; renewalsLeft: number }
|
|
83
|
+
| { renew: false; why: RenewalWhy; detail: string; renewalsLeft: number };
|
|
84
|
+
|
|
85
|
+
const usd = (n: number): string => `$${n.toFixed(2)}`;
|
|
86
|
+
|
|
87
|
+
/** The decision, in the order the record states the clauses: progress, the
|
|
88
|
+
* grant's count, the cap, the fit. `segment` is the one the renewal opens
|
|
89
|
+
* (the first segment is 1); `from` the sha it continues from when a push made
|
|
90
|
+
* the progress. */
|
|
91
|
+
export function renewalDecision(input: RenewalInput): RenewalDecision {
|
|
92
|
+
const renewalsLeft = Math.max(0, input.grant.renewals - input.renewalsSpent);
|
|
93
|
+
if (!input.progress.progressed) return { renew: false, why: "no_progress", detail: input.progress.why, renewalsLeft };
|
|
94
|
+
if (renewalsLeft === 0) {
|
|
95
|
+
const detail =
|
|
96
|
+
input.grant.renewals === 0
|
|
97
|
+
? "the grant holds no renewals"
|
|
98
|
+
: `the grant's ${input.grant.renewals} renewal${input.grant.renewals === 1 ? "" : "s"} are spent`;
|
|
99
|
+
return { renew: false, why: "grant_exhausted", detail, renewalsLeft };
|
|
100
|
+
}
|
|
101
|
+
const cap = input.grant.costCapUsd;
|
|
102
|
+
if (cap !== undefined) {
|
|
103
|
+
if (input.spendUsd === null)
|
|
104
|
+
return {
|
|
105
|
+
renew: false,
|
|
106
|
+
why: "cost_cap",
|
|
107
|
+
detail: `spend is unknown (a model had no price) under the grant's cap of $${cap}`,
|
|
108
|
+
renewalsLeft,
|
|
109
|
+
};
|
|
110
|
+
if (input.spendUsd >= cap)
|
|
111
|
+
return {
|
|
112
|
+
renew: false,
|
|
113
|
+
why: "cost_cap",
|
|
114
|
+
detail: `spend ${usd(input.spendUsd)} reached the grant's cap of $${cap}`,
|
|
115
|
+
renewalsLeft,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const held = fit(input.pipeline);
|
|
119
|
+
if (!held.ok)
|
|
120
|
+
return {
|
|
121
|
+
renew: false,
|
|
122
|
+
why: "unfit",
|
|
123
|
+
detail: `a ${held.have}-minute segment cannot hold the ship loop (${input.pipeline.maxRounds} review rounds need ${held.need} min)`,
|
|
124
|
+
renewalsLeft,
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
renew: true,
|
|
128
|
+
segment: input.renewalsSpent + 2,
|
|
129
|
+
...(input.progress.by === "push" ? { from: input.progress.sha } : {}),
|
|
130
|
+
renewalsLeft: renewalsLeft - 1,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The card's words (the record's trace, steps 4 and 9): a renewal names its
|
|
135
|
+
* number of the grant's and the sha it continues from; a stop names the
|
|
136
|
+
* clause and, when renewals remain, the reply that spends one by hand. */
|
|
137
|
+
export function renderRenewal(decision: RenewalDecision, grant: Grant): string {
|
|
138
|
+
if (decision.renew)
|
|
139
|
+
return `renewal ${decision.segment - 1} of ${grant.renewals}, continues ${decision.from !== undefined ? decision.from.slice(0, 7) : "the branch's head"}`;
|
|
140
|
+
const holds =
|
|
141
|
+
decision.renewalsLeft > 0
|
|
142
|
+
? `grant holds ${decision.renewalsLeft} renewal${decision.renewalsLeft === 1 ? "" : "s"}`
|
|
143
|
+
: "the grant holds no renewals";
|
|
144
|
+
switch (decision.why) {
|
|
145
|
+
case "no_progress":
|
|
146
|
+
return decision.renewalsLeft > 0
|
|
147
|
+
? `no progress in the last lease; ${holds}; reply continue to spend one`
|
|
148
|
+
: `no progress in the last lease; ${holds}`;
|
|
149
|
+
case "cost_cap":
|
|
150
|
+
return decision.renewalsLeft > 0 ? `${decision.detail}; ${holds} unspent` : decision.detail;
|
|
151
|
+
case "grant_exhausted":
|
|
152
|
+
case "unfit":
|
|
153
|
+
return decision.detail;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -34,6 +34,14 @@ export interface AttrDomain {
|
|
|
34
34
|
/** The `<provider>/<model>` that took the turn — a ship run's children answer
|
|
35
35
|
* on different models, and the run page badges the switch per step. */
|
|
36
36
|
model: string;
|
|
37
|
+
/** The tools a proxied call offered the model (model-proxy.md item 6): how
|
|
38
|
+
* many, their names sorted and comma-joined, and the request's `tool_choice`
|
|
39
|
+
* by its word — `auto` when tools came and nothing was said, `none` when no
|
|
40
|
+
* tools came or the request said so, `any` for OpenAI's `required`, `tool`
|
|
41
|
+
* for a named tool. */
|
|
42
|
+
tools: number;
|
|
43
|
+
toolNames: string;
|
|
44
|
+
toolChoice: "auto" | "none" | "any" | "tool";
|
|
37
45
|
stopReason: "end_turn" | "tool_use" | "max_tokens" | "stop_sequence" | "other";
|
|
38
46
|
inputTokens: number;
|
|
39
47
|
outputTokens: number;
|
|
@@ -159,6 +167,9 @@ const ATTR_TYPE: Record<SpanAttrKey, "string" | "number" | "boolean"> = {
|
|
|
159
167
|
backend: "string",
|
|
160
168
|
command: "string",
|
|
161
169
|
model: "string",
|
|
170
|
+
tools: "number",
|
|
171
|
+
toolNames: "string",
|
|
172
|
+
toolChoice: "string",
|
|
162
173
|
stopReason: "string",
|
|
163
174
|
inputTokens: "number",
|
|
164
175
|
outputTokens: "number",
|
|
@@ -70,9 +70,10 @@ export interface TraceOptions {
|
|
|
70
70
|
span?: Span;
|
|
71
71
|
/** Which door the call came through when it was not the surface's own
|
|
72
72
|
* grammar: `route` for a command the request router bound from prose
|
|
73
|
-
* (record 0036)
|
|
74
|
-
*
|
|
75
|
-
|
|
73
|
+
* (record 0036), `confirm` for a stored input run at a confirmation's
|
|
74
|
+
* click (record 0044). Copied onto the registry's audit line, never read
|
|
75
|
+
* by a handler. Absent for a typed command. */
|
|
76
|
+
source?: "route" | "confirm";
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
export interface Span {
|
|
@@ -2,28 +2,31 @@
|
|
|
2
2
|
// one set of bounds, shared by the tool layer, every executor, and both deploy
|
|
3
3
|
// Workers (which import from src/execution like shellQuote/residentDetach).
|
|
4
4
|
// Deliberately free of node: imports so wrangler can bundle it into Workers.
|
|
5
|
+
// The numbers are rows of src/core/budgets.ts (docs/decisions/0046); this file
|
|
6
|
+
// keeps their names for the readers that learned them here.
|
|
7
|
+
import { ALLOWANCES, BASH_COMMAND, MINUTE_MS } from "../core/budgets.js";
|
|
5
8
|
|
|
6
9
|
/** Default per-command budget when the caller passes no timeoutMs. */
|
|
7
|
-
export const BASH_TIMEOUT_MS =
|
|
10
|
+
export const BASH_TIMEOUT_MS = BASH_COMMAND.defaultMinutes * MINUTE_MS;
|
|
8
11
|
|
|
9
12
|
/** Floor: anything lower is a typo or an attack, not a budget. */
|
|
10
|
-
export const BASH_TIMEOUT_MIN_MS =
|
|
13
|
+
export const BASH_TIMEOUT_MIN_MS = BASH_COMMAND.minMs;
|
|
11
14
|
|
|
12
15
|
/** Hard ceiling a caller can raise the budget to. Not by itself a guarantee
|
|
13
16
|
* against one command eating a run — 20 minutes is 80% of a 25-minute review
|
|
14
17
|
* — which is what `bashBudgetWithinRun` below is for. */
|
|
15
|
-
export const BASH_TIMEOUT_MAX_MS =
|
|
18
|
+
export const BASH_TIMEOUT_MAX_MS = BASH_COMMAND.maxMinutes * MINUTE_MS;
|
|
16
19
|
|
|
17
20
|
/** Wall clock a run keeps back from its last command for the write-up: the
|
|
18
21
|
* runner's deadline forces a final answer, and a command still running at
|
|
19
22
|
* that moment would have been wasted anyway. */
|
|
20
|
-
export const RUN_DEADLINE_RESERVE_MS =
|
|
23
|
+
export const RUN_DEADLINE_RESERVE_MS = ALLOWANCES.commandWriteUp * MINUTE_MS;
|
|
21
24
|
|
|
22
25
|
/** Margin the remote exec clients add to their HTTP wait over the command
|
|
23
26
|
* budget, so the server's own timeout answer (a streamed exit 124) wins the
|
|
24
27
|
* race against the client's transport deadline instead of both firing at the
|
|
25
28
|
* same instant. */
|
|
26
|
-
export const EXEC_CALL_MARGIN_MS =
|
|
29
|
+
export const EXEC_CALL_MARGIN_MS = ALLOWANCES.execCall * MINUTE_MS;
|
|
27
30
|
|
|
28
31
|
/** The documented clamp rule, applied identically bot-side and server-side
|
|
29
32
|
* (a server never trusts the client's number): a finite number is truncated
|
|
@@ -186,6 +186,22 @@ export function killStaleBuildProcessesCommand(user: string, dir: string): strin
|
|
|
186
186
|
* failure however it died. */
|
|
187
187
|
const INTERRUPTION_SIGNATURE = /\bexit 143\b|Session terminated|SIGTERM|^restore-interrupted:/;
|
|
188
188
|
|
|
189
|
+
/** The wordings git gives when the MIRROR itself is broken: its `origin`
|
|
190
|
+
* remote gone from `config`, the repository not a repository, an object store
|
|
191
|
+
* with bad, corrupt or unreadable objects, a local file it cannot open. None
|
|
192
|
+
* of these is GitHub's doing, and none is cured by waiting for GitHub. */
|
|
193
|
+
const MIRROR_FAILURE_WORDING =
|
|
194
|
+
/'origin' does not appear to be a git repository|not a git repository|\bbad object\b|\bcorrupt\b|unable to read (?:tree|object|commit|blob)|object file .* is empty|cannot open \.git\/|unable to open object|missing object/i;
|
|
195
|
+
|
|
196
|
+
/** Whether a failed mirror fetch failed on the mirror (the resident's own
|
|
197
|
+
* disk — item 67 counts it) rather than on GitHub (`github-unreachable`, a
|
|
198
|
+
* serviceable reason that parks on repeat, item 16b). GitHub is the default:
|
|
199
|
+
* a message names the mirror only through the wordings above, so an unknown
|
|
200
|
+
* failure keeps the reading it always had. */
|
|
201
|
+
export function fetchFailureIsMirrors(message: string): boolean {
|
|
202
|
+
return MIRROR_FAILURE_WORDING.test(message);
|
|
203
|
+
}
|
|
204
|
+
|
|
189
205
|
/** Message wording of the Sandbox SDK's runtime-replacement error family — the
|
|
190
206
|
* container went away UNDER a live SDK call, so the failure never reaches the
|
|
191
207
|
* shell-kill signature above: a step that dies this way surfaces as
|
|
@@ -209,9 +225,34 @@ const INTERRUPTION_SIGNATURE = /\bexit 143\b|Session terminated|SIGTERM|^restore
|
|
|
209
225
|
* exec path and the refresh classifier agree on one wording list (a container
|
|
210
226
|
* stop mid-snapshot produces "Process supervisor is closed"; classified as the
|
|
211
227
|
* repo's own `snapshot-failed` it would end the instance `degraded` instead of
|
|
212
|
-
* being retried into the same step).
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
* being retried into the same step).
|
|
229
|
+
*
|
|
230
|
+
* The list is the union of two a reader tells apart, because the resident's
|
|
231
|
+
* `/exec` answer treats them differently (resident-repos item 43): the SDK's
|
|
232
|
+
* words that vouch the runtime MOVED — a new incarnation serves the thread,
|
|
233
|
+
* so the container the command was aimed at is gone whatever else is known
|
|
234
|
+
* — and the platform's words for a container that is merely DOWN, which say
|
|
235
|
+
* nothing about a replacement: a stopped container answers "Process
|
|
236
|
+
* supervisor is closed" to a spawn, and the workerd binding answers "The
|
|
237
|
+
* container is not running" whether the container is asleep, starting, or
|
|
238
|
+
* gone under a roll. For the refresh classifier and `isRuntimeReplacement`
|
|
239
|
+
* the union is right: a step interrupted either way is retried onto the
|
|
240
|
+
* container that comes back. */
|
|
241
|
+
export const RUNTIME_MOVED_WORDING =
|
|
242
|
+
/previous runtime incarnation|interrupted because the runtime changed|runtime identity is no longer active|sandbox lifetime is no longer current|platform was updating the sandbox runtime|no longer identifies pid/i;
|
|
243
|
+
/** The platform's words for a container that is down under a command (see
|
|
244
|
+
* `RUNTIME_MOVED_WORDING`): the spawn refusal of a stopped container and the
|
|
245
|
+
* binding's not-running refusal, anchored to the full phrase so a genuine
|
|
246
|
+
* crash ("container exited with unexpected exit code") and the readiness
|
|
247
|
+
* probe ("the container is not listening") never match. The harness's
|
|
248
|
+
* container seam reads the same two as a container down under its one more
|
|
249
|
+
* command — a wait, never a judgement (harness.md item 6). */
|
|
250
|
+
export const STOPPED_CONTAINER_WORDING =
|
|
251
|
+
/process supervisor is closed|container is not running, consider calling start/i;
|
|
252
|
+
export const RUNTIME_REPLACEMENT_WORDING = new RegExp(
|
|
253
|
+
`${RUNTIME_MOVED_WORDING.source}|${STOPPED_CONTAINER_WORDING.source}`,
|
|
254
|
+
"i",
|
|
255
|
+
);
|
|
215
256
|
|
|
216
257
|
/** `runtimeUnreachable` is the caller's word that the failure was the SDK's
|
|
217
258
|
* connect abort at the exec choke point (the Worker's typed check over the
|
|
@@ -314,10 +355,14 @@ export const WAKE_PORT_READY_MS = 3 * 60_000;
|
|
|
314
355
|
* SDK by a test. */
|
|
315
356
|
export const SDK_RUNTIME_RECORD_KEY = "currentRuntimeIdentity";
|
|
316
357
|
|
|
317
|
-
/** The DOMException the SDK's connect abort raises
|
|
318
|
-
* `
|
|
319
|
-
* own `
|
|
320
|
-
|
|
358
|
+
/** The DOMException the SDK's connect abort raises — `fetchUpgradeAttempt`
|
|
359
|
+
* calls `controller.abort()` with no reason when `DEFAULT_CONNECT_TIMEOUT_MS`
|
|
360
|
+
* passes, so the runtime's own `AbortError` with the message exactly `The
|
|
361
|
+
* operation was aborted`. Anchored on the WHOLE message, so a command's own
|
|
362
|
+
* `Aborted (core dumped)` never matches and neither does a `TimeoutError`'s
|
|
363
|
+
* `The operation was aborted due to timeout` — the text `AbortSignal.timeout`
|
|
364
|
+
* raises, a route's own bound on a slow GitHub, not a silent runtime. */
|
|
365
|
+
export const RUNTIME_UNREACHABLE_WORDING = /^The operation was aborted\.?$/;
|
|
321
366
|
|
|
322
367
|
/** Is this one link of an error's cause chain the SDK's connect abort? By
|
|
323
368
|
* name first (the DOMException's `AbortError`), by the DOMException's message
|
|
@@ -49,7 +49,7 @@ export const RESIDENT_STEP_LABELS = {
|
|
|
49
49
|
"worktree-clean": "cleaning the worktree",
|
|
50
50
|
"clean-workspace": "cleaning the workspace",
|
|
51
51
|
"clean-before-restore": "cleaning before the restore",
|
|
52
|
-
"break-mirror": "removing the mirror's
|
|
52
|
+
"break-mirror": "removing the mirror's config (fault injection)",
|
|
53
53
|
"unmount-restores": "unmounting earlier restores",
|
|
54
54
|
"mirror-restore-extract": "extracting the mirror snapshot",
|
|
55
55
|
"checkout-restore-extract": "extracting the checkout snapshot",
|
|
@@ -38,7 +38,12 @@ export function residentErrorText(text: string): string {
|
|
|
38
38
|
return redactAndCap(stripAnsi(text), RESIDENT_ERROR_CAP);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/** The free-text fields a resident body carries: `error` (the reply), `reason`
|
|
42
|
+
* (the answer's own word or a lifecycle reason), `stateReason` (the lifecycle
|
|
43
|
+
* reason beside `state` on a thread route's 503 — the same remote text
|
|
44
|
+
* `reason` used to carry there, item 62), `summary`. A field added to a
|
|
45
|
+
* resident answer that carries remote output is added HERE first. */
|
|
46
|
+
const SANITIZED_FIELDS = ["error", "reason", "stateReason", "summary"] as const;
|
|
42
47
|
|
|
43
48
|
/** How deep the sanitizer descends. The deepest resident shape today is
|
|
44
49
|
* `/residents` → `residents[]` → `live` → `reason` (depth 3); the bound exists
|
|
@@ -48,8 +53,8 @@ const SANITIZE_DEPTH = 4;
|
|
|
48
53
|
/** A parsed resident body with its free-text fields made safe, at every level:
|
|
49
54
|
* `/residents` nests each resident's `state`/`reason` (or an `error`) under
|
|
50
55
|
* `residents[].live`, and `repo list` renders those. In each plain object only
|
|
51
|
-
* `error`, `reason` and `summary` are touched (when strings) —
|
|
52
|
-
* reply-sized cap, the
|
|
56
|
+
* `error`, `reason`, `stateReason` and `summary` are touched (when strings) —
|
|
57
|
+
* `error` at the reply-sized cap, the others at the card-sized one; `stderr` is
|
|
53
58
|
* rewritten only when it mirrors `error` (the thread routes' failure shape
|
|
54
59
|
* copies the error into stderr). Every other field — `needs`, `state`,
|
|
55
60
|
* `stdout`, `status`, bindings, numbers — passes through untouched. Arrays are
|
|
@@ -238,6 +238,9 @@ export interface McpServerView {
|
|
|
238
238
|
promotedFromName?: string;
|
|
239
239
|
/** A user row's owner (the id after `user:` in `scopeKey`) as a display name, when the lookup answered. */
|
|
240
240
|
ownerName?: string;
|
|
241
|
+
/** A channel row's channel (the id after `channel:` in `scopeKey`) as its name without the hash,
|
|
242
|
+
* when the lookup answered. */
|
|
243
|
+
channelName?: string;
|
|
241
244
|
/** A higher tier holds this name, so runs use that entry and this one is idle (`mcpServersFor`'s
|
|
242
245
|
* `shadowedBy`, the winning scope key — `org`, or `channel:<id>`); absent when this entry serves. */
|
|
243
246
|
shadowedBy?: string;
|