@jr2/orchestrator 0.4.1 → 0.5.1
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/README.md +20 -2
- package/console/components/app.ts +2 -0
- package/console/components/fleet.ts +14 -1
- package/console/components/machine-pane.ts +41 -2
- package/console/main.ts +13 -1
- package/console/store.ts +102 -6
- package/console/style.css +21 -0
- package/docs/agents-block.md +5 -0
- package/docs/concepts/agents-and-turns.md +442 -0
- package/docs/concepts/architecture.md +230 -0
- package/docs/concepts/charts.md +143 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +254 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +603 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +289 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +222 -0
- package/docs/concepts/the-menu.md +378 -0
- package/docs/concepts/workspaces.md +678 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
- package/docs/get-started/connect-a-model.md +248 -0
- package/docs/get-started/create-an-instance.md +194 -0
- package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
- package/docs/get-started/install.md +126 -0
- package/docs/guides/add-tools-to-the-sandbox.md +295 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +223 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/fix-a-failed-converge.md +361 -0
- package/docs/guides/give-the-cluster-git-credentials.md +222 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
- package/docs/guides/let-an-agent-write-a-chart.md +226 -0
- package/docs/guides/look-inside-a-sandbox.md +174 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
- package/docs/guides/open-the-console.md +91 -0
- package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
- package/docs/guides/push-and-sign-a-branch.md +486 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +544 -0
- package/docs/guides/run-one-worker-per-item.md +321 -0
- package/docs/guides/set-an-agents-access-and-dials.md +375 -0
- package/docs/guides/size-a-workspace.md +204 -0
- package/docs/guides/start-a-run-from-a-run.md +237 -0
- package/docs/guides/start-a-run.md +207 -0
- package/docs/guides/upgrade-the-kit-version.md +240 -0
- package/docs/guides/use-a-hosted-provider.md +208 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +326 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +216 -0
- package/docs/reference/authoring-surface/agent.md +128 -0
- package/docs/reference/authoring-surface/answer.md +140 -0
- package/docs/reference/authoring-surface/arg.md +60 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/chartschema.md +23 -0
- package/docs/reference/authoring-surface/chartschemafor.md +48 -0
- package/docs/reference/authoring-surface/command.md +191 -0
- package/docs/reference/authoring-surface/customize.md +64 -0
- package/docs/reference/authoring-surface/defineconfig.md +41 -0
- package/docs/reference/authoring-surface/defineevent.md +114 -0
- package/docs/reference/authoring-surface/dispatch.md +126 -0
- package/docs/reference/authoring-surface/fail.md +83 -0
- package/docs/reference/authoring-surface/file.md +122 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +159 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +74 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +125 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +112 -0
- package/docs/reference/authoring-surface/secret.md +118 -0
- package/docs/reference/authoring-surface/sentby.md +83 -0
- package/docs/reference/authoring-surface/source.md +51 -0
- package/docs/reference/authoring-surface/text.md +62 -0
- package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
- package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
- package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
- package/docs/reference/authoring-surface/type-agentslots.md +12 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- package/docs/reference/authoring-surface/type-arg.md +19 -0
- package/docs/reference/authoring-surface/type-argnames.md +12 -0
- package/docs/reference/authoring-surface/type-binding.md +62 -0
- package/docs/reference/authoring-surface/type-bindingof.md +12 -0
- package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
- package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
- package/docs/reference/authoring-surface/type-ceilings.md +41 -0
- package/docs/reference/authoring-surface/type-chainlink.md +19 -0
- package/docs/reference/authoring-surface/type-chartaction.md +46 -0
- package/docs/reference/authoring-surface/type-chartagent.md +28 -0
- package/docs/reference/authoring-surface/type-chartevent.md +20 -0
- package/docs/reference/authoring-surface/type-chartguard.md +20 -0
- package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
- package/docs/reference/authoring-surface/type-chartjson.md +14 -0
- package/docs/reference/authoring-surface/type-chartstate.md +46 -0
- package/docs/reference/authoring-surface/type-charttext.md +40 -0
- package/docs/reference/authoring-surface/type-charttransition.md +22 -0
- package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
- package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
- package/docs/reference/authoring-surface/type-childat.md +12 -0
- package/docs/reference/authoring-surface/type-childslots.md +12 -0
- package/docs/reference/authoring-surface/type-commandenv.md +15 -0
- package/docs/reference/authoring-surface/type-commandinput.md +19 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
- package/docs/reference/authoring-surface/type-customize.md +109 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
- package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
- package/docs/reference/authoring-surface/type-duration.md +15 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
- package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
- package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
- package/docs/reference/authoring-surface/type-eventdef.md +45 -0
- package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
- package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
- package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
- package/docs/reference/authoring-surface/type-failslot.md +14 -0
- package/docs/reference/authoring-surface/type-failure.md +19 -0
- package/docs/reference/authoring-surface/type-failureinput.md +20 -0
- package/docs/reference/authoring-surface/type-filebody.md +41 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
- package/docs/reference/authoring-surface/type-httpinput.md +12 -0
- package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
- package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
- package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
- package/docs/reference/authoring-surface/type-jr2config.md +126 -0
- package/docs/reference/authoring-surface/type-lends.md +38 -0
- package/docs/reference/authoring-surface/type-logicat.md +12 -0
- package/docs/reference/authoring-surface/type-markedif.md +12 -0
- package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
- package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
- package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
- package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
- package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
- package/docs/reference/authoring-surface/type-openeffect.md +12 -0
- package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
- package/docs/reference/authoring-surface/type-openof.md +12 -0
- package/docs/reference/authoring-surface/type-opensecret.md +23 -0
- package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +15 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
- package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
- package/docs/reference/authoring-surface/type-quantity.md +14 -0
- package/docs/reference/authoring-surface/type-reached.md +12 -0
- package/docs/reference/authoring-surface/type-reposlot.md +70 -0
- package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
- package/docs/reference/authoring-surface/type-secret.md +25 -0
- package/docs/reference/authoring-surface/type-sender.md +19 -0
- package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
- package/docs/reference/authoring-surface/type-size.md +85 -0
- package/docs/reference/authoring-surface/type-slothandles.md +32 -0
- package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
- package/docs/reference/authoring-surface/type-syncstate.md +12 -0
- package/docs/reference/authoring-surface/type-text.md +19 -0
- package/docs/reference/authoring-surface/type-textpart.md +24 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
- package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
- package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
- package/docs/reference/authoring-surface/type-unknown.md +21 -0
- package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
- package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
- package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
- package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
- package/docs/reference/authoring-surface/type-workspaced.md +94 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
- package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
- package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
- package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
- package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
- package/docs/reference/authoring-surface/workspace.md +259 -0
- package/docs/reference/authoring-surface.md +197 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +756 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
- package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
- package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
- package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
- package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
- package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
- package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
- package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
- package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
- package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
- package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
- package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
- package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
- package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
- package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
- package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
- package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
- package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
- package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
- package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
- package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
- package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
- package/docs/reference/decisions.md +83 -0
- package/docs/reference/glossary.md +648 -0
- package/docs/reference/http-api.md +1348 -0
- package/docs/reference/machines.md +190 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +206 -0
- package/package.json +5 -3
- package/src/actor.ts +541 -79
- package/src/agent.ts +114 -38
- package/src/ambient.ts +2 -1
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +169 -37
- package/src/charts.ts +34 -6
- package/src/config.ts +430 -178
- package/src/custodian.ts +74 -22
- package/src/customize.ts +188 -46
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect.ts +341 -44
- package/src/effector-wire.ts +160 -11
- package/src/fail.ts +209 -0
- package/src/harness-client.ts +541 -47
- package/src/http-effect.ts +495 -53
- package/src/http.ts +427 -16
- package/src/index.ts +60 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +84 -0
- package/src/machine-doc.ts +21 -3
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +18 -58
- package/src/notices.ts +66 -13
- package/src/open-body.ts +159 -23
- package/src/open-effect.ts +81 -8
- package/src/open.ts +59 -6
- package/src/parts.ts +184 -13
- package/src/party.ts +68 -2
- package/src/pick.ts +463 -11
- package/src/pool.ts +247 -36
- package/src/registration.ts +83 -4
- package/src/repos.ts +2 -1
- package/src/run-host.ts +565 -31
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +273 -38
- package/src/sandbox-kube.ts +3 -1
- package/src/secret-placement.ts +52 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +239 -56
- package/src/size.ts +121 -9
- package/src/snapshot-store.ts +54 -10
- package/src/stub-harness.ts +44 -16
- package/src/timers.ts +14 -1
- package/src/vocabulary.ts +69 -14
- package/src/wire.ts +90 -16
- package/src/workspace.ts +697 -175
package/src/instance.ts
CHANGED
|
@@ -38,8 +38,17 @@ import {
|
|
|
38
38
|
type ResolvedAssembly,
|
|
39
39
|
} from "./assembly.ts";
|
|
40
40
|
import { assemblyRefusals, type LoadedWorkflow } from "./assembly-checks.ts";
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
chartName,
|
|
43
|
+
NamedCharts,
|
|
44
|
+
readChartFiles,
|
|
45
|
+
readRuntimeCharts,
|
|
46
|
+
splitChartName,
|
|
47
|
+
type ChartCounts,
|
|
48
|
+
type ChartFile,
|
|
49
|
+
} from "./charts.ts";
|
|
42
50
|
import type { ImageRefs } from "./images.ts";
|
|
51
|
+
import { partsOf } from "./parts.ts";
|
|
43
52
|
import { join } from "node:path";
|
|
44
53
|
import { pathToFileURL } from "node:url";
|
|
45
54
|
import type { AddressInfo } from "node:net";
|
|
@@ -56,6 +65,7 @@ import type { SnapshotStore } from "./snapshot-store.ts";
|
|
|
56
65
|
import { createAuthenticator, effectorKey, harnessToken, loadSigningKey, mintInstanceToken } from "./tokens.ts";
|
|
57
66
|
import type { SandboxPort } from "./workspace.ts";
|
|
58
67
|
import type { EffectorPlacement, EffectorTarget } from "./http-effect.ts";
|
|
68
|
+
import { WORKFLOW_NAME } from "./effector-wire.ts";
|
|
59
69
|
|
|
60
70
|
export type InstanceOptions = {
|
|
61
71
|
/** The instance folder: the state volume `.jr2/` (its `state.db` unless `store` is supplied, and
|
|
@@ -119,6 +129,10 @@ export type InstanceOptions = {
|
|
|
119
129
|
* The entrypoint derives it from the pod's namespace (deterministic Service DNS); absent,
|
|
120
130
|
* such a Turn without an explicit `endpoint` faults pointedly. */
|
|
121
131
|
instanceHarness?: string;
|
|
132
|
+
/** The name this Orchestrator gives itself at another Orchestrator's door (ADR-0071): a foreign
|
|
133
|
+
* dispatch's `dispatchedBy.instance`. The entrypoint passes the pod's namespace; absent, a foreign
|
|
134
|
+
* dispatch names an unnamed caller. */
|
|
135
|
+
orchestratorName?: string;
|
|
122
136
|
/** The Instance Effector's headless Service, `host:port` (ADR-0068) — where an http Effect is sent.
|
|
123
137
|
* The entrypoint derives it from the pod's namespace; absent, an http Effect faults pointedly. */
|
|
124
138
|
instanceEffector?: string;
|
|
@@ -287,6 +301,7 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
|
|
|
287
301
|
resolveDef,
|
|
288
302
|
sandbox: opts.sandbox,
|
|
289
303
|
instanceHarness: opts.instanceHarness,
|
|
304
|
+
...(opts.orchestratorName !== undefined ? { orchestratorName: opts.orchestratorName } : {}),
|
|
290
305
|
harnessBearer: (placement) => harnessToken(signingKey, placement),
|
|
291
306
|
// The Effectors (ADR-0068): each placement's Service, and the key its wire is signed with —
|
|
292
307
|
// derived from the signing key as the Harness bearer is, so nothing is stored. The Instance
|
|
@@ -307,12 +322,15 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
|
|
|
307
322
|
// says why (ADR-0030). stderr, because it is a fault, not the boot's structured result.
|
|
308
323
|
onRestoreError: (runId, err) =>
|
|
309
324
|
console.error(`restore failed for run ${runId}: ${err instanceof Error ? err.message : err}`),
|
|
325
|
+
// An in-process dispatch of a named Chart the current Assembly left unrunnable answers 409, as
|
|
326
|
+
// the HTTP door does (ADR-0071). The registry is built after the host; this runs only at a start.
|
|
327
|
+
unrunnableChart: (name): string | undefined => charts.get(name)?.unrunnable,
|
|
310
328
|
});
|
|
311
329
|
|
|
312
330
|
// The named Charts (ADR-0070). The runtime ones are read from the volume now, and admitted when
|
|
313
331
|
// the first Assembly binds; with none current, every one waits unrunnable ("not in the current
|
|
314
332
|
// Assembly") and is never deleted.
|
|
315
|
-
const charts = new NamedCharts({ dir: opts.dir, host });
|
|
333
|
+
const charts: NamedCharts = new NamedCharts({ dir: opts.dir, host });
|
|
316
334
|
await charts.load();
|
|
317
335
|
// The current Assembly's own Workflow names (ADR-0069, ADR-0070): its Machines and its Chart
|
|
318
336
|
// files, as its record lists them. `GET /assembly` answers these; the runtime Charts the host
|
|
@@ -352,7 +370,8 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
|
|
|
352
370
|
// by name. No current Assembly → no Workflows, and the process still serves (ADR-0048).
|
|
353
371
|
if (current) {
|
|
354
372
|
const booted = current;
|
|
355
|
-
|
|
373
|
+
const loaded = await importAssembly(booted.hash, booted.root);
|
|
374
|
+
swap(loaded, await readChartFiles(booted.root, undefined, contextDirsOf(loaded)), booted);
|
|
356
375
|
} else {
|
|
357
376
|
charts.bind({ workflows: [], files: [] });
|
|
358
377
|
}
|
|
@@ -390,11 +409,14 @@ export async function startInstance(opts: InstanceOptions): Promise<RunningInsta
|
|
|
390
409
|
const workflows = await importWorkflows(root);
|
|
391
410
|
// The Assembly's Chart files (ADR-0070): the door admits each against its wrapper, and one
|
|
392
411
|
// that fails refuses the publish naming the file.
|
|
393
|
-
const files = await readChartFiles(root);
|
|
412
|
+
const files = await readChartFiles(root, undefined, contextDirsOf(workflows));
|
|
413
|
+
// The runtime Charts on the volume: a dispatch of a named Chart may resolve to one (ADR-0071).
|
|
414
|
+
const runtimeCharts = (await readRuntimeCharts(opts.dir)).map((c) => ({ name: chartName(c.wrapper, c.name) }));
|
|
394
415
|
const refusals = await assemblyRefusals({
|
|
395
416
|
config,
|
|
396
417
|
workflows,
|
|
397
418
|
charts: files,
|
|
419
|
+
runtimeCharts,
|
|
398
420
|
...(opts.imageMap ? { images: opts.imageMap } : {}),
|
|
399
421
|
root,
|
|
400
422
|
});
|
|
@@ -615,10 +637,22 @@ export async function loadWorkflows(dir: string): Promise<Array<{ name: string;
|
|
|
615
637
|
* `workflows/<wrapper>/<name>.chart.json`, one level below the flat `workflows/*.ts`. The sibling
|
|
616
638
|
* of {@link loadWorkflows}, for `jr2 up`'s host-side walk, which admits each file against its
|
|
617
639
|
* wrapper with `chartFileRefusals`, the check the door's `assemblyRefusals({ charts })` runs. Any other file in a
|
|
618
|
-
* wrapper's directory is ignored and named to `warn
|
|
640
|
+
* wrapper's directory is ignored and named to `warn`, except in a docker context that one of
|
|
641
|
+
* `workflows` names (ADR-0037): jr2 reads that folder too.
|
|
619
642
|
*/
|
|
620
|
-
export function loadCharts(
|
|
621
|
-
|
|
643
|
+
export function loadCharts(
|
|
644
|
+
root: string,
|
|
645
|
+
workflows: LoadedWorkflow[],
|
|
646
|
+
warn?: (line: string) => void,
|
|
647
|
+
): Promise<ChartFile[]> {
|
|
648
|
+
return readChartFiles(root, warn, contextDirsOf(workflows));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/** Every docker context folder the registered Machines name (ADR-0037, ADR-0049): a Sandbox
|
|
652
|
+
* Image, a User Container image or an Effector image that jr2 builds. The Chart discovery does not
|
|
653
|
+
* name a file in one (ADR-0070). */
|
|
654
|
+
function contextDirsOf(workflows: LoadedWorkflow[]): string[] {
|
|
655
|
+
return partsOf(workflows.map((w) => w.machine)).images.map((i) => i.dir);
|
|
622
656
|
}
|
|
623
657
|
|
|
624
658
|
/**
|
|
@@ -629,6 +663,11 @@ export function loadCharts(root: string, warn?: (line: string) => void): Promise
|
|
|
629
663
|
* and the `images/<name>` dirname scan it was also written for both retired with ADR-0049. An
|
|
630
664
|
* ABSENT dir is empty; any other readdir failure (EACCES, ENOTDIR, …) throws — a directory that
|
|
631
665
|
* exists but cannot be read must be loud, never "no modules".
|
|
666
|
+
*
|
|
667
|
+
* A stem is a Workflow name, and a Workflow name has one rule wherever it is written: lowercase
|
|
668
|
+
* letters, digits and "-", the rule a Chart name keeps too (`WORKFLOW_NAME`). So `jr2 run`, the
|
|
669
|
+
* door's path and a `dispatch` slot name the same set of Workflows (ADR-0071), and a stem outside
|
|
670
|
+
* the rule throws here, naming the rename, before anything registers it.
|
|
632
671
|
*/
|
|
633
672
|
export async function discoverModules(moduleDir: string): Promise<Array<{ name: string; file: string }>> {
|
|
634
673
|
let entries: string[];
|
|
@@ -638,10 +677,31 @@ export async function discoverModules(moduleDir: string): Promise<Array<{ name:
|
|
|
638
677
|
if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
639
678
|
throw err;
|
|
640
679
|
}
|
|
641
|
-
|
|
680
|
+
const modules = entries
|
|
642
681
|
.filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts") && !f.startsWith("_"))
|
|
643
682
|
.sort()
|
|
644
683
|
.map((f) => ({ name: f.slice(0, -3), file: join(moduleDir, f) }));
|
|
684
|
+
for (const { name } of modules) {
|
|
685
|
+
if (!WORKFLOW_NAME.test(name)) {
|
|
686
|
+
throw new Error(
|
|
687
|
+
`${join(moduleDir, `${name}.ts`)}: a Workflow is named by its filename stem, in lowercase letters, digits ` +
|
|
688
|
+
`and "-", so that \`jr2 run\` and a \`dispatch\` slot name it the same way — rename it to ` +
|
|
689
|
+
`${kebabOf(name)}.ts (ADR-0071)`,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return modules;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/** A stem outside the rule, as the rule would spell it: `reviewLoop` and `review_loop` are
|
|
697
|
+
* `review-loop`. */
|
|
698
|
+
function kebabOf(stem: string): string {
|
|
699
|
+
const out = stem
|
|
700
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
701
|
+
.toLowerCase()
|
|
702
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
703
|
+
.replace(/^-+|-+$/g, "");
|
|
704
|
+
return out === "" ? "workflow" : out;
|
|
645
705
|
}
|
|
646
706
|
|
|
647
707
|
/**
|
package/src/internal.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// @jr2/orchestrator/internal — NOT for workflow authors. Everything the kit's own packages share
|
|
2
|
+
// that is not on the Authoring surface (index.ts): the run host, the HTTP API, the Harness wire,
|
|
3
|
+
// the Custodian, the Effector wire, the cluster names, the load walk. The CLI, the e2e suite,
|
|
4
|
+
// and any other kit package that imports this one use it. No stability promise: any
|
|
5
|
+
// name here can change or go in any release, so a workflow or a `jr2.config.ts` that imports it
|
|
6
|
+
// is wrong, whatever compiles.
|
|
7
|
+
//
|
|
8
|
+
// It is the whole of index.ts plus the rest, so a kit file imports one specifier. The Orchestrator
|
|
9
|
+
// does NOT speak MCP (ADR-0013): the Harness presents the Menu to its model itself, and reaches the
|
|
10
|
+
// surface here through the Custodian in its pod.
|
|
11
|
+
|
|
12
|
+
export * from "./index.ts";
|
|
13
|
+
|
|
14
|
+
// The wire contract (ADR-0011): `defineEvent` is on the surface; the rest is the kit's.
|
|
15
|
+
export * from "@jr2/agent-protocol";
|
|
16
|
+
export * from "./agent.ts";
|
|
17
|
+
export * from "./effect.ts";
|
|
18
|
+
export * from "./http-effect.ts";
|
|
19
|
+
export * from "./sandbox-effect.ts";
|
|
20
|
+
export * from "./dispatch-effect.ts";
|
|
21
|
+
export type { EffectBinding, OpenEffect, OpenEffectPart } from "./open-effect.ts";
|
|
22
|
+
export { openPartsText } from "./open-effect.ts";
|
|
23
|
+
export * from "./effector-wire.ts";
|
|
24
|
+
export * from "./pick.ts";
|
|
25
|
+
export * from "./chart.ts";
|
|
26
|
+
export * from "./open-body.ts";
|
|
27
|
+
export * from "./charts.ts";
|
|
28
|
+
export * from "./parts.ts";
|
|
29
|
+
export * from "./size.ts";
|
|
30
|
+
export * from "./customize.ts";
|
|
31
|
+
export * from "./ambient.ts";
|
|
32
|
+
export * from "./config.ts";
|
|
33
|
+
export * from "./repo-identity.ts";
|
|
34
|
+
export * from "./pool.ts";
|
|
35
|
+
export * from "./setup.ts";
|
|
36
|
+
export { sentBy } from "./party.ts";
|
|
37
|
+
export * from "./vocabulary.ts";
|
|
38
|
+
export * from "./tokens.ts";
|
|
39
|
+
export * from "./durability.ts";
|
|
40
|
+
export * from "./snapshot-store.ts";
|
|
41
|
+
export * from "./actor.ts";
|
|
42
|
+
// The `gate` actor: jr2Setup pre-registers it and a state invokes it as `src: "gate"`, so no
|
|
43
|
+
// author writes the value (ADR-0015).
|
|
44
|
+
export * from "./gate.ts";
|
|
45
|
+
// The Failure's brand stays in fail.ts, and so does the logic that throws it: the kit makes a Failure
|
|
46
|
+
// only through `fail()` and `workspace()`, which imports that logic from fail.ts (ADR-0073).
|
|
47
|
+
export { failurePayload, isFailSlot } from "./fail.ts";
|
|
48
|
+
export type { Failure, FailSlot, FailureInput } from "./fail.ts";
|
|
49
|
+
export * from "./workspace.ts";
|
|
50
|
+
export * from "./images.ts";
|
|
51
|
+
export * from "./content-hash.ts";
|
|
52
|
+
export * from "./assembly-tar.ts";
|
|
53
|
+
export * from "./assembly.ts";
|
|
54
|
+
export * from "./assembly-checks.ts";
|
|
55
|
+
export * from "./mount-digest.ts";
|
|
56
|
+
export * from "./model-keys.ts";
|
|
57
|
+
export * from "./held-secrets.ts";
|
|
58
|
+
export * from "./secret-placement.ts";
|
|
59
|
+
export * from "./git-push.ts";
|
|
60
|
+
export * from "./git-clone.ts";
|
|
61
|
+
export * from "./custodian.ts";
|
|
62
|
+
export * from "./sandbox-kube.ts";
|
|
63
|
+
export {
|
|
64
|
+
effectorManifestKey,
|
|
65
|
+
LABEL_EFFECTOR_POD,
|
|
66
|
+
readSandboxEffectorManifest,
|
|
67
|
+
sandboxEffectorKeySecret,
|
|
68
|
+
sandboxEffectorObjectsOf,
|
|
69
|
+
type ProjectedSecret,
|
|
70
|
+
type SandboxEffectorManifest,
|
|
71
|
+
} from "./sandbox-effector.ts";
|
|
72
|
+
export * from "./repos.ts";
|
|
73
|
+
export * from "./repo-fetch.ts";
|
|
74
|
+
// Type-only: what a workflow event delivery looks like (the registration TABLE stays internal
|
|
75
|
+
// even here — ADR-0011).
|
|
76
|
+
export type { ChildErrorMarker, DeliveredEvent, PlacingMarker, RunMarker, TurnMarker } from "./registration.ts";
|
|
77
|
+
export * from "./machine-doc.ts";
|
|
78
|
+
export * from "./run-host.ts";
|
|
79
|
+
export * from "./http.ts";
|
|
80
|
+
export * from "./harness-client.ts";
|
|
81
|
+
export * from "./stub-harness.ts";
|
|
82
|
+
export * from "./instance.ts";
|
|
83
|
+
export * from "./server.ts";
|
|
84
|
+
export * from "./names.ts";
|
package/src/machine-doc.ts
CHANGED
|
@@ -19,7 +19,9 @@ import type { AnyStateMachine, StateNode, TransitionDefinition } from "xstate";
|
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import type { EventAudience } from "@jr2/agent-protocol";
|
|
21
21
|
import { argvText, isEffect, targetText, type EffectParts } from "./effect.ts";
|
|
22
|
+
import { isFailSlot } from "./fail.ts";
|
|
22
23
|
import { loadWarningsOf, seatsOf, vocabularyOf, type MachineSeat } from "./vocabulary.ts";
|
|
24
|
+
import { open } from "./open.ts";
|
|
23
25
|
|
|
24
26
|
/** One transition of the Machine, id-addressed at both ends. */
|
|
25
27
|
export type MachineTransitionDoc = {
|
|
@@ -55,7 +57,11 @@ export type EffectDoc =
|
|
|
55
57
|
file?: true;
|
|
56
58
|
}
|
|
57
59
|
/** A push (ADR-0067): which options the author set. */
|
|
58
|
-
| { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
|
|
60
|
+
| { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
|
|
61
|
+
/** A dispatch (ADR-0071): the Workflow it starts a run of, whether it waits, and the other
|
|
62
|
+
* Orchestrator's origin (`<open>` while a package leaves it Open); no target is this Orchestrator.
|
|
63
|
+
* The token's secret is named on the parts, never here: the target is what a reader routes on. */
|
|
64
|
+
| { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
|
|
59
65
|
|
|
60
66
|
/** One state of the Machine; `states` nests in document order. */
|
|
61
67
|
export type MachineStateDoc = {
|
|
@@ -67,8 +73,9 @@ export type MachineStateDoc = {
|
|
|
67
73
|
/** Initial child state id (compound only). */
|
|
68
74
|
initial?: string;
|
|
69
75
|
/** Each invoke's id and slot; an Effect's carries its plain-data parts (ADR-0067) — what it runs,
|
|
70
|
-
* where, and for how long, read without running anything.
|
|
71
|
-
|
|
76
|
+
* where, and for how long, read without running anything. `fail`: the slot is a `fail()`, so this
|
|
77
|
+
* state can fail the run (ADR-0073). */
|
|
78
|
+
invoke: Array<{ id: string; src: string; effect?: EffectDoc; fail?: true }>;
|
|
72
79
|
tags: string[];
|
|
73
80
|
description?: string;
|
|
74
81
|
states: MachineStateDoc[];
|
|
@@ -326,6 +333,7 @@ function serializeState(node: StateNode<any, any>, into: MachineTransitionDoc[],
|
|
|
326
333
|
type: node.type,
|
|
327
334
|
invoke: node.invoke.map((inv) => {
|
|
328
335
|
const logic = typeof inv.src === "string" ? scope.actors[inv.src] : undefined;
|
|
336
|
+
if (isFailSlot(logic)) return { id: inv.id, src: srcName(inv.src), fail: true as const };
|
|
329
337
|
if (!isEffect(logic)) return { id: inv.id, src: srcName(inv.src) };
|
|
330
338
|
return { id: inv.id, src: srcName(inv.src), effect: effectDoc(logic.effect) };
|
|
331
339
|
}),
|
|
@@ -404,6 +412,16 @@ function effectDoc(parts: EffectParts): EffectDoc {
|
|
|
404
412
|
const { sign, squash, force, pin, timeoutMs, slot } = parts;
|
|
405
413
|
return { kind: "push", sign, squash, force, pin, timeoutMs, ...(slot ? { slot } : {}) };
|
|
406
414
|
}
|
|
415
|
+
if (parts.kind === "dispatch") {
|
|
416
|
+
const { workflow, wait, timeoutMs, target } = parts;
|
|
417
|
+
return {
|
|
418
|
+
kind: "dispatch",
|
|
419
|
+
workflow,
|
|
420
|
+
wait,
|
|
421
|
+
...(target === undefined ? {} : { target: target === open ? "<open>" : target.url }),
|
|
422
|
+
timeoutMs,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
407
425
|
const { argv, timeoutMs, slot, secrets, pin } = parts;
|
|
408
426
|
return {
|
|
409
427
|
kind: "command",
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// A mechanism event must have a route (ADR-0074). jr2 sends `agent.fault` to the Machine that
|
|
2
|
+
// invoked an Agent when the Agent's Turn cannot go on (ADR-0016), and `workspace()` sends
|
|
3
|
+
// `workspace.lost` to its body when the Sandbox is gone (ADR-0012), and an Open body sends a
|
|
4
|
+
// Chart's unrouted `agent.fault` to the Machine that invoked it (ADR-0070). Each is a relayed
|
|
5
|
+
// event, and xstate drops an event that no active state handles. So the walk refuses a state that cannot
|
|
6
|
+
// route one, and the run host faults a run in which none took one.
|
|
7
|
+
//
|
|
8
|
+
// - `refuseUnroutedAgentFault`: a state that invokes an Agent, or an Open body that can run one
|
|
9
|
+
// (it lends an Agent or allows a model), when that state and all its ancestors have no
|
|
10
|
+
// `agent.fault` route. In `jr2Setup().createMachine` and in the load walk (`partsOf`), beside the
|
|
11
|
+
// lost-output refusal, so `jr2 check`, `jr2 up` and the Orchestrator's load show it. Not for a
|
|
12
|
+
// compiled Chart: there the route is optional, and the Open body sends an `agent.fault` that no
|
|
13
|
+
// transition of the Chart takes to the Machine that invoked it (open-body.ts; ADR-0070).
|
|
14
|
+
// - `refuseUnroutedLost`: a non-final leaf state of a `workspace()` body, when that state and all
|
|
15
|
+
// its ancestors have no `workspace.lost` route. In `workspace()`, the one place that knows that a
|
|
16
|
+
// Machine is a body. A compound or parallel state is not judged by itself: an event goes to the
|
|
17
|
+
// active leaves first, so a route on each leaf (or above it) is a route for the compound state.
|
|
18
|
+
// A final state of the root ends the body, so it is not judged. A nested final state is judged
|
|
19
|
+
// when its compound state has no `onDone`: the body then stays in the compound state, and a
|
|
20
|
+
// route on the compound state or above it is the route. The line names the compound state. A
|
|
21
|
+
// final state of a parallel region is not judged: the other regions are active, and their leaves
|
|
22
|
+
// are judged.
|
|
23
|
+
// - `unroutedMechanismEvent`: the runtime half, for a route that the walk cannot judge (a guard
|
|
24
|
+
// that is false when the event comes). The run host reads it off each microstep.
|
|
25
|
+
//
|
|
26
|
+
// A route is any transition for the event: one with actions only counts, because it can be a
|
|
27
|
+
// deliberate `reenter: true` that invokes the Agent again. jr2 does not judge what a route does.
|
|
28
|
+
// A wildcard (`"*"`, `"agent.*"`) that takes the event is a route too, as it is at runtime.
|
|
29
|
+
|
|
30
|
+
import type { AnyStateMachine, StateNode } from "xstate";
|
|
31
|
+
import { isAgent } from "./agent.ts";
|
|
32
|
+
import { isOpenBody, openBodyDeclarationOf } from "./open-body.ts";
|
|
33
|
+
|
|
34
|
+
/** The two mechanism events that must have a route. */
|
|
35
|
+
const ROUTED_MECHANISM_EVENTS = ["agent.fault", "workspace.lost"] as const;
|
|
36
|
+
|
|
37
|
+
type Node = StateNode<any, any>;
|
|
38
|
+
|
|
39
|
+
/** Does a descriptor of a state's `on` take this event type? xstate's own match: the exact type,
|
|
40
|
+
* `"*"`, or a partial wildcard like `"agent.*"`. */
|
|
41
|
+
function takes(descriptor: string, type: string): boolean {
|
|
42
|
+
if (descriptor === type || descriptor === "*") return true;
|
|
43
|
+
return descriptor.endsWith(".*") && type.startsWith(descriptor.slice(0, -1));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Does this state, or a state above it, have a route for `type`? */
|
|
47
|
+
function routedAt(node: Node, type: string): boolean {
|
|
48
|
+
for (let at: Node | undefined = node; at !== undefined; at = at.parent) {
|
|
49
|
+
for (const descriptor of at.transitions.keys()) if (takes(descriptor, type)) return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Every state node of the tree below (and with) `node`, in document order. */
|
|
55
|
+
function statesOf(node: Node): Node[] {
|
|
56
|
+
const out: Node[] = [node];
|
|
57
|
+
const children = Object.values(node.states as Record<string, Node>).sort((a, b) => a.order - b.order);
|
|
58
|
+
for (const child of children) out.push(...statesOf(child));
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The text of one or more state ids, quoted. */
|
|
63
|
+
function quoted(ids: readonly string[]): string {
|
|
64
|
+
return ids.map((id) => `"${id}"`).join(", ");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Is this slot an Open body that can run an Agent whose `agent.fault` comes to the Machine? One
|
|
68
|
+
* that lends an Agent or lets a Chart add its own: the body sends the Machine a Chart's unrouted
|
|
69
|
+
* `agent.fault`. A lent Machine routes its own Agents, and the walk judges it by itself. */
|
|
70
|
+
function runsChartAgents(logic: unknown): boolean {
|
|
71
|
+
if (!isOpenBody(logic)) return false;
|
|
72
|
+
const decl = openBodyDeclarationOf(logic);
|
|
73
|
+
return (decl.lends.agents?.length ?? 0) > 0 || decl.allows.models.length > 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Refuse a Machine with a state that invokes an Agent, or an Open body that can run one, when that
|
|
78
|
+
* state and all its ancestors have no `agent.fault` route (ADR-0074). The slots are the Machine's
|
|
79
|
+
* own `actors`. Throws one line that names each state, the event and the route to write.
|
|
80
|
+
*/
|
|
81
|
+
export function refuseUnroutedAgentFault(machine: AnyStateMachine): void {
|
|
82
|
+
const actors = (machine.implementations.actors ?? {}) as Record<string, unknown>;
|
|
83
|
+
const agents: string[] = [];
|
|
84
|
+
const bodies: string[] = [];
|
|
85
|
+
for (const node of statesOf(machine.root as Node)) {
|
|
86
|
+
if (routedAt(node, "agent.fault")) continue;
|
|
87
|
+
const srcs = node.invoke.map((inv) => (typeof inv.src === "string" ? actors[inv.src] : undefined));
|
|
88
|
+
if (srcs.some((logic) => isAgent(logic))) agents.push(node.id);
|
|
89
|
+
else if (srcs.some((logic) => runsChartAgents(logic))) bodies.push(node.id);
|
|
90
|
+
}
|
|
91
|
+
if (!agents.length && !bodies.length) return;
|
|
92
|
+
const clause = (ids: string[], what: string) =>
|
|
93
|
+
`${ids.length > 1 ? "states" : "state"} ${quoted(ids)} ${ids.length > 1 ? "invoke" : "invokes"} ${what}`;
|
|
94
|
+
const what = [
|
|
95
|
+
...(agents.length ? [clause(agents, "an Agent")] : []),
|
|
96
|
+
...(bodies.length ? [clause(bodies, "an Open body that can run an Agent")] : []),
|
|
97
|
+
].join(", and ");
|
|
98
|
+
const many = agents.length + bodies.length > 1;
|
|
99
|
+
throw new Error(
|
|
100
|
+
`machine "${machine.id}": ${what}, and no route for "agent.fault" is on ${many ? "them" : "it"} or on a state ` +
|
|
101
|
+
`above — jr2 sends agent.fault when the Agent's Turn cannot go on, and with no route the run faults: route it ` +
|
|
102
|
+
`on that state or on a state above it, as on: { "agent.fault": "<the state that handles it>" } (ADR-0074)`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Does the body stay in this final state? It does when the final state is nested, its compound
|
|
107
|
+
* state has no `onDone`, and that compound state is no region of a parallel state. */
|
|
108
|
+
function staysIn(final: Node): boolean {
|
|
109
|
+
const parent = final.parent;
|
|
110
|
+
if (parent === undefined || parent.parent === undefined) return false;
|
|
111
|
+
if (parent.parent.type === "parallel") return false;
|
|
112
|
+
return !parent.transitions.has(`xstate.done.state.${parent.id}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Refuse a `workspace()` body with a state that is not final, that it can stay in, when that state
|
|
117
|
+
* and all its ancestors have no `workspace.lost` route (ADR-0074). Throws one line that names each
|
|
118
|
+
* state, the event and the route to write.
|
|
119
|
+
*/
|
|
120
|
+
export function refuseUnroutedLost(body: AnyStateMachine): void {
|
|
121
|
+
// The leaves: an atomic state, a root with no child state, or a final state that the body can stay
|
|
122
|
+
// in. A history state is no state the body is in.
|
|
123
|
+
const unrouted = statesOf(body.root as Node)
|
|
124
|
+
.filter((node) => Object.keys(node.states).length === 0 && node.type !== "history")
|
|
125
|
+
.filter((node) => node.type !== "final" || staysIn(node))
|
|
126
|
+
.filter((node) => !routedAt(node, "workspace.lost"))
|
|
127
|
+
// The body stays in the compound state of a final state that it can stay in, and that state is
|
|
128
|
+
// not final: the line names it.
|
|
129
|
+
.map((node) => (node.type === "final" ? node.parent!.id : node.id));
|
|
130
|
+
if (!unrouted.length) return;
|
|
131
|
+
const many = unrouted.length > 1;
|
|
132
|
+
throw new Error(
|
|
133
|
+
`workspace(): the body "${body.id}" has ${many ? "states" : "a state"} ${quoted(unrouted)} that ` +
|
|
134
|
+
`${many ? "are" : "is"} not final, and no route for "workspace.lost" is on ${many ? "them" : "it"} or on a ` +
|
|
135
|
+
`state above — jr2 sends workspace.lost to the body when its Sandbox is gone, and with no route the run ` +
|
|
136
|
+
`faults: route it on the body's root, as on: { "workspace.lost": "<the state that handles it>" } (ADR-0074)`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The parts of an `@xstate.microstep` inspection event that the runtime check reads. */
|
|
141
|
+
export type MicrostepView = {
|
|
142
|
+
actorRef: { id: string; getSnapshot(): unknown; logic?: unknown };
|
|
143
|
+
event: { type: string; reason?: unknown };
|
|
144
|
+
snapshot?: unknown;
|
|
145
|
+
_transitions?: readonly unknown[];
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The fault of a microstep in which a Machine got `agent.fault` or `workspace.lost` and took no
|
|
150
|
+
* transition for it (ADR-0074), or undefined for every other microstep. The text names the event,
|
|
151
|
+
* the Machine and its state, and the event's `reason`. For `workspace.lost` this is the body's
|
|
152
|
+
* microstep: the wrapper always takes its own transition, to forward the event to the body.
|
|
153
|
+
*/
|
|
154
|
+
export function unroutedMechanismEvent(ev: MicrostepView): string | undefined {
|
|
155
|
+
const type = ev.event?.type;
|
|
156
|
+
if (!(ROUTED_MECHANISM_EVENTS as readonly string[]).includes(type)) return undefined;
|
|
157
|
+
if (ev._transitions === undefined || ev._transitions.length > 0) return undefined;
|
|
158
|
+
const snapshot = (ev.snapshot ?? ev.actorRef.getSnapshot()) as { value?: unknown; status?: string } | undefined;
|
|
159
|
+
if (snapshot?.status !== undefined && snapshot.status !== "active") return undefined;
|
|
160
|
+
const machine = (ev.actorRef.logic as { id?: unknown } | undefined)?.id;
|
|
161
|
+
const name = typeof machine === "string" ? machine : ev.actorRef.id;
|
|
162
|
+
const reason = typeof ev.event.reason === "string" && ev.event.reason !== "" ? ev.event.reason : "no reason given";
|
|
163
|
+
return (
|
|
164
|
+
`machine "${name}" got "${type}" in state ${JSON.stringify(snapshot?.value ?? null)}, and no transition took ` +
|
|
165
|
+
`it (${reason}) — no route for "${type}" is on that state or on a state above, or the guard of each route ` +
|
|
166
|
+
`was false: route it with a transition that is always taken (ADR-0074)`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Made from @earendil-works/pi-ai 1.4.0 by `just model-keys` (packages/harness/scripts/model-keys.ts).
|
|
2
|
+
// Do not edit: run the recipe at each pi bump. `model-keys.ts` says what the table means (ADR-0059).
|
|
3
|
+
|
|
4
|
+
import type { ModelKey } from "./model-keys.ts";
|
|
5
|
+
|
|
6
|
+
export const MODEL_KEYS: Readonly<Record<string, ModelKey>> = {
|
|
7
|
+
// No default host: the calls go to more than one host: bedrock-runtime.us-east-1.amazonaws.com, bedrock-runtime.eu-central-1.amazonaws.com.
|
|
8
|
+
AWS_BEARER_TOKEN_BEDROCK: { providers: ["amazon-bedrock"] },
|
|
9
|
+
ANT_LING_API_KEY: { providers: ["ant-ling"], host: "api.ant-ling.com" },
|
|
10
|
+
ANTHROPIC_AUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
11
|
+
ANTHROPIC_OAUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
12
|
+
ANTHROPIC_API_KEY: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
13
|
+
// No default host: the catalog names no base URL.
|
|
14
|
+
AZURE_OPENAI_API_KEY: { providers: ["azure"] },
|
|
15
|
+
BASETEN_API_KEY: { providers: ["baseten"], host: "inference.baseten.co" },
|
|
16
|
+
CEREBRAS_API_KEY: { providers: ["cerebras"], host: "api.cerebras.ai" },
|
|
17
|
+
// No default host: a base URL is a template.
|
|
18
|
+
CLOUDFLARE_API_KEY: { providers: ["cloudflare-ai-gateway", "cloudflare-workers-ai"] },
|
|
19
|
+
DEEPSEEK_API_KEY: { providers: ["deepseek"], host: "api.deepseek.com" },
|
|
20
|
+
FIREWORKS_API_KEY: { providers: ["fireworks"], host: "api.fireworks.ai" },
|
|
21
|
+
COPILOT_GITHUB_TOKEN: { providers: ["github-copilot"], host: "api.individual.githubcopilot.com" },
|
|
22
|
+
GEMINI_API_KEY: { providers: ["google"], host: "generativelanguage.googleapis.com" },
|
|
23
|
+
// No default host: a base URL is a template.
|
|
24
|
+
GOOGLE_CLOUD_API_KEY: { providers: ["google-vertex"] },
|
|
25
|
+
GROQ_API_KEY: { providers: ["groq"], host: "api.groq.com" },
|
|
26
|
+
HF_TOKEN: { providers: ["huggingface"], host: "router.huggingface.co" },
|
|
27
|
+
KIMI_API_KEY: { providers: ["kimi-coding"], host: "api.kimi.com" },
|
|
28
|
+
META_API_KEY: { providers: ["meta"], host: "api.meta.ai" },
|
|
29
|
+
MINIMAX_API_KEY: { providers: ["minimax"], host: "api.minimax.io" },
|
|
30
|
+
MINIMAX_CN_API_KEY: { providers: ["minimax-cn"], host: "api.minimaxi.com" },
|
|
31
|
+
MISTRAL_API_KEY: { providers: ["mistral"], host: "api.mistral.ai" },
|
|
32
|
+
// No default host: the calls go to more than one host: api.moonshot.ai, api.moonshot.cn.
|
|
33
|
+
MOONSHOT_API_KEY: { providers: ["moonshotai", "moonshotai-cn"] },
|
|
34
|
+
NVIDIA_API_KEY: { providers: ["nvidia"], host: "integrate.api.nvidia.com" },
|
|
35
|
+
OPENAI_API_KEY: { providers: ["openai"], host: "api.openai.com" },
|
|
36
|
+
OPENCODE_API_KEY: { providers: ["opencode", "opencode-go"], host: "opencode.ai" },
|
|
37
|
+
OPENROUTER_API_KEY: { providers: ["openrouter"], host: "openrouter.ai" },
|
|
38
|
+
QWEN_TOKEN_PLAN_API_KEY: {
|
|
39
|
+
providers: ["qwen-token-plan", "qwen-token-plan-individual"],
|
|
40
|
+
host: "token-plan.ap-southeast-1.maas.aliyuncs.com",
|
|
41
|
+
},
|
|
42
|
+
QWEN_TOKEN_PLAN_CN_API_KEY: { providers: ["qwen-token-plan-cn"], host: "token-plan.cn-beijing.maas.aliyuncs.com" },
|
|
43
|
+
RADIUS_API_KEY: { providers: ["radius"], host: "radius.pi.dev" },
|
|
44
|
+
TOGETHER_API_KEY: { providers: ["together"], host: "api.together.ai" },
|
|
45
|
+
AI_GATEWAY_API_KEY: { providers: ["vercel-ai-gateway"], host: "ai-gateway.vercel.sh" },
|
|
46
|
+
XAI_API_KEY: { providers: ["xai"], host: "api.x.ai" },
|
|
47
|
+
XIAOMI_API_KEY: { providers: ["xiaomi"], host: "api.xiaomimimo.com" },
|
|
48
|
+
XIAOMI_TOKEN_PLAN_AMS_API_KEY: { providers: ["xiaomi-token-plan-ams"], host: "token-plan-ams.xiaomimimo.com" },
|
|
49
|
+
XIAOMI_TOKEN_PLAN_CN_API_KEY: { providers: ["xiaomi-token-plan-cn"], host: "token-plan-cn.xiaomimimo.com" },
|
|
50
|
+
XIAOMI_TOKEN_PLAN_SGP_API_KEY: { providers: ["xiaomi-token-plan-sgp"], host: "token-plan-sgp.xiaomimimo.com" },
|
|
51
|
+
ZAI_API_KEY: { providers: ["zai"], host: "api.z.ai" },
|
|
52
|
+
ZAI_CODING_CN_API_KEY: { providers: ["zai-coding-cn"], host: "open.bigmodel.cn" },
|
|
53
|
+
};
|
package/src/model-keys.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
// The known model keys (ADR-0059): every env var pi's catalog reads a provider's credential from,
|
|
2
2
|
// the catalog provider(s) that read it, and the one host those providers' calls go to by default.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
// dependency and must not grow one: `jr2 up` reads this table host-side (R1, and
|
|
6
|
-
// `hosts` of a held secret), and the in-cluster Orchestrator never touches pi. The
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
4
|
+
// pi's data at the exact pin `@jr2/harness` holds, made into plain data by a script, because this
|
|
5
|
+
// package has no pi dependency and must not grow one: `jr2 up` reads this table host-side (R1, and
|
|
6
|
+
// the default `hosts` of a held secret), and the in-cluster Orchestrator never touches pi. The table
|
|
7
|
+
// is `model-keys.generated.ts`, and `just model-keys` writes it from pi-ai at each pi bump
|
|
8
|
+
// (`packages/harness/scripts/model-keys.ts`). It cannot drift silently: `@jr2/harness`'s
|
|
9
|
+
// `model-keys.test.ts` checks it against pi itself, env names per provider and every provider's
|
|
10
|
+
// default host, in the default gate. A pi bump that adds or moves a provider fails there first, the
|
|
11
|
+
// same canary role Harness conformance plays (ADR-0027).
|
|
10
12
|
//
|
|
11
13
|
// `host` is present only when every model of every provider that reads the name goes to ONE static
|
|
12
14
|
// host. It is absent when there is none: the base URL is a template (`{location}`, an account id),
|
|
13
15
|
// the providers disagree (MOONSHOT_API_KEY serves two regions), or the host is regional (Bedrock).
|
|
14
16
|
// A held secret with such a name must then say its `hosts` itself (§3.4 of the spec, R5).
|
|
17
|
+
//
|
|
18
|
+
// Two facts the data does not say. anthropic reads three names: AUTH_TOKEN goes as `Authorization:
|
|
19
|
+
// Bearer`, the other two as `x-api-key` (an `sk-ant-oat` value is an OAuth token, which a Stand-in
|
|
20
|
+
// never is). AWS_BEARER_TOKEN_BEDROCK is the bearer form of Bedrock auth, which pi reads through
|
|
21
|
+
// its credential chain; SigV4 credentials cannot be held at all — a signature is not a header a
|
|
22
|
+
// Stand-in can be swapped into — and `jr2 up` warns on them (`SIGNING_CREDENTIALS`).
|
|
23
|
+
|
|
24
|
+
import { MODEL_KEYS } from "./model-keys.generated.ts";
|
|
25
|
+
|
|
26
|
+
export { MODEL_KEYS };
|
|
15
27
|
|
|
16
28
|
/** One known model-key env name. */
|
|
17
29
|
export type ModelKey = {
|
|
@@ -21,58 +33,6 @@ export type ModelKey = {
|
|
|
21
33
|
host?: string;
|
|
22
34
|
};
|
|
23
35
|
|
|
24
|
-
export const MODEL_KEYS: Readonly<Record<string, ModelKey>> = {
|
|
25
|
-
// anthropic reads three names. AUTH_TOKEN goes as `Authorization: Bearer`, the other two as
|
|
26
|
-
// `x-api-key` (an `sk-ant-oat` value is an OAuth token, which a Stand-in never is).
|
|
27
|
-
ANTHROPIC_API_KEY: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
28
|
-
ANTHROPIC_AUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
29
|
-
ANTHROPIC_OAUTH_TOKEN: { providers: ["anthropic"], host: "api.anthropic.com" },
|
|
30
|
-
OPENAI_API_KEY: { providers: ["openai"], host: "api.openai.com" },
|
|
31
|
-
GEMINI_API_KEY: { providers: ["google"], host: "generativelanguage.googleapis.com" },
|
|
32
|
-
// `https://{location}-aiplatform.googleapis.com` — regional, so no default.
|
|
33
|
-
GOOGLE_CLOUD_API_KEY: { providers: ["google-vertex"] },
|
|
34
|
-
COPILOT_GITHUB_TOKEN: { providers: ["github-copilot"], host: "api.individual.githubcopilot.com" },
|
|
35
|
-
// The bearer form of Bedrock auth. Regional, so no default. (SigV4 credentials cannot be held at
|
|
36
|
-
// all — a signature is not a header a Stand-in can be swapped into — and `jr2 up` warns on them.)
|
|
37
|
-
AWS_BEARER_TOKEN_BEDROCK: { providers: ["amazon-bedrock"] },
|
|
38
|
-
ANT_LING_API_KEY: { providers: ["ant-ling"], host: "api.ant-ling.com" },
|
|
39
|
-
QWEN_TOKEN_PLAN_API_KEY: {
|
|
40
|
-
providers: ["qwen-token-plan"],
|
|
41
|
-
host: "token-plan.ap-southeast-1.maas.aliyuncs.com",
|
|
42
|
-
},
|
|
43
|
-
QWEN_TOKEN_PLAN_CN_API_KEY: { providers: ["qwen-token-plan-cn"], host: "token-plan.cn-beijing.maas.aliyuncs.com" },
|
|
44
|
-
// The catalog names no base URL: an Azure deployment is the user's own resource.
|
|
45
|
-
AZURE_OPENAI_API_KEY: { providers: ["azure-openai-responses"] },
|
|
46
|
-
NVIDIA_API_KEY: { providers: ["nvidia"], host: "integrate.api.nvidia.com" },
|
|
47
|
-
DEEPSEEK_API_KEY: { providers: ["deepseek"], host: "api.deepseek.com" },
|
|
48
|
-
GROQ_API_KEY: { providers: ["groq"], host: "api.groq.com" },
|
|
49
|
-
CEREBRAS_API_KEY: { providers: ["cerebras"], host: "api.cerebras.ai" },
|
|
50
|
-
XAI_API_KEY: { providers: ["xai"], host: "api.x.ai" },
|
|
51
|
-
// pi reads the name, and its catalog lists no model for the provider at this pin.
|
|
52
|
-
RADIUS_API_KEY: { providers: ["radius"] },
|
|
53
|
-
OPENROUTER_API_KEY: { providers: ["openrouter"], host: "openrouter.ai" },
|
|
54
|
-
AI_GATEWAY_API_KEY: { providers: ["vercel-ai-gateway"], host: "ai-gateway.vercel.sh" },
|
|
55
|
-
ZAI_API_KEY: { providers: ["zai"], host: "api.z.ai" },
|
|
56
|
-
ZAI_CODING_CN_API_KEY: { providers: ["zai-coding-cn"], host: "open.bigmodel.cn" },
|
|
57
|
-
MISTRAL_API_KEY: { providers: ["mistral"], host: "api.mistral.ai" },
|
|
58
|
-
MINIMAX_API_KEY: { providers: ["minimax"], host: "api.minimax.io" },
|
|
59
|
-
MINIMAX_CN_API_KEY: { providers: ["minimax-cn"], host: "api.minimaxi.com" },
|
|
60
|
-
// Two providers, two hosts (`.ai` and `.cn`): no single default.
|
|
61
|
-
MOONSHOT_API_KEY: { providers: ["moonshotai", "moonshotai-cn"] },
|
|
62
|
-
HF_TOKEN: { providers: ["huggingface"], host: "router.huggingface.co" },
|
|
63
|
-
FIREWORKS_API_KEY: { providers: ["fireworks"], host: "api.fireworks.ai" },
|
|
64
|
-
TOGETHER_API_KEY: { providers: ["together"], host: "api.together.ai" },
|
|
65
|
-
// Two providers, one host.
|
|
66
|
-
OPENCODE_API_KEY: { providers: ["opencode", "opencode-go"], host: "opencode.ai" },
|
|
67
|
-
KIMI_API_KEY: { providers: ["kimi-coding"], host: "api.kimi.com" },
|
|
68
|
-
// Account-templated base URLs.
|
|
69
|
-
CLOUDFLARE_API_KEY: { providers: ["cloudflare-workers-ai", "cloudflare-ai-gateway"] },
|
|
70
|
-
XIAOMI_API_KEY: { providers: ["xiaomi"], host: "api.xiaomimimo.com" },
|
|
71
|
-
XIAOMI_TOKEN_PLAN_CN_API_KEY: { providers: ["xiaomi-token-plan-cn"], host: "token-plan-cn.xiaomimimo.com" },
|
|
72
|
-
XIAOMI_TOKEN_PLAN_AMS_API_KEY: { providers: ["xiaomi-token-plan-ams"], host: "token-plan-ams.xiaomimimo.com" },
|
|
73
|
-
XIAOMI_TOKEN_PLAN_SGP_API_KEY: { providers: ["xiaomi-token-plan-sgp"], host: "token-plan-sgp.xiaomimimo.com" },
|
|
74
|
-
};
|
|
75
|
-
|
|
76
36
|
/** Every catalog provider id some known model key belongs to — the set `harness.catalog` may name. */
|
|
77
37
|
export const MODEL_KEY_PROVIDERS: ReadonlySet<string> = new Set(
|
|
78
38
|
Object.values(MODEL_KEYS).flatMap((key) => key.providers),
|