@coreplane/switchboard 1.250.0 → 1.252.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 +13 -2
- package/dist/assets/deploy/cloudflare/preflight.mjs +19 -21
- package/dist/assets/deploy/cloudflare/worker.ts +6 -3
- package/dist/assets/deploy/cloudflare-memory/worker.ts +108 -12
- package/dist/assets/deploy/cloudflare-resident/memoryGuard.ts +212 -0
- package/dist/assets/deploy/cloudflare-resident/refresh.ts +1 -1
- package/dist/assets/deploy/cloudflare-resident/worker.ts +317 -56
- package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +4 -2
- package/dist/assets/deploy/cloudflare-sandbox/runtime-supervisor.d.mts +31 -0
- package/dist/assets/deploy/cloudflare-sandbox/runtime-supervisor.mjs +119 -0
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +3 -2
- package/dist/assets/project.json +13 -9
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +5 -5
- package/dist/assets/src/core/authz/policy.ts +4 -0
- package/dist/assets/src/core/authz/resource.ts +6 -2
- package/dist/assets/src/core/authz/types.ts +2 -0
- package/dist/assets/src/core/budgets.ts +22 -0
- package/dist/assets/src/core/coordinator/contract.ts +42 -0
- package/dist/assets/src/core/coordinator/driver.ts +150 -12
- package/dist/assets/src/core/drain.ts +50 -0
- package/dist/assets/src/core/memory/engine.ts +98 -0
- package/dist/assets/src/core/memory/scorer.ts +12 -4
- package/dist/assets/src/core/memory/types.ts +69 -12
- package/dist/assets/src/core/modelCard.ts +51 -7
- package/dist/assets/src/core/modelPricing.ts +111 -1
- package/dist/assets/src/core/modelProxy/usage.ts +88 -0
- package/dist/assets/src/core/modelRegistry.ts +15 -1
- package/dist/assets/src/core/provider.ts +49 -0
- package/dist/assets/src/core/refusal.ts +6 -6
- package/dist/assets/src/core/reviewVerdict.ts +4 -0
- package/dist/assets/src/core/runEvents.ts +51 -2
- package/dist/assets/src/core/runFriction.ts +7 -2
- package/dist/assets/src/core/runLedger/types.ts +23 -4
- package/dist/assets/src/core/runRecord.ts +16 -0
- package/dist/assets/src/core/runUsage.ts +67 -13
- package/dist/assets/src/core/schedules.ts +3 -0
- package/dist/assets/src/core/ship/contract.ts +45 -5
- package/dist/assets/src/core/ship/coordinator.ts +441 -48
- package/dist/assets/src/core/ship/renewal.ts +10 -5
- package/dist/assets/src/core/trace/attrs.ts +24 -0
- package/dist/assets/src/core/types.ts +5 -5
- package/dist/assets/src/core/verbosity.ts +48 -0
- package/dist/assets/src/deploy/liveGate.ts +40 -13
- package/dist/assets/src/deploy/restart.ts +11 -12
- package/dist/assets/src/execution/residentDepCache.ts +50 -1
- package/dist/assets/src/execution/residentDepsStore.ts +40 -2
- package/dist/assets/src/execution/residentRefresh.ts +55 -3
- package/dist/assets/src/execution/residentSteps.ts +4 -0
- package/dist/assets/src/execution/sandboxErrors.ts +8 -0
- package/dist/assets/web/dist/.vite/manifest.json +58 -52
- package/dist/assets/web/dist/assets/DeliveryPage-3ELQWM0r.js +1 -0
- package/dist/assets/web/dist/assets/{HomePage-AnycA57D.js → HomePage-BG_ok-K2.js} +2 -2
- package/dist/assets/web/dist/assets/{PendingTurnRow-BuRre8it.js → PendingTurnRow-ChCQOLgZ.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentDetailPage-Cb3sFkfj.js → ResidentDetailPage-C9y3nbo8.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-BZ6n6UxF.js → ResidentsIndexPage-i1RG9e7g.js} +1 -1
- package/dist/assets/web/dist/assets/RunFoldRow-D3wVpzBa.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-B3IirUVi.js +9 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-DiFmtGaJ.js +1 -0
- package/dist/assets/web/dist/assets/{ScheduledPage-CBUxbeqN.js → ScheduledPage-DvYwM2TE.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingsPage-CBTnZ9Qv.js → SettingsPage-Bo6yCyXZ.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-BnRjWzFN.js → StatusDot-CAfS1AUi.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-Brge0wnd.js → Tooltip-tZoum_T-.js} +1 -1
- package/dist/assets/web/dist/assets/{UnitRoutePage-o6sLju16.js → UnitRoutePage-BmdOHwNn.js} +1 -1
- package/dist/assets/web/dist/assets/budgets-CbIyPAER.js +1 -0
- package/dist/assets/web/dist/assets/{dist-rgAhsmE-.js → dist-DfbEpHXR.js} +1 -1
- package/dist/assets/web/dist/assets/indexRow-BT0cPVRw.js +1 -0
- package/dist/assets/web/dist/assets/{main-CeRuGONy.js → main-5Gm_1Gv8.js} +2 -2
- package/dist/assets/web/dist/assets/sseReplay-DmyMXfRC.js +11 -0
- package/dist/cli.js +3319 -1015
- package/package.json +1 -1
- package/dist/assets/deploy/cloudflare-sandbox/runtime-supervisor.sh +0 -68
- package/dist/assets/web/dist/assets/DeliveryPage-CIfBiINK.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-BRXkjkgO.js +0 -1
- package/dist/assets/web/dist/assets/RunRoutePage-psSMI3fN.js +0 -9
- package/dist/assets/web/dist/assets/RunsIndexPage-68YT_RWt.js +0 -1
- package/dist/assets/web/dist/assets/indexRow-BmK74Vp1.js +0 -1
- package/dist/assets/web/dist/assets/sseReplay-DXC7kGbN.js +0 -9
|
@@ -23,6 +23,17 @@ export interface ModelUsage {
|
|
|
23
23
|
outputTokens: number;
|
|
24
24
|
cacheReadTokens: number;
|
|
25
25
|
cacheWriteTokens: number;
|
|
26
|
+
/** The model's dollars summed from its turns' own `usd` attrs (the proxy's
|
|
27
|
+
* meter row, model-proxy item 6): a number when every turn that counted
|
|
28
|
+
* tokens carried one, null when such a turn lacked it (unpriced — a sum
|
|
29
|
+
* that left counted tokens out would understate the model; a turn that
|
|
30
|
+
* counted none, an errored call or a retry, leaves nothing out), absent on
|
|
31
|
+
* a record whose turns predate the meter row (the price table then prices
|
|
32
|
+
* the tokens, costs.md item 4b). */
|
|
33
|
+
usd?: number | null;
|
|
34
|
+
/** The distinct `priceSource` words the turns carried, sorted — what the
|
|
35
|
+
* by-model view names as the source. Absent when no turn carried one. */
|
|
36
|
+
priceSources?: string[];
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
export interface RunUsage {
|
|
@@ -46,6 +57,14 @@ export const emptyUsage = (): RunUsage => ({ turns: 0, byModel: {} });
|
|
|
46
57
|
/** The run's usage from its events: one `model.turn` span end per provider call. */
|
|
47
58
|
export function usageOfEvents(events: readonly RunEvent[]): RunUsage {
|
|
48
59
|
const usage = emptyUsage();
|
|
60
|
+
// The meter row per model (model-proxy item 6): the turns' own dollars and
|
|
61
|
+
// sources. `usd` sums only when every turn that counted tokens carried one —
|
|
62
|
+
// a turn that counted none (an upstream error, a retry the harness's SDK
|
|
63
|
+
// spent, a stream broken before its usage) has nothing a sum could leave
|
|
64
|
+
// out, so it never turns the model unpriced; a model none of whose turns
|
|
65
|
+
// carried price attrs stays without the fields (a record from before the
|
|
66
|
+
// meter row — the table prices it, costs.md item 4b).
|
|
67
|
+
const priced = new Map<string, { usd: number; pricedTurns: number; tokenTurns: number; sources: Set<string> }>();
|
|
49
68
|
for (const e of events) {
|
|
50
69
|
if (e.type !== "span_end" || e.name !== MODEL_TURN) continue;
|
|
51
70
|
const attrs = (e.attrs ?? {}) as Record<string, unknown>;
|
|
@@ -57,6 +76,8 @@ export function usageOfEvents(events: readonly RunEvent[]): RunUsage {
|
|
|
57
76
|
cacheReadTokens: 0,
|
|
58
77
|
cacheWriteTokens: 0,
|
|
59
78
|
};
|
|
79
|
+
const counted =
|
|
80
|
+
num(attrs.inputTokens) + num(attrs.outputTokens) + num(attrs.cacheReadTokens) + num(attrs.cacheWriteTokens);
|
|
60
81
|
m.turns += 1;
|
|
61
82
|
m.inputTokens += num(attrs.inputTokens);
|
|
62
83
|
m.outputTokens += num(attrs.outputTokens);
|
|
@@ -64,38 +85,71 @@ export function usageOfEvents(events: readonly RunEvent[]): RunUsage {
|
|
|
64
85
|
m.cacheWriteTokens += num(attrs.cacheWriteTokens);
|
|
65
86
|
usage.byModel[model] = m;
|
|
66
87
|
usage.turns += 1;
|
|
88
|
+
const hasUsd = typeof attrs.usd === "number" && Number.isFinite(attrs.usd);
|
|
89
|
+
const hasSource = typeof attrs.priceSource === "string" && attrs.priceSource !== "";
|
|
90
|
+
const p = priced.get(model) ?? { usd: 0, pricedTurns: 0, tokenTurns: 0, sources: new Set<string>() };
|
|
91
|
+
if (counted > 0) p.tokenTurns += 1;
|
|
92
|
+
if (hasUsd) {
|
|
93
|
+
p.usd += attrs.usd as number;
|
|
94
|
+
p.pricedTurns += 1;
|
|
95
|
+
}
|
|
96
|
+
if (hasSource) p.sources.add(attrs.priceSource as string);
|
|
97
|
+
priced.set(model, p);
|
|
98
|
+
}
|
|
99
|
+
for (const [model, p] of priced) {
|
|
100
|
+
if (p.pricedTurns === 0 && p.sources.size === 0) continue;
|
|
101
|
+
const m = usage.byModel[model]!;
|
|
102
|
+
m.usd = p.pricedTurns >= p.tokenTurns ? p.usd : null;
|
|
103
|
+
if (p.sources.size > 0) m.priceSources = [...p.sources].sort();
|
|
67
104
|
}
|
|
68
105
|
return usage;
|
|
69
106
|
}
|
|
70
107
|
|
|
108
|
+
/** The two sides' `usd` folded: a sum when both carry one, null when either
|
|
109
|
+
* has an unpriced turn, absent when either predates the meter row (the whole
|
|
110
|
+
* cell then prices from the table, never half a figure). */
|
|
111
|
+
const foldUsd = (a: ModelUsage["usd"], b: ModelUsage["usd"]): ModelUsage["usd"] =>
|
|
112
|
+
a === undefined || b === undefined ? undefined : a === null || b === null ? null : a + b;
|
|
113
|
+
|
|
71
114
|
export function addUsage(a: RunUsage, b: RunUsage): RunUsage {
|
|
72
115
|
const out: RunUsage = { turns: a.turns + b.turns, byModel: {} };
|
|
73
116
|
for (const src of [a.byModel, b.byModel]) {
|
|
74
117
|
for (const [model, m] of Object.entries(src)) {
|
|
75
|
-
const acc = out.byModel[model]
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
cacheWriteTokens: 0,
|
|
81
|
-
};
|
|
118
|
+
const acc = out.byModel[model];
|
|
119
|
+
if (!acc) {
|
|
120
|
+
out.byModel[model] = { ...m, ...(m.priceSources ? { priceSources: [...m.priceSources] } : {}) };
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
82
123
|
acc.turns += m.turns;
|
|
83
124
|
acc.inputTokens += m.inputTokens;
|
|
84
125
|
acc.outputTokens += m.outputTokens;
|
|
85
126
|
acc.cacheReadTokens += m.cacheReadTokens;
|
|
86
127
|
acc.cacheWriteTokens += m.cacheWriteTokens;
|
|
87
|
-
|
|
128
|
+
const usd = foldUsd(acc.usd, m.usd);
|
|
129
|
+
if (usd === undefined) delete acc.usd;
|
|
130
|
+
else acc.usd = usd;
|
|
131
|
+
const sources = new Set([...(acc.priceSources ?? []), ...(m.priceSources ?? [])]);
|
|
132
|
+
if (sources.size > 0) acc.priceSources = [...sources].sort();
|
|
88
133
|
}
|
|
89
134
|
}
|
|
90
135
|
return out;
|
|
91
136
|
}
|
|
92
137
|
|
|
93
|
-
const isModelUsage = (v: unknown): v is ModelUsage =>
|
|
94
|
-
typeof v
|
|
95
|
-
v
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
const isModelUsage = (v: unknown): v is ModelUsage => {
|
|
139
|
+
if (typeof v !== "object" || v === null) return false;
|
|
140
|
+
const m = v as Record<string, unknown>;
|
|
141
|
+
if (
|
|
142
|
+
!["turns", "inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"].every(
|
|
143
|
+
(k) => typeof m[k] === "number",
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
return false;
|
|
147
|
+
if (m.usd !== undefined && m.usd !== null && typeof m.usd !== "number") return false;
|
|
148
|
+
return (
|
|
149
|
+
m.priceSources === undefined ||
|
|
150
|
+
(Array.isArray(m.priceSources) && m.priceSources.every((s) => typeof s === "string"))
|
|
98
151
|
);
|
|
152
|
+
};
|
|
99
153
|
|
|
100
154
|
export function isRunUsage(v: unknown): v is RunUsage {
|
|
101
155
|
if (typeof v !== "object" || v === null) return false;
|
|
@@ -379,6 +379,9 @@ export interface WatchdogSummary {
|
|
|
379
379
|
action?: unknown;
|
|
380
380
|
error?: string;
|
|
381
381
|
disk?: unknown;
|
|
382
|
+
/** The resident's last memory reading (`{usedBytes, capBytes, percent, …}`,
|
|
383
|
+
* resident-repos.md item 70) when it has one. */
|
|
384
|
+
memory?: unknown;
|
|
382
385
|
/** What the pass did about the resident's refresh Workflow instance: `{id, action, why}`. */
|
|
383
386
|
instance?: unknown;
|
|
384
387
|
}>;
|
|
@@ -439,17 +439,57 @@ export const TIMEOUT_ON_LONG_COMMANDS =
|
|
|
439
439
|
"State a timeout on any command you expect to run longer than a minute: a timeout that reaches past the loop's " +
|
|
440
440
|
"end is refused before the command runs, never cut midway.";
|
|
441
441
|
|
|
442
|
+
/** The fast gates a plan child runs before every push, named one by one and
|
|
443
|
+
* each scoped to the changed set (agent-ship item 13; agent-coding item 9).
|
|
444
|
+
* "Its cheapest proving checks" left the choice to the child, and children
|
|
445
|
+
* chose wrong in both directions: prettier was reported clean while
|
|
446
|
+
* `format:check` was red, hygiene imprints reached CI that `hygiene:check`
|
|
447
|
+
* would have caught locally — and children ran the whole suite and the whole
|
|
448
|
+
* typecheck on the shared resident, minutes each call, time-sliced against
|
|
449
|
+
* every other run. So the gates are the changed-set forms, the full runs are
|
|
450
|
+
* said to be CI's alone in the same breath, and each gate is a receipt — the
|
|
451
|
+
* exit line goes into the PR description's validation table as the row's proof
|
|
452
|
+
* (the handoff has no verified list; parseHandoff carries deviations, followUps,
|
|
453
|
+
* unproven and landed), and a gate the child could not run goes under the
|
|
454
|
+
* handoff's unproven list, never claimed clean. The tests compare against the
|
|
455
|
+
* pull request's own base — `origin/<base>` — which the render substitutes
|
|
456
|
+
* from the rebase's `onto` when the contract knows it. */
|
|
457
|
+
/** The changed-set test command as the contract renders it before the base is
|
|
458
|
+
* known; the render substitutes the unit's base for `<base>` (`renderFirstInstruction`). */
|
|
459
|
+
export const CHANGED_SET_TEST_PLACEHOLDER = "`npx vitest run --changed origin/<base>`";
|
|
460
|
+
|
|
461
|
+
export const FAST_GATES_BEFORE_PUSH =
|
|
462
|
+
"The fast gates, before every push — each scoped to the changed set, never the whole project: " +
|
|
463
|
+
`${CHANGED_SET_TEST_PLACEHOLDER} (the pull request's base; or the touched test files), ` +
|
|
464
|
+
"`tsc --noEmit -p` the touched tsconfig under `NODE_OPTIONS=--max-old-space-size=6144`, " +
|
|
465
|
+
"`npx prettier --check` on the changed files, `npm run hygiene:check` and `npm run specs:check` — " +
|
|
466
|
+
"then your judgement on what else this change needs, not a longer checklist. Every CI pipeline runs the " +
|
|
467
|
+
"tests, the types, the formatting and the full verification on your push, so you never run them again: " +
|
|
468
|
+
"you validate and fix your own change before pushing, at the changed-set scope. Passing the full test suite " +
|
|
469
|
+
"and the full typecheck is NOT part of your criteria: CI is that gate and the only place they run — on a " +
|
|
470
|
+
"shared resident they cost minutes that every other run pays for. " +
|
|
471
|
+
"Paste each command's exit line into the PR description's validation table as the row's proof; a gate you " +
|
|
472
|
+
"could not run goes under the handoff's unproven list and is never claimed clean.";
|
|
473
|
+
|
|
442
474
|
function renderFirstInstruction(rebase: ChildContract["rebase"]): string {
|
|
443
475
|
const branch = rebase.branch ? `\`${rebase.branch}\`` : "the unit's branch";
|
|
444
476
|
const onto = rebase.onto ? `\`${rebase.onto}\`` : "the merged parent";
|
|
477
|
+
// the changed-set test run compares against the unit's own base, which the
|
|
478
|
+
// contract knows as the rebase target; unknown, the placeholder stands
|
|
479
|
+
// split/join, never String.replace: a `$` in a branch name is literal text here
|
|
480
|
+
const gates = rebase.onto
|
|
481
|
+
? FAST_GATES_BEFORE_PUSH.split(`${CHANGED_SET_TEST_PLACEHOLDER} (the pull request's base; or`).join(
|
|
482
|
+
`\`npx vitest run --changed origin/${rebase.onto}\` (or`,
|
|
483
|
+
)
|
|
484
|
+
: FAST_GATES_BEFORE_PUSH;
|
|
445
485
|
return (
|
|
446
486
|
`Rebase ${branch} onto ${onto} before any other work — the parent unit has merged and the base has moved; ` +
|
|
447
487
|
`the only writes are your own on that branch. A conflict ends the unit: report it as the handoff and stop. ` +
|
|
448
|
-
`Push the branch as soon as the change exists and
|
|
449
|
-
`
|
|
450
|
-
`survive the run's end. Right before each push, fetch ${onto} again and rebase
|
|
451
|
-
`you worked, so the pull request is not born conflicting. At the wind-down note,
|
|
452
|
-
`say what does not, then answer. ${TIMEOUT_ON_LONG_COMMANDS}`
|
|
488
|
+
`Push the branch as soon as the change exists and the fast gates pass — the project's full verification ` +
|
|
489
|
+
`is CI's gate, run there after the push with any fix as a further commit; an unpushed tree does not ` +
|
|
490
|
+
`survive the run's end. ${gates} Right before each push, fetch ${onto} again and rebase ` +
|
|
491
|
+
`once more if it moved while you worked, so the pull request is not born conflicting. At the wind-down note, ` +
|
|
492
|
+
`commit and push what compiles, say what does not, then answer. ${TIMEOUT_ON_LONG_COMMANDS}`
|
|
453
493
|
);
|
|
454
494
|
}
|
|
455
495
|
|