@jr2/orchestrator 0.4.1 → 0.6.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/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 +478 -0
- package/docs/concepts/architecture.md +243 -0
- package/docs/concepts/charts.md +146 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +257 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +658 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +298 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +306 -0
- package/docs/concepts/the-menu.md +381 -0
- package/docs/concepts/workspaces.md +756 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +383 -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 +993 -0
- package/docs/get-started/install.md +130 -0
- package/docs/guides/add-tools-to-the-sandbox.md +308 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +224 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/cut-a-worktree.md +560 -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 +230 -0
- package/docs/guides/look-inside-a-sandbox.md +177 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -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 +532 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +587 -0
- package/docs/guides/run-one-worker-per-item.md +322 -0
- package/docs/guides/set-an-agents-access-and-dials.md +420 -0
- package/docs/guides/size-a-workspace.md +217 -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 +214 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +349 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +237 -0
- package/docs/reference/authoring-surface/agent.md +151 -0
- package/docs/reference/authoring-surface/answer.md +156 -0
- package/docs/reference/authoring-surface/arg.md +82 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/branchname.md +15 -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 +206 -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 +96 -0
- package/docs/reference/authoring-surface/file.md +142 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +259 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +105 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +143 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +130 -0
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +139 -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 +15 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -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-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +61 -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 +20 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
- package/docs/reference/authoring-surface/type-customize.md +111 -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 +72 -0
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -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 +37 -0
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -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 +95 -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-lateroutcome.md +21 -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-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -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 +22 -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 +26 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +34 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +149 -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 +84 -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 +24 -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 +115 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +22 -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 +27 -0
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +287 -0
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +217 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +765 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +50 -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 +175 -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 +271 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -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 +287 -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 +101 -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 +175 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -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 +245 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -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 +193 -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 +126 -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 +238 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -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 +108 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -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 +239 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -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 +552 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -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 +334 -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/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +86 -0
- package/docs/reference/glossary.md +687 -0
- package/docs/reference/http-api.md +1386 -0
- package/docs/reference/machines.md +189 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +207 -0
- package/package.json +5 -3
- package/src/actor.ts +694 -106
- package/src/agent.ts +114 -38
- package/src/ambient.ts +3 -4
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +345 -46
- package/src/charts.ts +43 -8
- package/src/config.ts +440 -178
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +325 -99
- package/src/customize.ts +199 -49
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +641 -128
- package/src/effector-wire.ts +165 -13
- package/src/fail.ts +222 -0
- package/src/harness-client.ts +587 -50
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +556 -58
- package/src/http.ts +480 -30
- package/src/index.ts +74 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +85 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +24 -7
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +42 -58
- package/src/names.ts +19 -6
- package/src/notices.ts +483 -41
- package/src/open-body.ts +242 -40
- package/src/open-effect.ts +81 -8
- package/src/open.ts +90 -6
- package/src/parts.ts +213 -30
- package/src/party.ts +101 -2
- package/src/pick.ts +497 -11
- package/src/pool.ts +282 -50
- package/src/registration.ts +133 -12
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +22 -3
- package/src/run-host.ts +665 -45
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +457 -71
- package/src/sandbox-kube.ts +66 -17
- package/src/secret-placement.ts +59 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +325 -62
- 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/tokens.ts +107 -32
- package/src/vocabulary.ts +88 -14
- package/src/wire.ts +100 -26
- package/src/workspace.ts +771 -238
- package/src/worktree.ts +249 -0
package/src/chart.ts
CHANGED
|
@@ -19,10 +19,12 @@
|
|
|
19
19
|
// (open-body.ts), so that `jr2Setup`'s own refusals and load warnings are lines of the same report.
|
|
20
20
|
|
|
21
21
|
import { createHash } from "node:crypto";
|
|
22
|
+
import { posix } from "node:path";
|
|
22
23
|
import { z } from "zod";
|
|
23
24
|
import { defineEvent, type EventAudience, type EventDef } from "@jr2/agent-protocol";
|
|
24
25
|
import type { AgentDefinition, ThinkingLevel, WorkspaceAccess } from "./agent.ts";
|
|
25
|
-
import { inputKeysOf, isEffect } from "./effect.ts";
|
|
26
|
+
import { inputKeysOf, inputShapeOf, isEffect, type InputShape } from "./effect.ts";
|
|
27
|
+
import { FAILURE_GUARD } from "./fail.ts";
|
|
26
28
|
import { compileChart, durationMs, type LentParts, type OpenBodyDeclaration } from "./open-body.ts";
|
|
27
29
|
import { SENT_BY } from "./party.ts";
|
|
28
30
|
import { loadWarningsOf } from "./vocabulary.ts";
|
|
@@ -38,7 +40,8 @@ export type ChartJson = string | number | boolean | null | ChartJson[] | { [key:
|
|
|
38
40
|
*/
|
|
39
41
|
export type ChartValue = ChartJson;
|
|
40
42
|
|
|
41
|
-
/** A guard: a lent guard by name, with or without params, `jr2.sentBy`,
|
|
43
|
+
/** A guard: a lent guard by name, with or without params, `jr2.sentBy`, `jr2.failure`, or
|
|
44
|
+
* `not`/`and`/`or`. */
|
|
42
45
|
export type ChartGuard =
|
|
43
46
|
| string
|
|
44
47
|
| { type: string; params?: ChartValue }
|
|
@@ -71,7 +74,7 @@ export type ChartTransition =
|
|
|
71
74
|
/** One transition or a list, in xstate's order. */
|
|
72
75
|
export type ChartTransitions = ChartTransition | ChartTransition[];
|
|
73
76
|
|
|
74
|
-
/** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate`. */
|
|
77
|
+
/** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate` or `fail`. */
|
|
75
78
|
export type ChartInvoke = {
|
|
76
79
|
src: string;
|
|
77
80
|
id?: string;
|
|
@@ -138,6 +141,10 @@ const MECHANISM_EVENTS: Record<string, { fields: Record<string, boolean> }> = {
|
|
|
138
141
|
* placement; see {@link checkAgentInput}). */
|
|
139
142
|
const AGENT_INPUT_KEYS = ["prompt", "cwd", "continue", "model", "thinkingLevel"] as const;
|
|
140
143
|
|
|
144
|
+
/** The kit's test for a cut that settled `ready`, in every Chart beside `jr2.failure`: a cut's own
|
|
145
|
+
* outcomes are output, so a Chart routes on them, and a Chart has no other way to read one. */
|
|
146
|
+
export const READY_GUARD = "jr2.ready";
|
|
147
|
+
|
|
141
148
|
/**
|
|
142
149
|
* The Chart briefing (ADR-0070): jr2's text on how a Chart runs, for whoever writes one. It is the
|
|
143
150
|
* grammar's root description, so it rides `GET /workflows/<wrapper>/chart-schema` and a pick's tool
|
|
@@ -158,6 +165,20 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
158
165
|
? ' `src: "gate"` is the kit\'s Gate: the Chart waits there for an outside answer (a human, a webhook), ' +
|
|
159
166
|
'one of its `events` with `audience: "external"`, not an Agent\'s Turn.'
|
|
160
167
|
: "";
|
|
168
|
+
const fail = narrow?.fail
|
|
169
|
+
? ' `src: "fail"` with `input: { reason }` ends the run as a Failure: work that did not succeed, with ' +
|
|
170
|
+
`the reason for a person; an \`onError\` with the guard \`${FAILURE_GUARD}\` catches it. A final state means done.`
|
|
171
|
+
: "";
|
|
172
|
+
// A declaration is names alone, so this says "a lent Effect that cuts".
|
|
173
|
+
const cut =
|
|
174
|
+
narrow === undefined || narrow.lendsEffects
|
|
175
|
+
? ' In a Workspace with Repo Slots, work happens in a Worktree. A lent Effect that cuts one takes `input: { path: "/work/<slot>/<name>", ' +
|
|
176
|
+
'branch: "<branch>" }`: `<name>` is one folder name that you choose, and it is not the branch. The Open body sets the cut\'s `expect`, ' +
|
|
177
|
+
"so its input has no `expect`. Give its `onDone` two edges: the guard " +
|
|
178
|
+
`\`${READY_GUARD}\` on the one that goes on, and one with no guard for a refused cut. Then write that same literal path as \`cwd\` in an ` +
|
|
179
|
+
"Agent's `input` and as `worktree` in an Effect's `input`: each Turn and each Effect there must say where it works. " +
|
|
180
|
+
"The slot's folder `/work/<slot>` is not a Worktree."
|
|
181
|
+
: "";
|
|
161
182
|
const agents = mints
|
|
162
183
|
? `A state runs an Agent with \`invoke: { src, input: { prompt } }\`: \`src\` is a lent Agent's name${lent}. ` +
|
|
163
184
|
"This Chart mints no Agent of its own: there is no `agents`."
|
|
@@ -165,7 +186,7 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
165
186
|
"a key of `agents`; a lent Agent needs no entry in `agents`, which mints new ones.";
|
|
166
187
|
return (
|
|
167
188
|
`A Chart is a workflow written as data: states, the events its Agents pick${mints ? "" : ", and the Agents it mints"}. ` +
|
|
168
|
-
`${agents}${gate} An Agent's Turn ends only when it ` +
|
|
189
|
+
`${agents}${gate}${fail}${cut} An Agent's Turn ends only when it ` +
|
|
169
190
|
"picks one of the Chart's `events`: declare each pick in `events` with an `input` schema " +
|
|
170
191
|
'(`{ "type": "object", "properties": { "<field>": { "type": "string" } }, "required": ["<field>"] }`; a field not in ' +
|
|
171
192
|
"`required` may be absent), and " +
|
|
@@ -232,7 +253,9 @@ export function pathSegments(path: string): Array<string | number> {
|
|
|
232
253
|
type Narrow = {
|
|
233
254
|
srcs: string[];
|
|
234
255
|
lentAgents: string[];
|
|
256
|
+
lendsEffects: boolean;
|
|
235
257
|
gate: boolean;
|
|
258
|
+
fail: boolean;
|
|
236
259
|
guards: string[];
|
|
237
260
|
actions: string[];
|
|
238
261
|
delays: string[];
|
|
@@ -353,16 +376,25 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
353
376
|
})
|
|
354
377
|
.describe('A context path without the "context." root, like "round" or "ticket.notes".');
|
|
355
378
|
|
|
356
|
-
// `jr2.sentBy` is the kit's routing guard (ADR-0015), and
|
|
379
|
+
// `jr2.sentBy` is the kit's routing guard (ADR-0015), and `jr2.failure` its test for a Failure on
|
|
380
|
+
// an `onError` (ADR-0073): every Chart has both, and neither is lent.
|
|
357
381
|
const sentByArm = z.strictObject({ type: z.literal(SENT_BY), params: z.strictObject({ id: z.string() }) });
|
|
382
|
+
const failureArm = z.strictObject({ type: z.union([z.literal(FAILURE_GUARD), z.literal(READY_GUARD)]) });
|
|
358
383
|
const lentGuardName = narrow
|
|
359
|
-
? enumOf(narrow.guards)
|
|
384
|
+
? enumOf([...narrow.guards, FAILURE_GUARD, READY_GUARD])
|
|
360
385
|
: z.string().refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` });
|
|
386
|
+
const paramGuardName = narrow
|
|
387
|
+
? enumOf(narrow.guards)
|
|
388
|
+
: z
|
|
389
|
+
.string()
|
|
390
|
+
.refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` })
|
|
391
|
+
.refine((t) => t !== FAILURE_GUARD && t !== READY_GUARD, { message: "that guard takes no params" });
|
|
361
392
|
const guard: z.ZodType<ChartGuard> = z
|
|
362
393
|
.union([
|
|
363
394
|
lentGuardName,
|
|
364
395
|
sentByArm,
|
|
365
|
-
|
|
396
|
+
failureArm,
|
|
397
|
+
z.strictObject({ type: paramGuardName, params: template.optional() }),
|
|
366
398
|
z.strictObject({
|
|
367
399
|
get not() {
|
|
368
400
|
return guard;
|
|
@@ -381,7 +413,7 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
381
413
|
])
|
|
382
414
|
.meta({
|
|
383
415
|
id: `${ID_PREFIX}ChartGuard`,
|
|
384
|
-
description:
|
|
416
|
+
description: `A lent guard by name, \`jr2.sentBy\`, \`${FAILURE_GUARD}\`, \`${READY_GUARD}\`, or not/and/or of guards.`,
|
|
385
417
|
}) as never;
|
|
386
418
|
|
|
387
419
|
const lentActionName = narrow
|
|
@@ -425,9 +457,14 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
425
457
|
: z.string();
|
|
426
458
|
const invoke = z
|
|
427
459
|
.strictObject({
|
|
460
|
+
// A narrowed grammar names `fail` only where the body lends it: a model reads only what it can use.
|
|
428
461
|
src: src.describe(
|
|
429
|
-
|
|
430
|
-
"
|
|
462
|
+
narrow && !narrow.fail
|
|
463
|
+
? "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
|
|
464
|
+
"webhook) where the body lends it; or a key of this Chart's `agents`."
|
|
465
|
+
: "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
|
|
466
|
+
"webhook) or `fail` (ends the run as a Failure, with `input: { reason }`) where the body lends it; or a " +
|
|
467
|
+
"key of this Chart's `agents`.",
|
|
431
468
|
),
|
|
432
469
|
id: name("invoke id").optional(),
|
|
433
470
|
input: template.optional().describe("An Agent takes `{ prompt }`; an Effect or a Machine takes its own input."),
|
|
@@ -526,9 +563,17 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
526
563
|
.describe(chartBriefing(narrow)) as unknown as z.ZodType<ChartText>;
|
|
527
564
|
}
|
|
528
565
|
|
|
566
|
+
// A door field or a pick param that IS this schema is a Chart field: `isChartSchema` tells, and
|
|
567
|
+
// jr2 reads a Chart wrapper's one-shot door off it (ADR-0070).
|
|
529
568
|
/**
|
|
530
|
-
* The Chart grammar
|
|
531
|
-
*
|
|
569
|
+
* The Chart grammar, as a zod schema. Make a field of the run input of a Chart wrapper this schema,
|
|
570
|
+
* as in `z.object({ topic: z.string(), chart: chartSchema })`. Then a run input can carry a Chart,
|
|
571
|
+
* and the wrapper can keep Charts under names: Chart files in `workflows/<wrapper>/`, and runtime
|
|
572
|
+
* Charts. For the pick of a planner, use `chartSchemaFor`, which knows the names that the Open body
|
|
573
|
+
* lends.
|
|
574
|
+
*
|
|
575
|
+
* @see docs/guides/run-a-chart-by-name.md
|
|
576
|
+
* @see docs/concepts/charts.md
|
|
532
577
|
*/
|
|
533
578
|
export const chartSchema: z.ZodType<ChartText> = guarded(grammar());
|
|
534
579
|
|
|
@@ -578,13 +623,38 @@ export function chartBriefingOf(schema: unknown): string | undefined {
|
|
|
578
623
|
|
|
579
624
|
const narrowedSchemas = new WeakMap<OpenBodyDeclaration, z.ZodType<ChartText>>();
|
|
580
625
|
|
|
626
|
+
// Either way it is a Chart field (`isChartSchema`), and the door still admits the Chart against
|
|
627
|
+
// the lent set the body has at its start. One schema per declaration (ADR-0070).
|
|
581
628
|
/**
|
|
582
|
-
* The Chart grammar
|
|
583
|
-
*
|
|
584
|
-
* pick
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
629
|
+
* The Chart grammar for one Open body, as a zod schema. The names that the Open body lends, and the
|
|
630
|
+
* models and the Workspace accesses that it allows, are the only values that the schema takes.
|
|
631
|
+
* Make it the parameter of the pick of a planner. The model then gets the grammar as the
|
|
632
|
+
* parameters of its tool, and the Briefing tells it how a Chart runs. The door still checks each
|
|
633
|
+
* Chart before it runs.
|
|
634
|
+
*
|
|
635
|
+
* @param decl - The declaration of the Open body that the Chart binds.
|
|
636
|
+
* @returns The schema of a Chart for that Open body.
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```ts
|
|
640
|
+
* import { z } from "zod";
|
|
641
|
+
* import { chartSchemaFor, defineEvent, type OpenBodyDeclaration } from "@jr2/orchestrator";
|
|
642
|
+
*
|
|
643
|
+
* const declaration: OpenBodyDeclaration = {
|
|
644
|
+
* lends: { agents: ["writer"], gate: true },
|
|
645
|
+
* allows: { models: ["anthropic/claude-haiku-5-5"], workspace: ["none"] },
|
|
646
|
+
* ceilings: { turns: 12, wallClock: "30m", transitions: 500, fanOut: 2, size: 65536 },
|
|
647
|
+
* };
|
|
648
|
+
*
|
|
649
|
+
* export const runChart = defineEvent({
|
|
650
|
+
* name: "run_chart",
|
|
651
|
+
* description: "Run this Chart. Its outcome is the answer to this call.",
|
|
652
|
+
* input: z.object({ chart: chartSchemaFor(declaration) }),
|
|
653
|
+
* });
|
|
654
|
+
* ```
|
|
655
|
+
*
|
|
656
|
+
* @see docs/guides/let-an-agent-write-a-chart.md
|
|
657
|
+
* @see docs/concepts/charts.md
|
|
588
658
|
*/
|
|
589
659
|
export function chartSchemaFor(decl: OpenBodyDeclaration): z.ZodType<ChartText> {
|
|
590
660
|
const hit = narrowedSchemas.get(decl);
|
|
@@ -607,6 +677,7 @@ function lendsOf(decl: OpenBodyDeclaration) {
|
|
|
607
677
|
actions: decl.lends.actions ?? [],
|
|
608
678
|
delays: decl.lends.delays ?? [],
|
|
609
679
|
gate: decl.lends.gate === true,
|
|
680
|
+
fail: decl.lends.fail === true,
|
|
610
681
|
};
|
|
611
682
|
}
|
|
612
683
|
|
|
@@ -624,9 +695,11 @@ export function narrowedChartSchema(decl: OpenBodyDeclaration): Record<string, u
|
|
|
624
695
|
function narrowOf(decl: OpenBodyDeclaration): Narrow {
|
|
625
696
|
const l = lendsOf(decl);
|
|
626
697
|
return {
|
|
627
|
-
srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : [])],
|
|
698
|
+
srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : []), ...(l.fail ? ["fail"] : [])],
|
|
628
699
|
lentAgents: l.agents,
|
|
700
|
+
lendsEffects: l.effects.length > 0,
|
|
629
701
|
gate: l.gate,
|
|
702
|
+
fail: l.fail,
|
|
630
703
|
guards: l.guards,
|
|
631
704
|
actions: l.actions,
|
|
632
705
|
delays: l.delays,
|
|
@@ -782,6 +855,17 @@ export type AdmittedChart = {
|
|
|
782
855
|
size: number;
|
|
783
856
|
};
|
|
784
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Why a Chart's `cwd` is not a place to work, or undefined when it can be: a Repo Slot's folder is no
|
|
860
|
+
* Worktree. There, an Agent's search hits `default/`, and a relative write lands outside each
|
|
861
|
+
* Worktree (ADR-0057, ADR-0075).
|
|
862
|
+
*/
|
|
863
|
+
export function slotFolderFault(cwd: string, slots: readonly string[]): string | undefined {
|
|
864
|
+
const slot = posix.normalize(cwd).match(/^\/work\/([^/]+)\/?$/)?.[1];
|
|
865
|
+
if (slot === undefined || !slots.includes(slot)) return undefined;
|
|
866
|
+
return `"${cwd}" is a Repo Slot's folder, not a Worktree; \`cwd\` is a Worktree that a cut made, "/work/${slot}/<name>", or "/work/${slot}/default" to read, or "/work"`;
|
|
867
|
+
}
|
|
868
|
+
|
|
785
869
|
/** What the door knows beyond the declaration and the lent set. */
|
|
786
870
|
export type AdmitOptions = {
|
|
787
871
|
/**
|
|
@@ -790,6 +874,11 @@ export type AdmitOptions = {
|
|
|
790
874
|
* `input.<key>` reference is checked against it; absent, `input` references are not checked.
|
|
791
875
|
*/
|
|
792
876
|
inputKeys?: readonly string[];
|
|
877
|
+
/** The Repo Slots of the Workspace around the Open body, where the door can read them. Given and
|
|
878
|
+
* not empty, a Turn's `cwd` and an Effect's `worktree` are required, a literal path's slot is
|
|
879
|
+
* checked, and a literal `cwd` cannot be a slot's folder. Absent: the start of a Turn or an Effect
|
|
880
|
+
* says the same of a missing `cwd`, of a Worktree path, and of a `cwd` that is a slot's folder. */
|
|
881
|
+
repoSlots?: readonly string[];
|
|
793
882
|
};
|
|
794
883
|
|
|
795
884
|
const MAX_STATES = 200;
|
|
@@ -867,6 +956,9 @@ function issueLines(
|
|
|
867
956
|
|
|
868
957
|
const pathText = (path: readonly PropertyKey[]): string => (path.length ? path.map(String).join(".") : "(chart)");
|
|
869
958
|
|
|
959
|
+
/** What an invoke's `src` names (rule 3). */
|
|
960
|
+
type InvokeKind = "agent" | "minted" | "effect" | "machine" | "gate" | "fail";
|
|
961
|
+
|
|
870
962
|
/** One state of the Chart, indexed for the checks. */
|
|
871
963
|
type Node = {
|
|
872
964
|
key: string;
|
|
@@ -932,6 +1024,13 @@ export function admitChart(
|
|
|
932
1024
|
return new Door(chart, decl, lent, options).admit(size);
|
|
933
1025
|
}
|
|
934
1026
|
|
|
1027
|
+
/** What an Effect's input takes, for a line of the door. */
|
|
1028
|
+
function shapeText(shape: InputShape): string {
|
|
1029
|
+
const need = [...shape.required, ...(shape.oneOf ? [`exactly one of ${shape.oneOf.join(", ")}`] : [])].join(", ");
|
|
1030
|
+
const may = shape.optional.length ? `${need ? ", and " : ""}optionally ${shape.optional.join(", ")}` : "";
|
|
1031
|
+
return need || may ? `${need}${may}` : "nothing";
|
|
1032
|
+
}
|
|
1033
|
+
|
|
935
1034
|
/** The checks from rule 3 on, over a Chart the grammar admitted. */
|
|
936
1035
|
class Door {
|
|
937
1036
|
private readonly lines: Line[] = [];
|
|
@@ -1064,11 +1163,12 @@ class Door {
|
|
|
1064
1163
|
private lentText(): string {
|
|
1065
1164
|
const actors = this.lentActors().map((a) => `${a.name} (${a.kind})`);
|
|
1066
1165
|
const minted = Object.keys(this.chart.agents ?? {});
|
|
1067
|
-
|
|
1166
|
+
const kit = [...(this.lends.gate ? ["gate"] : []), ...(this.lends.fail ? ["fail"] : [])];
|
|
1167
|
+
return `lent: ${names(actors)}; kit: ${kit.length ? kit.join(", ") : "none"}; minted: ${names(minted)}`;
|
|
1068
1168
|
}
|
|
1069
1169
|
|
|
1070
1170
|
/** What an invoke's `src` names, or undefined after a refusal line. */
|
|
1071
|
-
private srcKind(src: string, path: string):
|
|
1171
|
+
private srcKind(src: string, path: string): InvokeKind | undefined {
|
|
1072
1172
|
const lentNow = (name: string) => name in this.lent.actors;
|
|
1073
1173
|
const declared = [...this.lends.agents, ...this.lends.effects, ...this.lends.machines];
|
|
1074
1174
|
if (declared.includes(src) && !lentNow(src)) {
|
|
@@ -1089,6 +1189,14 @@ class Door {
|
|
|
1089
1189
|
);
|
|
1090
1190
|
return undefined;
|
|
1091
1191
|
}
|
|
1192
|
+
if (src === "fail") {
|
|
1193
|
+
if (this.lends.fail) return "fail";
|
|
1194
|
+
this.fail(
|
|
1195
|
+
3,
|
|
1196
|
+
`${path}: "fail" is the kit's Part that ends the run as a Failure, and this Open body does not lend it (\`lends.fail: true\` would); ${this.lentText()}`,
|
|
1197
|
+
);
|
|
1198
|
+
return undefined;
|
|
1199
|
+
}
|
|
1092
1200
|
if (Object.hasOwn(this.chart.agents ?? {}, src)) return "minted";
|
|
1093
1201
|
this.fail(3, `${path}: "${src}" is not a lent actor or an Agent of this Chart; ${this.lentText()}`);
|
|
1094
1202
|
return undefined;
|
|
@@ -1103,11 +1211,16 @@ class Door {
|
|
|
1103
1211
|
this.sentBys.push({ id: (guard.params as { id: string }).id, path: `${path}.params.id` });
|
|
1104
1212
|
return;
|
|
1105
1213
|
}
|
|
1214
|
+
if (guard.type === FAILURE_GUARD || guard.type === READY_GUARD) {
|
|
1215
|
+
if (guard.params !== undefined) this.fail(3, `${path}.params: "${guard.type}" takes no params`);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1106
1218
|
this.checkGuardName(guard.type, `${path}.type`);
|
|
1107
1219
|
if (guard.params !== undefined) this.checkRefs(guard.params, `${path}.params`, scope);
|
|
1108
1220
|
}
|
|
1109
1221
|
|
|
1110
1222
|
private checkGuardName(name: string, path: string): void {
|
|
1223
|
+
if (name === FAILURE_GUARD || name === READY_GUARD) return;
|
|
1111
1224
|
const lent = this.lends.guards.filter((g) => g in this.lent.guards);
|
|
1112
1225
|
if (this.lends.guards.includes(name) && !(name in this.lent.guards)) {
|
|
1113
1226
|
this.fail(
|
|
@@ -1117,7 +1230,7 @@ class Door {
|
|
|
1117
1230
|
} else if (!this.lends.guards.includes(name)) {
|
|
1118
1231
|
this.fail(
|
|
1119
1232
|
3,
|
|
1120
|
-
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id })`,
|
|
1233
|
+
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id }, ${FAILURE_GUARD} and ${READY_GUARD})`,
|
|
1121
1234
|
);
|
|
1122
1235
|
}
|
|
1123
1236
|
}
|
|
@@ -1262,7 +1375,7 @@ class Door {
|
|
|
1262
1375
|
// --- rule 5: minted Agents ------------------------------------------------------------------------
|
|
1263
1376
|
|
|
1264
1377
|
private checkAgents(): void {
|
|
1265
|
-
const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate"]);
|
|
1378
|
+
const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate", "fail"]);
|
|
1266
1379
|
for (const [slot, a] of Object.entries(this.chart.agents ?? {})) {
|
|
1267
1380
|
const path = `agents.${slot}`;
|
|
1268
1381
|
if (lentNames.has(slot)) {
|
|
@@ -1607,6 +1720,7 @@ class Door {
|
|
|
1607
1720
|
private checkParties(): void {
|
|
1608
1721
|
const agents = new Set<string>();
|
|
1609
1722
|
const gates = new Set<string>();
|
|
1723
|
+
const slotStates = new Map<string, Node[]>();
|
|
1610
1724
|
for (const node of this.nodes) {
|
|
1611
1725
|
const invokes = listAt(node.state.invoke, `${node.doc}.invoke`);
|
|
1612
1726
|
// A Gate's invoke id, as jr2Setup names an unnamed one: the state's key path.
|
|
@@ -1621,6 +1735,7 @@ class Door {
|
|
|
1621
1735
|
if (kind === "gate") gates.add(inv.id ?? (unnamed > 1 ? `${key}.${ordinal++}` : key));
|
|
1622
1736
|
if (kind !== "agent" && kind !== "minted") continue;
|
|
1623
1737
|
agents.add(inv.src);
|
|
1738
|
+
slotStates.set(inv.src, [...(slotStates.get(inv.src) ?? []), node]);
|
|
1624
1739
|
if (inv.onDone !== undefined) {
|
|
1625
1740
|
this.fail(
|
|
1626
1741
|
6,
|
|
@@ -1642,6 +1757,25 @@ class Door {
|
|
|
1642
1757
|
}
|
|
1643
1758
|
}
|
|
1644
1759
|
}
|
|
1760
|
+
// One Agent, two Turns at once: one sender and one conversation cannot hold both.
|
|
1761
|
+
// One line for each Agent: every state of it that can be active with another of them.
|
|
1762
|
+
for (const [slot, states] of slotStates) {
|
|
1763
|
+
const clash = new Set<Node>();
|
|
1764
|
+
for (let i = 0; i < states.length; i++) {
|
|
1765
|
+
for (let j = i + 1; j < states.length; j++) {
|
|
1766
|
+
const [a, b] = [states[i]!, states[j]!];
|
|
1767
|
+
const together =
|
|
1768
|
+
a === b || isAncestorOrSelf(a, b) || isAncestorOrSelf(b, a) || nearestCommon(a, b)?.kind === "parallel";
|
|
1769
|
+
if (together) clash.add(a).add(b);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
if (clash.size === 0) continue;
|
|
1773
|
+
const docs = [...clash].map((n) => n.doc).join(", ");
|
|
1774
|
+
this.fail(
|
|
1775
|
+
6,
|
|
1776
|
+
`${docs}: Agent "${slot}" would run two Turns at once: these states invoke it and can be active together (in parallel regions, one inside the other, or one state twice); an Agent runs one Turn at a time: invoke another Agent in one of them`,
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1645
1779
|
for (const { id, path } of this.sentBys) {
|
|
1646
1780
|
if (id.includes("{{")) {
|
|
1647
1781
|
this.fail(3, `${path}: a sentBy id is a literal party name, never a reference`);
|
|
@@ -1835,15 +1969,12 @@ class Door {
|
|
|
1835
1969
|
|
|
1836
1970
|
// --- rule 8: lent input shapes --------------------------------------------------------------------
|
|
1837
1971
|
|
|
1838
|
-
private checkInvokeInput(
|
|
1839
|
-
inv: ChartInvoke,
|
|
1840
|
-
kind: "agent" | "minted" | "effect" | "machine" | "gate",
|
|
1841
|
-
path: string,
|
|
1842
|
-
): void {
|
|
1972
|
+
private checkInvokeInput(inv: ChartInvoke, kind: InvokeKind, path: string): void {
|
|
1843
1973
|
const ipath = `${path}.input`;
|
|
1844
1974
|
const input = inv.input;
|
|
1845
1975
|
const isObject = typeof input === "object" && input !== null && !Array.isArray(input) && !("$ref" in input);
|
|
1846
1976
|
if (kind === "machine") return;
|
|
1977
|
+
if (kind === "fail") return this.checkFailInput(input, isObject, ipath);
|
|
1847
1978
|
if (kind === "gate") {
|
|
1848
1979
|
if (input === undefined) return;
|
|
1849
1980
|
if (!isObject) return this.fail(8, `${ipath}: a Gate's input is an object of gate (a name) and meta`);
|
|
@@ -1863,38 +1994,166 @@ class Door {
|
|
|
1863
1994
|
}
|
|
1864
1995
|
if (kind === "effect") {
|
|
1865
1996
|
const logic = this.lent.actors[inv.src];
|
|
1866
|
-
const
|
|
1997
|
+
const parts = isEffect(logic) ? logic.effect : undefined;
|
|
1998
|
+
const keys = parts ? inputKeysOf(parts) : undefined;
|
|
1999
|
+
if (keys === undefined && parts?.kind === "dispatch") {
|
|
2000
|
+
// A dispatch with no input schema has no keys to hold the Chart to: its input is the run's
|
|
2001
|
+
// body, and the callee's door is the check (ADR-0071). Its values are what any Effect's
|
|
2002
|
+
// input slot takes.
|
|
2003
|
+
if (input === undefined) return;
|
|
2004
|
+
if (!isObject) {
|
|
2005
|
+
return this.fail(
|
|
2006
|
+
8,
|
|
2007
|
+
`${ipath}: the lent dispatch "${inv.src}" takes an object, the run's body — the callee's door checks it`,
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
for (const [k, v] of Object.entries(input as Record<string, unknown>)) {
|
|
2011
|
+
if (typeof v !== "string" && !isRef(v))
|
|
2012
|
+
this.fail(8, `${ipath}.${k}: an Effect's input slot takes a string or a reference`);
|
|
2013
|
+
}
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
1867
2016
|
if (keys === undefined) {
|
|
1868
2017
|
return this.fail(
|
|
1869
2018
|
8,
|
|
1870
2019
|
`${ipath}: the lent Effect "${inv.src}" has no input keys the door can read (an Open argv nobody bound)`,
|
|
1871
2020
|
);
|
|
1872
2021
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2022
|
+
const shape = this.shapeHere(parts!);
|
|
2023
|
+
// A cut's `expect` is static, and a Chart cannot state it: an unstated one would let a planner's
|
|
2024
|
+
// name land on someone else's branch.
|
|
2025
|
+
if (parts?.kind === "command" && parts.cut && parts.cut.expect === undefined) {
|
|
2026
|
+
const fresh = this.lentNewCuts();
|
|
2027
|
+
this.fail(
|
|
2028
|
+
8,
|
|
2029
|
+
`${ipath}: the lent cut "${inv.src}" does not state \`expect\`, so a Chart cannot use it; ` +
|
|
2030
|
+
(fresh.length
|
|
2031
|
+
? `use a lent cut with \`expect: "new"\`: ${names(fresh)}`
|
|
2032
|
+
: `the Open body must state it (\`worktree({ expect: "new" })\` would); ${this.lentText()}`),
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
// A push publishes the branch of a cut's row, which stays in the run's ledger, so a Chart that
|
|
2036
|
+
// cuts and a later Chart that pushes are the same pair: the test is what is lent, not what this
|
|
2037
|
+
// Chart invokes.
|
|
2038
|
+
const force = this.lentForcePush();
|
|
2039
|
+
if (parts?.kind === "command" && parts.cut?.expect === "existing" && force !== undefined) {
|
|
2040
|
+
const fresh = this.lentNewCuts();
|
|
2041
|
+
this.fail(
|
|
2042
|
+
8,
|
|
2043
|
+
`${ipath}: the lent cut "${inv.src}" states \`expect: "existing"\` beside the lent force push "${force}", and together they can force-push any existing branch; ` +
|
|
2044
|
+
(fresh.length
|
|
2045
|
+
? `use a lent cut with \`expect: "new"\`: ${names(fresh)}`
|
|
2046
|
+
: `this Open body lends no cut with \`expect: "new"\`, so a Chart cannot cut here (\`worktree({ expect: "new" })\` would); ${this.lentText()}`),
|
|
2047
|
+
);
|
|
1876
2048
|
}
|
|
2049
|
+
if (input === undefined && shape.required.length === 0 && !shape.oneOf) return;
|
|
2050
|
+
const takes = shapeText(shape);
|
|
2051
|
+
if (!isObject) return this.fail(8, `${ipath}: the lent Effect "${inv.src}" takes an object: ${takes}`);
|
|
1877
2052
|
const given = Object.keys(input);
|
|
1878
|
-
const missing =
|
|
2053
|
+
const missing = shape.required.filter((k) => !given.includes(k));
|
|
1879
2054
|
const extra = given.filter((k) => !keys.includes(k));
|
|
1880
|
-
|
|
2055
|
+
const ofOne = shape.oneOf?.filter((k) => given.includes(k)).length ?? 1;
|
|
2056
|
+
if (missing.length || extra.length || ofOne !== 1) {
|
|
1881
2057
|
this.fail(
|
|
1882
2058
|
8,
|
|
1883
|
-
`${ipath}: the lent Effect "${inv.src}" takes
|
|
2059
|
+
`${ipath}: the lent Effect "${inv.src}" takes ${takes}` +
|
|
1884
2060
|
(missing.length ? `; missing: ${missing.join(", ")}` : "") +
|
|
1885
|
-
(extra.length ? `; not its input: ${extra.join(", ")}` : "")
|
|
2061
|
+
(extra.length ? `; not its input: ${extra.join(", ")}` : "") +
|
|
2062
|
+
(ofOne !== 1 ? `; give one of ${shape.oneOf!.join(", ")}` : ""),
|
|
1886
2063
|
);
|
|
1887
2064
|
}
|
|
2065
|
+
if (parts?.kind === "command" && parts.cut && given.includes("detach") && given.includes("from")) {
|
|
2066
|
+
this.fail(8, `${ipath}: a detached cut takes no \`from\``);
|
|
2067
|
+
}
|
|
1888
2068
|
for (const k of given.filter((g) => keys.includes(g))) {
|
|
1889
2069
|
const v = (input as Record<string, unknown>)[k];
|
|
1890
2070
|
if (typeof v !== "string" && !isRef(v))
|
|
1891
2071
|
this.fail(8, `${ipath}.${k}: an Effect's input slot takes a string or a reference`);
|
|
2072
|
+
else if ((k === "worktree" || k === "path") && typeof v === "string") this.checkLiteralPath(v, `${ipath}.${k}`);
|
|
1892
2073
|
}
|
|
1893
2074
|
return;
|
|
1894
2075
|
}
|
|
1895
2076
|
this.checkAgentInput(inv.src, kind, input, isObject, ipath);
|
|
1896
2077
|
}
|
|
1897
2078
|
|
|
2079
|
+
/** An Effect's shape here: where the door knows the Workspace has Repo Slots, `worktree` is
|
|
2080
|
+
* required on what can name one. Elsewhere the start says so (`drive`). */
|
|
2081
|
+
private shapeHere(parts: Parameters<typeof inputShapeOf>[0]): InputShape {
|
|
2082
|
+
const shape = inputShapeOf(parts)!;
|
|
2083
|
+
if (!this.options.repoSlots?.length || !shape.optional.includes("worktree")) return shape;
|
|
2084
|
+
return {
|
|
2085
|
+
...shape,
|
|
2086
|
+
required: [...shape.required, "worktree"],
|
|
2087
|
+
optional: shape.optional.filter((k) => k !== "worktree"),
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
/** The lent Effects, each with its parts. */
|
|
2092
|
+
private lentEffects(): Array<{ name: string; parts: Parameters<typeof inputShapeOf>[0] }> {
|
|
2093
|
+
return this.lends.effects.flatMap((name) => {
|
|
2094
|
+
const logic = this.lent.actors[name];
|
|
2095
|
+
return isEffect(logic) ? [{ name, parts: logic.effect }] : [];
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
/** The name of a lent push that forces, if one is lent. */
|
|
2100
|
+
private lentForcePush(): string | undefined {
|
|
2101
|
+
return this.lentEffects().find(({ parts }) => parts.kind === "push" && parts.force)?.name;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
/** The names of the lent cuts that make only a new branch. */
|
|
2105
|
+
private lentNewCuts(): string[] {
|
|
2106
|
+
return this.lentEffects()
|
|
2107
|
+
.filter(({ parts }) => parts.kind === "command" && parts.cut?.expect === "new")
|
|
2108
|
+
.map(({ name }) => name);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
/** A literal Worktree path: a wrong slot costs no pod. A path with a reference waits for its start. */
|
|
2112
|
+
private checkLiteralPath(value: string, path: string): void {
|
|
2113
|
+
if (referencesOf(value).length) return;
|
|
2114
|
+
const match = value.match(/^\/work\/([^/]+)\/([^/.-][^/]*)$/);
|
|
2115
|
+
const slots = this.options.repoSlots;
|
|
2116
|
+
if (match === null) this.fail(8, `${path}: "${value}" is not a Worktree path: /work/<slot>/<name>`);
|
|
2117
|
+
else if (slots !== undefined && !slots.includes(match[1]!)) {
|
|
2118
|
+
this.fail(8, `${path}: "${value}" names no Repo Slot of this Workspace; its Repo Slots: ${names([...slots])}`);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
/** A literal `cwd`, see {@link slotFolderFault}. A `cwd` with a reference waits for its value: the
|
|
2123
|
+
* Open body asks the same question when the Turn starts. */
|
|
2124
|
+
private checkCwd(value: string, path: string): void {
|
|
2125
|
+
if (referencesOf(value).length) return;
|
|
2126
|
+
const fault = slotFolderFault(value, this.options.repoSlots ?? []);
|
|
2127
|
+
if (fault !== undefined) this.fail(8, `${path}: ${fault}`);
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
/** The input of `fail` (ADR-0073): `{ reason, detail? }`, `reason` a text for a person (a string
|
|
2131
|
+
* or a reference), `detail` any value. */
|
|
2132
|
+
private checkFailInput(input: unknown, isObject: boolean, ipath: string): void {
|
|
2133
|
+
if (!isObject) {
|
|
2134
|
+
return this.fail(8, `${ipath}: \`fail\` takes { reason, detail? }: reason is a text for a person`);
|
|
2135
|
+
}
|
|
2136
|
+
for (const key of Object.keys(input as Record<string, unknown>)) {
|
|
2137
|
+
if (key !== "reason" && key !== "detail") {
|
|
2138
|
+
this.fail(8, `${ipath}.${key}: \`fail\` takes reason and detail only`);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
const reason = (input as Record<string, unknown>).reason;
|
|
2142
|
+
if (reason === undefined) this.fail(8, `${ipath}.reason: \`fail\` needs a reason, a text for a person`);
|
|
2143
|
+
else if ((typeof reason !== "string" || reason === "") && !isRef(reason)) {
|
|
2144
|
+
this.fail(8, `${ipath}.reason: a reason is a string that is not empty, or a reference`);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
/** Does this Agent read `/work`? A `workspace: "none"` one frames nothing. */
|
|
2149
|
+
private worksInWorkspace(src: string, kind: "agent" | "minted"): boolean {
|
|
2150
|
+
if (kind === "agent") {
|
|
2151
|
+
const logic = this.lent.actors[src] as { definition?: { workspace?: unknown } } | undefined;
|
|
2152
|
+
return logic?.definition?.workspace !== "none";
|
|
2153
|
+
}
|
|
2154
|
+
return (this.chart.agents?.[src] as { workspace?: unknown } | undefined)?.workspace !== "none";
|
|
2155
|
+
}
|
|
2156
|
+
|
|
1898
2157
|
/**
|
|
1899
2158
|
* An Agent invoke's input is a Turn's Frame and Dials (ADR-0057): `prompt` (a string), and
|
|
1900
2159
|
* optionally `cwd`, `continue: true`, a `model` the Open body allows, and a `thinkingLevel`.
|
|
@@ -1928,6 +2187,13 @@ class Door {
|
|
|
1928
2187
|
else if (typeof obj.prompt !== "string")
|
|
1929
2188
|
this.fail(8, `${ipath}.prompt: a prompt is a string (placeholders inside it are fine)`);
|
|
1930
2189
|
if (obj.cwd !== undefined && typeof obj.cwd !== "string") this.fail(8, `${ipath}.cwd: a cwd is a string`);
|
|
2190
|
+
if (obj.cwd === undefined && this.options.repoSlots?.length && this.worksInWorkspace(src, kind)) {
|
|
2191
|
+
this.fail(
|
|
2192
|
+
8,
|
|
2193
|
+
`${ipath}: ${who} works under a Workspace with Repo Slots, and a Turn there says where: \`cwd\` is a Worktree, or "/work"`,
|
|
2194
|
+
);
|
|
2195
|
+
}
|
|
2196
|
+
if (typeof obj.cwd === "string" && this.worksInWorkspace(src, kind)) this.checkCwd(obj.cwd, `${ipath}.cwd`);
|
|
1931
2197
|
if (obj.continue !== undefined && obj.continue !== true)
|
|
1932
2198
|
this.fail(8, `${ipath}.continue: \`continue\` is true or absent`);
|
|
1933
2199
|
if (obj.model !== undefined) {
|
|
@@ -1953,9 +2219,10 @@ class Door {
|
|
|
1953
2219
|
* The JSON Schema keywords a Chart event's payload may use (ADR-0070, rule 4). The Orchestrator
|
|
1954
2220
|
* checks every pick against this schema in its own process, before the Allowed check, so a keyword
|
|
1955
2221
|
* whose cost the door cannot bound is refused by name: `pattern` (a backtracking regex),
|
|
1956
|
-
* `patternProperties`, `$ref`, the combinators and the conditionals.
|
|
2222
|
+
* `patternProperties`, `$ref`, the combinators and the conditionals. It is also the subset a
|
|
2223
|
+
* dispatch's slot schema and its callee's door are compared within (ADR-0071, door-compat.ts).
|
|
1957
2224
|
*/
|
|
1958
|
-
const PAYLOAD_KEYWORDS = new Set([
|
|
2225
|
+
export const PAYLOAD_KEYWORDS: ReadonlySet<string> = new Set([
|
|
1959
2226
|
"type",
|
|
1960
2227
|
"properties",
|
|
1961
2228
|
"required",
|
|
@@ -1981,6 +2248,39 @@ const PAYLOAD_KEYWORDS = new Set([
|
|
|
1981
2248
|
* the payload name the event a Chart receives (registration.ts builds `{ type, ...payload }`).
|
|
1982
2249
|
*/
|
|
1983
2250
|
function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
|
|
2251
|
+
const out = subsetFaults(
|
|
2252
|
+
schema,
|
|
2253
|
+
(key) =>
|
|
2254
|
+
`"${key}" is not a keyword a Chart event's payload may use; it may use ${[...PAYLOAD_KEYWORDS].join(", ")}`,
|
|
2255
|
+
);
|
|
2256
|
+
const props = (schema as { properties?: unknown } | null)?.properties;
|
|
2257
|
+
if (typeof props === "object" && props !== null && Object.hasOwn(props, "type")) {
|
|
2258
|
+
out.push({
|
|
2259
|
+
path: ["properties", "type"],
|
|
2260
|
+
message: "a payload field named `type` is refused: jr2 writes the event's type, and the payload never names it",
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
return out;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
/**
|
|
2267
|
+
* The faults of a door's JSON Schema, or of a dispatch slot's (ADR-0071): every keyword not in
|
|
2268
|
+
* {@link PAYLOAD_KEYWORDS} and a list-form `items` — the payload rules without the Chart's own one
|
|
2269
|
+
* (a field named `type` is a door's to have). A schema with a fault is reported "not compared":
|
|
2270
|
+
* outside the subset, a comparison would be a guess.
|
|
2271
|
+
*/
|
|
2272
|
+
export function doorSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
|
|
2273
|
+
return subsetFaults(
|
|
2274
|
+
schema,
|
|
2275
|
+
(key) => `"${key}" is outside the JSON Schema subset jr2 compares (${[...PAYLOAD_KEYWORDS].join(", ")})`,
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
/** The subset's faults: `outside` words the fault of a keyword not in it, for whose rule it is. */
|
|
2280
|
+
function subsetFaults(
|
|
2281
|
+
schema: unknown,
|
|
2282
|
+
outside: (key: string) => string,
|
|
2283
|
+
): Array<{ path: PropertyKey[]; message: string }> {
|
|
1984
2284
|
const out: Array<{ path: PropertyKey[]; message: string }> = [];
|
|
1985
2285
|
const walk = (node: unknown, path: PropertyKey[]): void => {
|
|
1986
2286
|
if (typeof node === "boolean" && path.length && path[path.length - 1] === "additionalProperties") return;
|
|
@@ -1993,7 +2293,7 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
|
|
|
1993
2293
|
if (!PAYLOAD_KEYWORDS.has(key)) {
|
|
1994
2294
|
out.push({
|
|
1995
2295
|
path: [...path, key],
|
|
1996
|
-
message:
|
|
2296
|
+
message: outside(key),
|
|
1997
2297
|
});
|
|
1998
2298
|
continue;
|
|
1999
2299
|
}
|
|
@@ -2012,13 +2312,6 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
|
|
|
2012
2312
|
}
|
|
2013
2313
|
};
|
|
2014
2314
|
walk(schema, []);
|
|
2015
|
-
const props = (schema as { properties?: unknown } | null)?.properties;
|
|
2016
|
-
if (typeof props === "object" && props !== null && Object.hasOwn(props, "type")) {
|
|
2017
|
-
out.push({
|
|
2018
|
-
path: ["properties", "type"],
|
|
2019
|
-
message: "a payload field named `type` is refused: jr2 writes the event's type, and the payload never names it",
|
|
2020
|
-
});
|
|
2021
|
-
}
|
|
2022
2315
|
return out;
|
|
2023
2316
|
}
|
|
2024
2317
|
|
|
@@ -2032,6 +2325,12 @@ function wholeRefOf(v: unknown): string | undefined {
|
|
|
2032
2325
|
return isRef(v) ? (v as { $ref: string }).$ref : undefined;
|
|
2033
2326
|
}
|
|
2034
2327
|
|
|
2328
|
+
/** The nearest state at or above both `a` and `b`. */
|
|
2329
|
+
function nearestCommon(a: Node, b: Node): Node | undefined {
|
|
2330
|
+
for (let n: Node | undefined = a; n; n = n.parent) if (isAncestorOrSelf(n, b)) return n;
|
|
2331
|
+
return undefined;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2035
2334
|
/** Is `a` the state `b` or a state above it? */
|
|
2036
2335
|
function isAncestorOrSelf(a: Node, b: Node): boolean {
|
|
2037
2336
|
for (let n: Node | undefined = b; n; n = n.parent) if (n === a) return true;
|