@coreplane/switchboard 1.254.1 → 1.255.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 (63) hide show
  1. package/dist/assets/.dockerignore +3 -0
  2. package/dist/assets/Dockerfile +12 -1
  3. package/dist/assets/config/config.example.yaml +6 -1
  4. package/dist/assets/deploy/cloudflare/worker.ts +39 -23
  5. package/dist/assets/deploy/cloudflare-memory/worker.ts +552 -5
  6. package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +5 -0
  7. package/dist/assets/deploy/cloudflare-resident/Dockerfile +13 -1
  8. package/dist/assets/deploy/cloudflare-resident/levels.ts +84 -0
  9. package/dist/assets/deploy/cloudflare-resident/prepare-commit-msg +17 -0
  10. package/dist/assets/deploy/cloudflare-resident/worker.ts +202 -31
  11. package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +11 -1
  12. package/dist/assets/deploy/cloudflare-sandbox/prepare-commit-msg +17 -0
  13. package/dist/assets/deploy/cloudflare-sandbox/worker.ts +21 -4
  14. package/dist/assets/deploy/hooks/prepare-commit-msg +17 -0
  15. package/dist/assets/deploy/secrets.manifest.json +12 -0
  16. package/dist/assets/package-lock.json +3 -3
  17. package/dist/assets/package.json +1 -1
  18. package/dist/assets/source.json +3 -3
  19. package/dist/assets/src/agents/registry.ts +21 -0
  20. package/dist/assets/src/core/budgets.ts +35 -2
  21. package/dist/assets/src/core/coordinator/contract.ts +6 -0
  22. package/dist/assets/src/core/coordinator/driver.ts +49 -6
  23. package/dist/assets/src/core/costs.ts +39 -16
  24. package/dist/assets/src/core/pipelineStanding.ts +5 -0
  25. package/dist/assets/src/core/plane/decide.ts +389 -22
  26. package/dist/assets/src/core/refusal.ts +3 -0
  27. package/dist/assets/src/core/reviewVerdict.ts +15 -2
  28. package/dist/assets/src/core/runEvents.ts +31 -12
  29. package/dist/assets/src/core/runLedger/sessionLog.ts +128 -0
  30. package/dist/assets/src/core/runLedger/types.ts +3 -0
  31. package/dist/assets/src/core/runRecord.ts +24 -7
  32. package/dist/assets/src/core/ship/coordinator.ts +327 -61
  33. package/dist/assets/src/core/trace/workerTrace.ts +3 -0
  34. package/dist/assets/src/execution/sandboxErrors.ts +77 -6
  35. package/dist/assets/web/dist/.vite/manifest.json +59 -58
  36. package/dist/assets/web/dist/assets/CostsPage-BaeWnm-o.js +1 -0
  37. package/dist/assets/web/dist/assets/{DeliveryPage-ngPsO2to.js → DeliveryPage-BBAyLwPq.js} +1 -1
  38. package/dist/assets/web/dist/assets/{HomePage-DvxTHzPx.js → HomePage-Be7jLLnU.js} +1 -1
  39. package/dist/assets/web/dist/assets/PendingTurnRow-DGINv9XT.js +1 -0
  40. package/dist/assets/web/dist/assets/{PlanePage-DpWfiX4C.js → PlanePage-JEj-lqgz.js} +1 -1
  41. package/dist/assets/web/dist/assets/{ResidentDetailPage-DG86v39Y.js → ResidentDetailPage-D_RD6wLo.js} +1 -1
  42. package/dist/assets/web/dist/assets/{ResidentsIndexPage-x6p689VH.js → ResidentsIndexPage-BLkSuCxo.js} +1 -1
  43. package/dist/assets/web/dist/assets/RunFoldRow-CfuZqf_O.js +1 -0
  44. package/dist/assets/web/dist/assets/{RunRoutePage-ysJBY8xQ.js → RunRoutePage-CmWYGR36.js} +4 -4
  45. package/dist/assets/web/dist/assets/{RunsIndexPage-CuzFchcn.js → RunsIndexPage-DW-HHuZa.js} +1 -1
  46. package/dist/assets/web/dist/assets/{ScheduledPage-BuLmfcbG.js → ScheduledPage-3aYsDf-q.js} +1 -1
  47. package/dist/assets/web/dist/assets/{SettingsPage-BujWkdU_.js → SettingsPage-DG-p5Xy1.js} +1 -1
  48. package/dist/assets/web/dist/assets/{StatusDot-C8Bc0pTX.js → StatusDot-CEnGlyAL.js} +1 -1
  49. package/dist/assets/web/dist/assets/{Tooltip-BWwJx27K.js → Tooltip-CiunVowT.js} +1 -1
  50. package/dist/assets/web/dist/assets/UnitRoutePage-iOYnmTcQ.js +1 -0
  51. package/dist/assets/web/dist/assets/budgets-c1eumrqD.js +1 -0
  52. package/dist/assets/web/dist/assets/{dist-CpnyQGOb.js → dist-luhv3YSo.js} +1 -1
  53. package/dist/assets/web/dist/assets/indexRow-DborJPFp.js +1 -0
  54. package/dist/assets/web/dist/assets/{main-Comxmwi4.js → main-mAKx_zo9.js} +2 -2
  55. package/dist/assets/web/dist/assets/{sseReplay-IzTdD4-3.js → sseReplay-DE6wv1Ua.js} +6 -6
  56. package/dist/cli.js +6022 -4956
  57. package/package.json +1 -1
  58. package/dist/assets/web/dist/assets/CostsPage-BuKjw3nv.js +0 -1
  59. package/dist/assets/web/dist/assets/PendingTurnRow-ZYIRCCZ2.js +0 -1
  60. package/dist/assets/web/dist/assets/RunFoldRow-DG29LOTs.js +0 -1
  61. package/dist/assets/web/dist/assets/UnitRoutePage-B9kjA1AT.js +0 -1
  62. package/dist/assets/web/dist/assets/budgets-CbIyPAER.js +0 -1
  63. package/dist/assets/web/dist/assets/indexRow-DABQtONT.js +0 -1
@@ -16,6 +16,9 @@ deploy
16
16
  # Which variables are credentials: src/secrets.ts reads the manifest at startup
17
17
  # (src/core/secretsManifest.test.ts holds this line and the Dockerfile's COPY in place).
18
18
  !deploy/secrets.manifest.json
19
+ # The agent-trailer hook: the root Dockerfile COPYs it to /opt/switchboard/hooks
20
+ # (src/execution/prepareCommitMsgHook.test.ts holds this line in place).
21
+ !deploy/hooks/prepare-commit-msg
19
22
  docs
20
23
  !docs/package.json
21
24
  README.md
@@ -125,11 +125,22 @@ COPY skills ./skills
125
125
  RUN mkdir -p /app/data /app/workspaces && chown -R switchboard:switchboard /app
126
126
  USER switchboard
127
127
 
128
+ # The shared agent-trailer hook (deploy/hooks/prepare-commit-msg): every
129
+ # commit made in this image gains the bot pair's Co-Authored-By trailer.
130
+ COPY --chmod=755 deploy/hooks/prepare-commit-msg /opt/switchboard/hooks/prepare-commit-msg
131
+
128
132
  # gh auth: set GH_TOKEN (fine-grained PAT for a machine account); gh and git
129
133
  # (via gh's credential helper, configured below) pick it up automatically.
134
+ # The fallback user.email stays OFF the GitHub domain (a noreply-shaped
135
+ # address would render as a GitHub account it is not); the real pairs ride
136
+ # the per-exec environment. The global core.hooksPath replaces repo-local
137
+ # .git/hooks entirely — deliberate: an untrusted checkout's own hooks never
138
+ # run here (a repo-local core.hooksPath still wins) —
139
+ # docs/reference/specs/execution.md item 5.
130
140
  RUN git config --global credential.helper '!gh auth git-credential' \
131
141
  && git config --global user.name "switchboard-bot" \
132
- && git config --global user.email "switchboard-bot@users.noreply.github.com"
142
+ && git config --global user.email "switchboard-bot@switchboard.invalid" \
143
+ && git config --global core.hooksPath /opt/switchboard/hooks
133
144
 
134
145
  ENV NODE_ENV=production
135
146
  # PORT enables the /healthz-style probe endpoint (any path returns 200).
@@ -515,10 +515,15 @@ workspaceDir: ./workspaces
515
515
  # object, which logs its decider's decision beside it — nothing runs from the
516
516
  # decider; `on` — a later release's flip (the decider's answer is what runs).
517
517
  # `reaskMinutes` (default 2) is the plane's one re-ask cadence, used only while
518
- # something waits on a reporter that fell silent; nothing reads it yet.
518
+ # something waits on a reporter that fell silent — a queued run waiting on a
519
+ # resident that has said nothing within the cadence gets one probe per cadence.
520
+ # `coldFallback` (default false) flips write presets back to falling cold under
521
+ # a resident condition instead of queueing at the resident stage — the rollback
522
+ # lever for the resident conditions.
519
523
  # plane:
520
524
  # admission: shadow
521
525
  # reaskMinutes: 2
526
+ # coldFallback: false
522
527
 
523
528
  # Linked threads (docs/decisions/0037-a-linked-thread-is-quoted-not-joined.md;
524
529
  # docs/reference/specs/routing-and-config.md item 22). Off by default. On, a
@@ -94,6 +94,8 @@ export interface Env {
94
94
  GITHUB_WEBHOOK_SECRET?: string; // check-run intake: signs POST /webhooks/github; absent, the intake answers 503 disabled
95
95
  CF_ANALYTICS_TOKEN?: string; // costs dash: Cloudflare API token, Account Analytics:Read only
96
96
  ANTHROPIC_ADMIN_KEY?: string; // costs dash (optional): Anthropic Admin API key for the LLM cost report
97
+ OPENROUTER_MANAGEMENT_KEY?: string; // costs dash (optional): OpenRouter management key for the openrouter biller's invoice tie-out
98
+ OPENAI_ADMIN_KEY?: string; // costs dash (optional): OpenAI admin key for the openai biller's invoice tie-out
97
99
  MEMORY_TOKEN?: string; // durable memory + friction ledger + schedule firings + MCP registry: bearer for the state Worker
98
100
  MCP_CREDENTIAL_KEY?: string; // MCP registry: the bot-only key that seals server credentials before they reach the McpDO
99
101
  MCP_ACCESS_CLIENT_ID?: string; // MCP servers behind Cloudflare Access: the service token's client id, named by a server's `headersEnv`
@@ -129,6 +131,8 @@ const FORWARDED_OPTIONAL = [
129
131
  "DASHBOARD_TOKEN",
130
132
  "CF_ANALYTICS_TOKEN",
131
133
  "ANTHROPIC_ADMIN_KEY",
134
+ "OPENROUTER_MANAGEMENT_KEY",
135
+ "OPENAI_ADMIN_KEY",
132
136
  "SWITCHBOARD_INGRESS_TOKENS",
133
137
  "BRAVE_SEARCH_API_KEY",
134
138
  "GITHUB_WEBHOOK_SECRET",
@@ -504,29 +508,41 @@ export default {
504
508
  const eventsId = parseInstanceEventPath(pathname);
505
509
  const statusId = eventsId === undefined ? parseInstanceStatusPath(pathname) : undefined;
506
510
  const res =
507
- pathname === "/admin/restart"
508
- ? await handleAdminRestart(forwarded, env)
509
- : pathname === COORDINATOR_INSTANCES_PATH
510
- ? await handleCoordinatorInstances(forwarded, env)
511
- : eventsId !== undefined
512
- ? await handleCoordinatorInstanceEvent(forwarded, env, eventsId)
513
- : statusId !== undefined
514
- ? await handleCoordinatorInstanceStatus(forwarded, env, statusId)
515
- : pathname === COPY_PATH
516
- ? // The artifact copy (artifactsCopy.ts): the R2 binding and the Slack
517
- // token are this Worker's; the bot only asks, with its copy bearer.
518
- // `forwarded`, never `inbound`: `withTraceContext` rebuilt the request
519
- // with `new Request(inbound, …)`, which takes the body stream with it —
520
- // `inbound.text()` is empty afterwards and the route read "not JSON" live.
521
- await handleArtifactsCopy(forwarded, {
522
- bucket: env.ARTIFACTS,
523
- bucketName: env.ARTIFACTS_BUCKET_NAME,
524
- copyToken: env.ARTIFACTS_COPY_TOKEN,
525
- slackToken: env.SLACK_BOT_TOKEN,
526
- fetch: (input, init) => fetch(input, init),
527
- lengthPipe: (size) => new FixedLengthStream(size),
528
- })
529
- : withLength(await getContainer(env.SWITCHBOARD, INSTANCE).fetch(forwarded));
511
+ pathname === "/plane/effects"
512
+ ? // The plane's effect push (record 0064, "Where it lives"): the state
513
+ // Worker POSTs committed effects over its service binding with the
514
+ // shared bearer; the shim checks it and forwards to the container,
515
+ // which executes or defers each effect. A refused bearer never
516
+ // reaches the container.
517
+ request.headers.get("authorization") === `Bearer ${env.MEMORY_TOKEN}` && env.MEMORY_TOKEN
518
+ ? withLength(await getContainer(env.SWITCHBOARD, INSTANCE).fetch(forwarded))
519
+ : new Response(JSON.stringify({ error: "unauthorized" }), {
520
+ status: 401,
521
+ headers: { "content-type": "application/json" },
522
+ })
523
+ : pathname === "/admin/restart"
524
+ ? await handleAdminRestart(forwarded, env)
525
+ : pathname === COORDINATOR_INSTANCES_PATH
526
+ ? await handleCoordinatorInstances(forwarded, env)
527
+ : eventsId !== undefined
528
+ ? await handleCoordinatorInstanceEvent(forwarded, env, eventsId)
529
+ : statusId !== undefined
530
+ ? await handleCoordinatorInstanceStatus(forwarded, env, statusId)
531
+ : pathname === COPY_PATH
532
+ ? // The artifact copy (artifactsCopy.ts): the R2 binding and the Slack
533
+ // token are this Worker's; the bot only asks, with its copy bearer.
534
+ // `forwarded`, never `inbound`: `withTraceContext` rebuilt the request
535
+ // with `new Request(inbound, …)`, which takes the body stream with it —
536
+ // `inbound.text()` is empty afterwards and the route read "not JSON" live.
537
+ await handleArtifactsCopy(forwarded, {
538
+ bucket: env.ARTIFACTS,
539
+ bucketName: env.ARTIFACTS_BUCKET_NAME,
540
+ copyToken: env.ARTIFACTS_COPY_TOKEN,
541
+ slackToken: env.SLACK_BOT_TOKEN,
542
+ fetch: (input, init) => fetch(input, init),
543
+ lengthPipe: (size) => new FixedLengthStream(size),
544
+ })
545
+ : withLength(await getContainer(env.SWITCHBOARD, INSTANCE).fetch(forwarded));
530
546
  root.end(res.status >= 500 ? "error" : "ok", { httpStatus: res.status });
531
547
  return res;
532
548
  } catch (err) {