@coreplane/switchboard 1.251.0 → 1.253.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 +28 -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/runMetricsSink.ts +31 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +223 -38
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +12 -0
- 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/deploy/profile.example.json +1 -1
- 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 +8 -0
- package/dist/assets/src/core/budgets.ts +39 -0
- package/dist/assets/src/core/coordinator/contract.ts +93 -1
- package/dist/assets/src/core/coordinator/driver.ts +236 -21
- package/dist/assets/src/core/drain.ts +50 -0
- package/dist/assets/src/core/memory/engine.ts +123 -4
- package/dist/assets/src/core/memory/scorer.ts +12 -4
- package/dist/assets/src/core/memory/types.ts +73 -12
- package/dist/assets/src/core/modelCard.ts +32 -4
- 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/refusal.ts +4 -7
- package/dist/assets/src/core/reviewVerdict.ts +4 -0
- package/dist/assets/src/core/runEvents.ts +82 -3
- package/dist/assets/src/core/runFriction.ts +8 -2
- package/dist/assets/src/core/runLedger/types.ts +11 -0
- package/dist/assets/src/core/runMetrics.ts +237 -0
- package/dist/assets/src/core/runRecord.ts +46 -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 +34 -14
- package/dist/assets/src/core/ship/coordinator.ts +485 -55
- 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/profile.ts +16 -0
- 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 +444 -430
- package/dist/assets/web/dist/assets/{AppShell-DAtHiuI6.js → AppShell-Bmz8nzz3.js} +1 -1
- package/dist/assets/web/dist/assets/{CostsPage-BoKzYa4B.js → CostsPage-CxTKdpzU.js} +1 -1
- package/dist/assets/web/dist/assets/{DeliveryPage-DUXd-Sl-.js → DeliveryPage-C7jaEJip.js} +1 -1
- package/dist/assets/web/dist/assets/HomePage-cWbupQzy.js +3 -0
- package/dist/assets/web/dist/assets/{InputMenu-C6aPpe30.js → InputMenu-C9E7YoII.js} +1 -1
- package/dist/assets/web/dist/assets/{NotFoundPage-DtE-GgTk.js → NotFoundPage-4nlrq7DP.js} +1 -1
- package/dist/assets/web/dist/assets/{PendingTurnRow-DDhMhrI7.js → PendingTurnRow-C-pUgyyX.js} +1 -1
- package/dist/assets/web/dist/assets/PlanePage-BNy4tSgJ.js +1 -0
- package/dist/assets/web/dist/assets/{ResidentDetailPage-BnEoOnGQ.js → ResidentDetailPage-Dbtiq6_J.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-Dxpgf-l-.js → ResidentsIndexPage-CKtz9H1o.js} +1 -1
- package/dist/assets/web/dist/assets/RunFoldRow-sYFtuK2G.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-Clh68nw9.js +9 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-DPDbcOA_.js +1 -0
- package/dist/assets/web/dist/assets/{RunsTabs-YSbUu5py.js → RunsTabs-DotpuCSP.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-B_GgeJrb.js → ScheduledPage-C8FYoZy5.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingSelect-BIzAsLk1.js → SettingSelect-CSiBQCSX.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingsPage-BXX4R113.js → SettingsPage-CkYIhLUl.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-BOaw8le9.js → StatusDot-C0TUgnC1.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-DYZZ4l4V.js → Tooltip-CN5cZdl3.js} +1 -1
- package/dist/assets/web/dist/assets/{UnitRoutePage-BaSW5Odq.js → UnitRoutePage-D_EtAl8r.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-html-BjeQZdCq.js → angular-html-qW40ZL9x.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-ZGdORrL2.js → angular-ts-PUtZU8Cq.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-DhvV_X93.js → apl-D3fC7Qjd.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-DiRSE4Ug.js → astro-Bi3JFHhh.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-D8Fr5Wvq.js → blade-CSpsf9Z9.js} +1 -1
- package/dist/assets/web/dist/assets/budgets-CbIyPAER.js +1 -0
- package/dist/assets/web/dist/assets/{c-BwbC64D4.js → c-DE_b1oWP.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-DgmqRK97.js → chapel-CWXd9giL.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-DJod2RNK.js → cobol-CnMOmWNF.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-D1Y2CgBY.js → coffee-B9WvURu8.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-CsO97YOM.js → cpp-BZAJXlWI.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-CJ7m2tOc.js → crystal-Bm48wAlb.js} +1 -1
- package/dist/assets/web/dist/assets/{css-B2M-NKoj.js → css-WtMeC3ab.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-BCVXeBJ9.js → dist-B1Mf2JbM.js} +2 -2
- package/dist/assets/web/dist/assets/{edge-CFVW-m9B.js → edge-BmwNa6Ss.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-B2mtRgCr.js → elixir-BotfP-Mn.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-2teVAwCi.js → elm-DAFc6laN.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-LAbeNqen.js → erb-QkOON3bB.js} +1 -1
- package/dist/assets/web/dist/assets/{git-rebase-DnhzH_sp.js → git-rebase-BPs-dd73.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-DPKjXf1-.js → glimmer-js-D8eooepk.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-CABXnt5z.js → glimmer-ts-B0bRiDVg.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-Dz4-3-gJ.js → glsl-Bv6bMe1n.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-ByWUD1DL.js → graphql-Bh9y-n9o.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-CdmOK2K-.js → hack-DSEbn7Ir.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-DzegMpd2.js → haml-0NW1-z1f.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-B5NeiP0e.js → handlebars-BfgCHFbK.js} +1 -1
- package/dist/assets/web/dist/assets/{html-Zg_NHLv9.js → html-CQHc0gQ5.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-DZf9JNzh.js → html-derivative-B0Tm7wLP.js} +1 -1
- package/dist/assets/web/dist/assets/{http-CVFrQZeN.js → http-GAuU3aOF.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-L5brWAJe.js → hurl-CrDFbF5B.js} +1 -1
- package/dist/assets/web/dist/assets/indexRow-BxKswdlK.js +1 -0
- package/dist/assets/web/dist/assets/{java-hFphyZ4R.js → java-DYv5KhUe.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-CAJO1WKX.js → javascript-32SiLGg0.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-N4A4srXI.js → jinja--gJDUGYa.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-D3BZa_iU.js → jison-BlKLGzZH.js} +1 -1
- package/dist/assets/web/dist/assets/{json-DxD1Qh8W.js → json-BICanD1N.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-CHhag69S.js → jsx-CgW3-uFT.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-CrhkZ6Tt.js → julia-BErRpwwL.js} +1 -1
- package/dist/assets/web/dist/assets/{just-D0LFOuhq.js → just-CordHv0h.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-BHAlwCXK.js → latex-D7LVPLbi.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-CkaLfCWh.js → liquid-3ehz2i4U.js} +1 -1
- package/dist/assets/web/dist/assets/{lua-B1h4aAzP.js → lua-C9ppSVrm.js} +1 -1
- package/dist/assets/web/dist/assets/main-Cv0q9y56.js +28 -0
- package/dist/assets/web/dist/assets/main-DKFCM-sy.css +1 -0
- package/dist/assets/web/dist/assets/{marko-D6CG4hvX.js → marko-xBZLUBEJ.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-CVQzimAU.js → mdc-BJxGoVgD.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-h-9Ir54h.js → nginx-B-sZmpsb.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-DHVI30sp.js → nim--s9wRABK.js} +1 -1
- package/dist/assets/web/dist/assets/{org-AasAXfMF.js → org-DoUvssoI.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-BVi5aTEy.js → perl-B-vGHKOy.js} +1 -1
- package/dist/assets/web/dist/assets/{php-qOkCeUKr.js → php-CerWx_Xb.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-8TnUGvEX.js → pug-CzKrBMb1.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-CpJbhoxn.js → qml-Bs8L8I6k.js} +1 -1
- package/dist/assets/web/dist/assets/{r-JkUNcAJW.js → r-w6uG6mob.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-CtU4ws5a.js → razor-dz2oj2bd.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-J2zA6ceN.js → regexp-CfQdprnY.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-CMCvf-yZ.js → rst-DAOaX2tb.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-ChLBY_iR.js → ruby-QcCe4udB.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-cwn4x2VY.js → sas-BrM4lAAc.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-jSrJlzLI.js → scss-DVbJXoUb.js} +1 -1
- package/dist/assets/web/dist/assets/{shellscript-D5nAd0gs.js → shellscript-09fP2hZf.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-BZY4-Tdh.js → shellsession-Qgr8xjZF.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-COppPOKr.js → soy-DIYeNTuz.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-Blc_6dOA.js → sql-CzLR9msV.js} +1 -1
- package/dist/assets/web/dist/assets/sseReplay-BYo2EZKy.js +11 -0
- package/dist/assets/web/dist/assets/{stata-DRv3OY-h.js → stata-zCY1ZzH0.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-qMv_eeJ6.js → surrealql-CJIjTRV4.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-d1Edb6Rz.js → svelte-C5OTPFZg.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-CUMSIOXG.js → templ-DYj-UICF.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-BGZQA-Ei.js → tex-xcie-3Gd.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-Z9ulxVsq.js → ts-tags-CoN86VTt.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-oI4bI3Vf.js → tsx-n3-No_Br.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-D_RXwBIX.js → twig-DPiJ7Vp1.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-BgqQDYoN.js → typescript-BP6HBaOp.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-5MW-KRDd.js → typst-Do9ZjsVP.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-CZhHHsPm.js → vue-CzAeXNRY.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-DlYy2d8v.js → vue-html-D7iYmYjd.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-CJDnx-qn.js → vue-vine-DtrMRsJx.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-BRBDk65Q.js → xml-DGJHo1c0.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-BlCykPxo.js → xsl-Q00kvNgW.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-BqW48szf.js → yaml-F-UGKaiO.js} +1 -1
- package/dist/cli.js +5623 -2455
- package/package.json +1 -1
- package/dist/assets/deploy/cloudflare-sandbox/runtime-supervisor.sh +0 -68
- package/dist/assets/web/dist/assets/HomePage-mSiqEEcN.js +0 -2
- package/dist/assets/web/dist/assets/RunFoldRow-CSo4-vld.js +0 -1
- package/dist/assets/web/dist/assets/RunRoutePage-9klVWhSF.js +0 -9
- package/dist/assets/web/dist/assets/RunsIndexPage-BplMIgaw.js +0 -1
- package/dist/assets/web/dist/assets/budgets-BvWYKPsY.js +0 -1
- package/dist/assets/web/dist/assets/indexRow-Bde9OZxG.js +0 -1
- package/dist/assets/web/dist/assets/main-Dkcbtu3u.js +0 -28
- package/dist/assets/web/dist/assets/main-zbP_dTjR.css +0 -1
- package/dist/assets/web/dist/assets/sseReplay-DPwdsaok.js +0 -9
|
@@ -167,6 +167,12 @@ defaults:
|
|
|
167
167
|
# verbosity: verbose # hear every acknowledgement of what the bot does for you
|
|
168
168
|
# harness:
|
|
169
169
|
# coding: opencode
|
|
170
|
+
# # The person's GitHub author binding (record 0062; authorization.md item 18):
|
|
171
|
+
# # the login their commits are authored as. A bare login here is resolved to
|
|
172
|
+
# # { login, id } once at first use; `config set user --user <id> --github
|
|
173
|
+
# # <login>` (identity:write holders) stores the pair. Never set by the person
|
|
174
|
+
# # themselves; `config clear me` keeps it. One login/id binds one person.
|
|
175
|
+
# github: octocat
|
|
170
176
|
|
|
171
177
|
# Authorization (docs/reference/specs/authorization.md item 9, docs/reference/authorization.md):
|
|
172
178
|
# two blocks. `restrict` names what is CLOSED unless granted — an agent listed
|
|
@@ -325,8 +331,10 @@ workspaceDir: ./workspaces
|
|
|
325
331
|
# # and — when the run is bound to a repo — that repo's scope (repo:owner/name)
|
|
326
332
|
# # plus the requesting user's own scope (user:slack:U…) — a
|
|
327
333
|
# # person's records never surface for anyone else.
|
|
328
|
-
# limit:
|
|
329
|
-
# maxTokens:
|
|
334
|
+
# limit: 32 # max records retrieved/injected per request (default 32)
|
|
335
|
+
# maxTokens: 3000 # hard token budget for the injected block (default 3000)
|
|
336
|
+
# repoWindow: 24 # newest repo facts injected ahead of the keyword hits when
|
|
337
|
+
# # the run is bound to a repo (default 24; 0 disables the window)
|
|
330
338
|
# maxRecordsPerScope: 500
|
|
331
339
|
# # per-scope cap on ACTIVE records (default 500): a write
|
|
332
340
|
# # that would exceed it evicts the least recently used
|
|
@@ -386,6 +394,8 @@ workspaceDir: ./workspaces
|
|
|
386
394
|
# ship:
|
|
387
395
|
# maxRounds: 3 # review rounds per unit
|
|
388
396
|
# maxMinutes: 240 # the ship preset's wall-clock budget in minutes (the registry's default)
|
|
397
|
+
# idleDays: 0 # days a ship unit idles instead of ending (record 0051): 0–365, 0 = today's
|
|
398
|
+
# # endings; also per channel/user (`channels.<id>.ship.idleDays`), user > channel > org
|
|
389
399
|
|
|
390
400
|
# Which harness each preset's runs are driven by, deployment-wide
|
|
391
401
|
# (docs/reference/specs/harness.md item 8): a mapping of preset to a harness's
|
|
@@ -452,10 +462,16 @@ workspaceDir: ./workspaces
|
|
|
452
462
|
# `tool` (default) forces a call to the router's `route` tool, whose schema is
|
|
453
463
|
# the answer, so prose cannot occur; `text` is the one-JSON-object text contract
|
|
454
464
|
# alone — set it for a provider or model that cannot take a forced tool call.
|
|
465
|
+
# `operator` is the one door's flag (routing-and-config item 29): `off`
|
|
466
|
+
# (default) — the operator never runs; `shadow` — one operator turn per
|
|
467
|
+
# admitted chat event, ahead of stage A, its decision written beside the
|
|
468
|
+
# routed request in the run store and nothing run from it; `on` — the
|
|
469
|
+
# operator's decision is what runs. `shadow` runs even under `auto: false`.
|
|
455
470
|
# routing:
|
|
456
471
|
# auto: false
|
|
457
472
|
# model: anthropic/claude-haiku-4-5
|
|
458
473
|
# answer: text
|
|
474
|
+
# operator: shadow
|
|
459
475
|
|
|
460
476
|
# The thread-reply intake gate (docs/decisions/0058-a-thread-reply-is-read-before-it-is-answered-intake-decides-whether-the-bot-was-addressed.md;
|
|
461
477
|
# docs/reference/specs/routing-and-config.md item 27). How an unmentioned reply
|
|
@@ -517,6 +533,16 @@ workspaceDir: ./workspaces
|
|
|
517
533
|
# cacheRead: 0.125 # never $0, on the costs page and on a run's own page
|
|
518
534
|
# cacheWrite: 0
|
|
519
535
|
|
|
536
|
+
# Run metrics (docs/reference/specs/run-metrics.md). Optional. The Analytics Engine
|
|
537
|
+
# dataset every finished run's point lands in — the SAME name the deployment
|
|
538
|
+
# profile's `metrics.dataset` binds on the state Worker as RUN_METRICS (the bot
|
|
539
|
+
# warns at boot when the two differ; neither side refuses to start). The reader
|
|
540
|
+
# over the dataset — `metrics trend` and the /metrics page, behind the `metrics`
|
|
541
|
+
# capability — is later work of the same decision; until it lands, only `dataset`
|
|
542
|
+
# is read, for that boot comparison.
|
|
543
|
+
# metrics:
|
|
544
|
+
# dataset: switchboard_runs
|
|
545
|
+
|
|
520
546
|
# Delivery indicators: GET /delivery and `delivery report` (docs/reference/specs/delivery.md).
|
|
521
547
|
# Optional. Needs a GitHub credential (the App triple or GH_TOKEN); read-only on GitHub.
|
|
522
548
|
# delivery:
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Deploy preflight for the bot Worker (docs/reference/specs/slack-channel.md item 8).
|
|
3
3
|
//
|
|
4
|
-
// `wrangler deploy` rolls the bot container. Cloudflare's rollout sends SIGTERM
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// next generation
|
|
8
|
-
//
|
|
9
|
-
// `npm run deploy` runs
|
|
10
|
-
// - the bot reports runs in flight (`GET /healthz` `inFlight > 0`): the
|
|
11
|
-
// runner retries every minute up to its budget, then fails by name;
|
|
4
|
+
// `wrangler deploy` rolls the bot container. Cloudflare's rollout sends SIGTERM;
|
|
5
|
+
// since the run ledger's handoff (docs/reference/specs/run-history.md item 39) the bot
|
|
6
|
+
// hands every resumable run to the next generation and exits within seconds,
|
|
7
|
+
// and the next generation continues the runs under their own cards — so a
|
|
8
|
+
// deploy no longer waits on runs, and this preflight no longer refuses for
|
|
9
|
+
// them. `npm run deploy` runs it first and refuses only while
|
|
12
10
|
// - the container application is not in a settled state (a rollout is still
|
|
13
11
|
// provisioning/updating — `wrangler containers list --json`): a second
|
|
14
12
|
// rollout on top of one in progress replaces the instance the first put
|
|
15
13
|
// into its graceful drain and kills whatever it was running (two deploys
|
|
16
14
|
// 90 s apart once killed a review at 153 s).
|
|
17
|
-
// It WARNS (never refuses) when the bot
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
15
|
+
// It WARNS (never refuses) when the bot reports runs in flight (`inFlight > 0`
|
|
16
|
+
// — they hand off) or is already draining (`draining: true` — its resumable
|
|
17
|
+
// runs were handed off; a ship pipeline still in flight would be killed), and
|
|
18
|
+
// when /healthz says the reconnect catch-up is failing or the bot token lacks
|
|
19
|
+
// required scopes (`catchUp.error`, `catchUp.missingScopes`).
|
|
21
20
|
//
|
|
22
21
|
// Fail closed: unreachable bot, a body without the JSON shape (a bot whose
|
|
23
22
|
// /healthz answers a bare `ok` is not one this preflight can read), a wrangler failure, or an app
|
|
@@ -41,7 +40,7 @@ export const APP_NAME = "switchboard-switchboardserver";
|
|
|
41
40
|
const SETTLED_APP_STATES = new Set(["active", "ready"]);
|
|
42
41
|
|
|
43
42
|
const HOW_TO_FORCE =
|
|
44
|
-
"to deploy anyway (over
|
|
43
|
+
"to deploy anyway (over a rollout in progress, or blind when the bot cannot be consulted): `SWITCHBOARD_DEPLOY_FORCE=1 npm run deploy` (`node preflight.mjs --force` checks alone)";
|
|
45
44
|
|
|
46
45
|
/** GET /healthz. Never throws: `{ok:true,payload}` (parsed JSON, or the raw text when not JSON) or `{ok:false,error}`. */
|
|
47
46
|
export async function fetchHealth(baseUrl, { timeoutMs = 20_000 } = {}) {
|
|
@@ -165,16 +164,15 @@ export function decide({ health, apps }, { force = false } = {}) {
|
|
|
165
164
|
if (!Number.isInteger(p.inFlight) || p.inFlight < 0) {
|
|
166
165
|
problems.push(`bot reports an impossible inFlight=${JSON.stringify(p.inFlight)} (counter bug or old Worker)`);
|
|
167
166
|
} else if (p.inFlight > 0) {
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`${p.inFlight} run(s) in flight — the rollout would roll the bot container under them (a handoff is a recovery, not a guarantee)`,
|
|
167
|
+
// Not a refusal since the handoff (run-history item 39): SIGTERM hands
|
|
168
|
+
// every resumable run to the next generation, which continues it.
|
|
169
|
+
warnings.push(
|
|
170
|
+
`${p.inFlight} run(s) in flight — handed to the next generation on SIGTERM (run-history item 39); they continue there under their own cards`,
|
|
173
171
|
);
|
|
174
172
|
}
|
|
175
173
|
if (p.draining === true) {
|
|
176
174
|
warnings.push(
|
|
177
|
-
"bot is already draining from a previous deploy —
|
|
175
|
+
"bot is already draining from a previous deploy — its resumable runs are handed off; a ship pipeline still in flight would be killed when this rollout replaces the draining instance",
|
|
178
176
|
);
|
|
179
177
|
}
|
|
180
178
|
}
|
|
@@ -211,7 +209,7 @@ export function decide({ health, apps }, { force = false } = {}) {
|
|
|
211
209
|
forced: true,
|
|
212
210
|
problems,
|
|
213
211
|
warnings,
|
|
214
|
-
message: `preflight WARNING: deploying by force despite —\n${detail}\n
|
|
212
|
+
message: `preflight WARNING: deploying by force despite —\n${detail}\n a rollout landing on one in progress can disrupt it; in-flight runs hand off regardless (run-history item 39)${warningText}`,
|
|
215
213
|
};
|
|
216
214
|
}
|
|
217
215
|
return {
|
|
@@ -219,7 +217,7 @@ export function decide({ health, apps }, { force = false } = {}) {
|
|
|
219
217
|
forced: false,
|
|
220
218
|
problems,
|
|
221
219
|
warnings,
|
|
222
|
-
message: `preflight REFUSED: a Worker deploy rolls the bot container —\n${detail}\n wait
|
|
220
|
+
message: `preflight REFUSED: a Worker deploy rolls the bot container —\n${detail}\n wait and retry; ${HOW_TO_FORCE}${warningText}`,
|
|
223
221
|
};
|
|
224
222
|
}
|
|
225
223
|
|
|
@@ -197,8 +197,10 @@ export class SwitchboardServer extends Container<Env> {
|
|
|
197
197
|
* and exits, and the NEXT request through `fetch` starts the container again
|
|
198
198
|
* (`startBot`, env computed then). The keep-alive cron GETs /healthz every
|
|
199
199
|
* minute and the CLI's live gate polls it every 15 s, so the next request is
|
|
200
|
-
* never more than seconds away. Refuses (the deploy preflight's rules)
|
|
201
|
-
*
|
|
200
|
+
* never more than seconds away. Refuses (the deploy preflight's rules) only the
|
|
201
|
+
* fail-closed cases — no JSON body, an impossible `inFlight` — unless
|
|
202
|
+
* `force`; runs in flight or a drain under way warn and the stop proceeds
|
|
203
|
+
* (the handoff rule).
|
|
202
204
|
*/
|
|
203
205
|
async restart(opts: { force: boolean }): Promise<RestartOutcome> {
|
|
204
206
|
if (!this.ctx.container?.running) return { kind: "not-running" };
|
|
@@ -265,7 +267,8 @@ export class SwitchboardServer extends Container<Env> {
|
|
|
265
267
|
* `deploy:write` in the bot's config). The Worker authenticates the bearer
|
|
266
268
|
* against the map it holds — an unknown bearer never touches the container —
|
|
267
269
|
* and the Container DO asks the bot for the grant before stopping anything.
|
|
268
|
-
* Body `{ "force": true }` bypasses the
|
|
270
|
+
* Body `{ "force": true }` bypasses the fail-closed refusals (no JSON body,
|
|
271
|
+
* impossible `inFlight`); runs in flight or a drain warn and never refuse. */
|
|
269
272
|
async function handleAdminRestart(request: Request, env: Env): Promise<Response> {
|
|
270
273
|
const json = (status: number, body: Record<string, unknown>) =>
|
|
271
274
|
new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RunMetricsPoint } from "../../src/core/runMetrics.ts";
|
|
2
|
+
|
|
3
|
+
// The run-metrics sink (docs/reference/specs/run-metrics.md): where the
|
|
4
|
+
// RunHistoryDO writes the one point of a run whose row turned final, after the
|
|
5
|
+
// record's commit. Two implementations behind one seam (AGENTS.md invariant 2):
|
|
6
|
+
// the Workers Analytics Engine dataset when the Worker is deployed with the
|
|
7
|
+
// optional `RUN_METRICS` binding, the null sink otherwise — so a Worker without
|
|
8
|
+
// the binding behaves byte-identically, the same shape as `SHIP_COORDINATOR?`.
|
|
9
|
+
// The write is advisory on the caller's side too: one try, one console.warn,
|
|
10
|
+
// never a failed put or finish.
|
|
11
|
+
|
|
12
|
+
export interface RunMetricsSink {
|
|
13
|
+
write(point: RunMetricsPoint): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The dataset the deploy bound: `writeDataPoint` is synchronous and does not
|
|
17
|
+
* throw on a platform-side drop; the caller's try covers a missing or
|
|
18
|
+
* misconfigured binding. */
|
|
19
|
+
export class AnalyticsEngineSink implements RunMetricsSink {
|
|
20
|
+
constructor(private readonly dataset: AnalyticsEngineDataset) {}
|
|
21
|
+
write(point: RunMetricsPoint): void {
|
|
22
|
+
this.dataset.writeDataPoint({ indexes: point.indexes, blobs: point.blobs, doubles: point.doubles });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** No binding: points are dropped — the off state. */
|
|
27
|
+
export class NullSink implements RunMetricsSink {
|
|
28
|
+
write(): void {
|
|
29
|
+
// nothing is written without the binding
|
|
30
|
+
}
|
|
31
|
+
}
|