@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/cause.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// What took the Machine out of an Agent's state (ADR-0024): the Turn's own pick, or something else.
|
|
2
|
+
// The Agent's Actor asks it as its invocation stops, and its sweep says `cause: "pick"` only for
|
|
3
|
+
// the first, so the Harness settles that Submission `ended` and every other end `aborted`. The pick
|
|
4
|
+
// may be from an earlier Submission of the same Turn: a nudge or a wake starts a new Submission in
|
|
5
|
+
// the same Turn.
|
|
6
|
+
//
|
|
7
|
+
// The answer comes from the events the Machines took, never from the pick's answer: "was the pick's
|
|
8
|
+
// work stopped" (`Hold.cutShort`, the answer's `stopped`) is another question. Two facts are kept,
|
|
9
|
+
// read off xstate's inspection stream and the actor system's scheduler and clock:
|
|
10
|
+
//
|
|
11
|
+
// - THE CAUSE OF EACH RELAY: the set of Agents' picks it comes from. ADR-0024 lists the four ways
|
|
12
|
+
// a relay comes from a pick: the pick itself, the work below the Agent's state, a send of an
|
|
13
|
+
// actor that takes a caused relay, and a timer armed while its Machine took one. The cause rides
|
|
14
|
+
// the RELAY, never the event object: xstate relays one object each time a config action with an
|
|
15
|
+
// object literal runs. A timer's cause rides its own `setTimeout` on the run's clock, so two
|
|
16
|
+
// timers of one id each keep their own.
|
|
17
|
+
// - THE EXIT. On each microstep of a Machine, the first microstep that stops the Agent's
|
|
18
|
+
// invocation names the cause of the macrostep it is part of. After it, the Agent's actor is no
|
|
19
|
+
// longer the child of the Machine that invokes it, a child Machine on the way up is no longer a
|
|
20
|
+
// child of the one above, or a Machine on the way up is done. A microstep that throws is never
|
|
21
|
+
// inspected, so a fault is never the exit. When a Machine above catches a fault on the way up,
|
|
22
|
+
// that catch is the exit, and it has no cause: a fault is never the pick's, even when the pick's
|
|
23
|
+
// route caused it. The stop of a run is no event of the Machine's.
|
|
24
|
+
// - A FAILURE (ADR-0073) is not a fault: it is the end that a state chose. When a Machine on the
|
|
25
|
+
// way up from the Agent ends with a Failure, its own error event is the exit, with the cause of
|
|
26
|
+
// that relay. So a pick whose route invokes `fail` ends its Turn, whether a Machine above
|
|
27
|
+
// catches the Failure or the run ends `failed`.
|
|
28
|
+
//
|
|
29
|
+
// Which relay an actor takes now is read from the relay order: xstate relays an event (inspected as
|
|
30
|
+
// `@xstate.event`) to the actor's mailbox. The mailbox takes events one at a time, in order, and
|
|
31
|
+
// xstate inspects `@xstate.snapshot` with that event when the actor has taken it. An actor's own done
|
|
32
|
+
// or error event (`xstate.done.actor.<id>`, `xstate.error.actor.<id>`) is the last relay it makes
|
|
33
|
+
// with a cause. It takes the cause of the relay the actor took then; after it, the actor is
|
|
34
|
+
// finished. A finished actor has no cause, and a relay to it, or to a stopped actor, is not queued:
|
|
35
|
+
// xstate drops it. Status alone does not say it: a done Machine makes its last deferred sends with
|
|
36
|
+
// status `done`, and a Machine done at start is `done` before its init relay. And an errored callback
|
|
37
|
+
// is never stopped: its own later sends still reach its parent.
|
|
38
|
+
|
|
39
|
+
import type { AnyActorRef, AnyEventObject, AnyMachineSnapshot } from "xstate";
|
|
40
|
+
import { isFailure } from "./fail.ts";
|
|
41
|
+
import type { Registration } from "./registration.ts";
|
|
42
|
+
|
|
43
|
+
const XSTATE_STOP = "xstate.stop";
|
|
44
|
+
|
|
45
|
+
/** The Agents' picks a relay comes from; undefined for none. */
|
|
46
|
+
type Cause = ReadonlySet<Registration> | undefined;
|
|
47
|
+
|
|
48
|
+
export class PickCauses {
|
|
49
|
+
/** Each actor's relays that it has not taken yet, in order, with their causes: the first is the
|
|
50
|
+
* one it takes now, or next. */
|
|
51
|
+
private readonly pending = new WeakMap<AnyActorRef, Array<{ event: AnyEventObject; cause: Cause }>>();
|
|
52
|
+
/** For each registration, the cause of the microstep that took the Machine out of its Agent's
|
|
53
|
+
* state, or `null` for none. Set once, at the first such microstep. */
|
|
54
|
+
private readonly exits = new WeakMap<Registration, Cause | null>();
|
|
55
|
+
/** Each registration's Agent actor, read off the invoking Machine's committed snapshot. */
|
|
56
|
+
private readonly actors = new WeakMap<Registration, AnyActorRef>();
|
|
57
|
+
/** Each actor that relayed its own done or error event: it takes nothing more, and has no cause. */
|
|
58
|
+
private readonly finished = new WeakSet<AnyActorRef>();
|
|
59
|
+
/** The cause of the timer the scheduler arms now: the next `setTimeout` on the clock keeps it. */
|
|
60
|
+
private arming: Cause;
|
|
61
|
+
/** Set while a timer's callback runs: its cause, for the relay the scheduler makes next. */
|
|
62
|
+
private firing: { cause: Cause } | undefined;
|
|
63
|
+
|
|
64
|
+
/** The scheduler arms a timer whose source is `source`: its cause is the relay `source` takes now. */
|
|
65
|
+
armingFrom(source: AnyActorRef): void {
|
|
66
|
+
this.arming = this.causeOfCurrent(source);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The run's clock, over `base`: each `setTimeout` keeps the cause of the timer the scheduler arms
|
|
71
|
+
* with it, and gives it to the relay its callback makes. So each timer's cause is its own, even
|
|
72
|
+
* for two timers of one id. A clear needs nothing: the callback never runs.
|
|
73
|
+
*/
|
|
74
|
+
clockOver<T>(base: { setTimeout: (fn: () => void, ms: number) => T; clearTimeout: (timer: T) => void }) {
|
|
75
|
+
return {
|
|
76
|
+
setTimeout: (fn: () => void, ms: number): T => {
|
|
77
|
+
const cause = this.arming;
|
|
78
|
+
this.arming = undefined;
|
|
79
|
+
return base.setTimeout(() => {
|
|
80
|
+
this.firing = { cause };
|
|
81
|
+
try {
|
|
82
|
+
fn();
|
|
83
|
+
} finally {
|
|
84
|
+
this.firing = undefined;
|
|
85
|
+
}
|
|
86
|
+
}, ms);
|
|
87
|
+
},
|
|
88
|
+
clearTimeout: (timer: T) => base.clearTimeout(timer),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `@xstate.event`: `event` is relayed from `source` to `target`. `pickOf` names the registrations
|
|
94
|
+
* whose pick it is, or whose work below it comes from. With them: the cause of the timer whose
|
|
95
|
+
* callback makes this relay, or else of the relay that `source` takes now.
|
|
96
|
+
*
|
|
97
|
+
* When the relay is the error event of a Machine that a Failure ended, it is the exit of each of
|
|
98
|
+
* `registrations` whose Agent that Machine invokes, or is above: the Failure took the Agent's
|
|
99
|
+
* state away, and the pick that chose it is the cause.
|
|
100
|
+
*/
|
|
101
|
+
relayed(
|
|
102
|
+
source: AnyActorRef | undefined,
|
|
103
|
+
target: AnyActorRef,
|
|
104
|
+
event: AnyEventObject,
|
|
105
|
+
pickOf: () => Cause,
|
|
106
|
+
registrations: () => Iterable<Registration> = () => [],
|
|
107
|
+
): void {
|
|
108
|
+
const firing = this.firing;
|
|
109
|
+
this.firing = undefined;
|
|
110
|
+
const by = firing ? firing.cause : source !== undefined ? this.causeOfCurrent(source) : undefined;
|
|
111
|
+
const cause = union(pickOf(), by);
|
|
112
|
+
// The source's own done or error event: the last relay it makes with a cause.
|
|
113
|
+
if (source !== undefined && isOwnEnd(source, event)) {
|
|
114
|
+
this.finished.add(source);
|
|
115
|
+
this.pending.delete(source);
|
|
116
|
+
if (isFailure((event as { error?: unknown }).error)) this.failedAt(source, cause, registrations());
|
|
117
|
+
}
|
|
118
|
+
if (this.finished.has(target) || statusOf(target) === "stopped") return;
|
|
119
|
+
const queue = this.pending.get(target);
|
|
120
|
+
if (queue) queue.push({ event, cause });
|
|
121
|
+
else this.pending.set(target, [{ event, cause }]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A Failure ended `machine`: record the exit of each registration whose invoker is `machine` or
|
|
125
|
+
* is below it, with the cause of the Failure's relay. */
|
|
126
|
+
private failedAt(machine: AnyActorRef, cause: Cause, registrations: Iterable<Registration>): void {
|
|
127
|
+
for (const reg of registrations) {
|
|
128
|
+
if (this.exits.has(reg)) continue;
|
|
129
|
+
for (let at: AnyActorRef | undefined = reg.invoker; at !== undefined; at = at._parent) {
|
|
130
|
+
if (at !== machine) continue;
|
|
131
|
+
this.exits.set(reg, cause ?? null);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** `@xstate.snapshot`: `actor` has taken `event`. An event that was not relayed (a stop) leaves
|
|
138
|
+
* the order as it is; a stop clears it, since a stopped actor takes nothing more. */
|
|
139
|
+
taken(actor: AnyActorRef, event: AnyEventObject): void {
|
|
140
|
+
const queue = this.pending.get(actor);
|
|
141
|
+
if (!queue) return;
|
|
142
|
+
if (event.type === XSTATE_STOP) {
|
|
143
|
+
queue.length = 0;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const at = queue.findIndex((relay) => relay.event === event);
|
|
147
|
+
if (at !== -1) queue.splice(0, at + 1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* `@xstate.microstep` of `machine`, with the snapshot after it: record the exit of each of
|
|
152
|
+
* `registrations` that this microstep takes out of the Machine.
|
|
153
|
+
*/
|
|
154
|
+
microstep(
|
|
155
|
+
machine: AnyActorRef,
|
|
156
|
+
snapshot: AnyMachineSnapshot,
|
|
157
|
+
event: AnyEventObject,
|
|
158
|
+
registrations: Iterable<Registration>,
|
|
159
|
+
): void {
|
|
160
|
+
for (const reg of registrations) {
|
|
161
|
+
if (this.exits.has(reg) || !reg.invoker || !reg.invokeId) continue;
|
|
162
|
+
if (!this.actors.has(reg)) {
|
|
163
|
+
// The committed snapshot: the invoking Machine's own microstep is not committed until its
|
|
164
|
+
// macrostep ends, so this is the actor the registration was made for.
|
|
165
|
+
const ref = childrenOf(reg.invoker.getSnapshot())[reg.invokeId];
|
|
166
|
+
if (ref === undefined) continue;
|
|
167
|
+
this.actors.set(reg, ref);
|
|
168
|
+
}
|
|
169
|
+
const exit = leaves(reg, this.actors.get(reg)!, machine, snapshot);
|
|
170
|
+
if (exit === false) continue;
|
|
171
|
+
// A fault on the way up took the Agent's state away when the Machine above caught it: that is
|
|
172
|
+
// the exit, and a fault is never the pick's, even when the pick's route caused it.
|
|
173
|
+
const none = event.type === XSTATE_STOP || exit === "fault";
|
|
174
|
+
this.exits.set(reg, none ? null : (this.causeOfCurrent(machine) ?? null));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Did this registration's own pick take the Machine out of its Agent's state? */
|
|
179
|
+
endedByPick(reg: Registration): boolean {
|
|
180
|
+
return this.exits.get(reg)?.has(reg) === true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The cause of the relay `actor` takes now. A done or errored actor relays its own done or error
|
|
184
|
+
* event while it takes its last relay, so that event keeps the cause. A finished or stopped actor
|
|
185
|
+
* has none. */
|
|
186
|
+
private causeOfCurrent(actor: AnyActorRef): Cause {
|
|
187
|
+
const status = statusOf(actor);
|
|
188
|
+
if (status === undefined || status === "stopped" || this.finished.has(actor)) return undefined;
|
|
189
|
+
return this.pending.get(actor)?.[0]?.cause;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function statusOf(actor: AnyActorRef): string | undefined {
|
|
194
|
+
return (actor.getSnapshot() as { status?: string } | undefined)?.status;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Is `event` the actor's own done or error event? */
|
|
198
|
+
function isOwnEnd(actor: AnyActorRef, event: AnyEventObject): boolean {
|
|
199
|
+
return event.type === `xstate.done.actor.${actor.id}` || event.type === `xstate.error.actor.${actor.id}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function union(a: Cause, b: Cause): Cause {
|
|
203
|
+
if (a === undefined) return b;
|
|
204
|
+
if (b === undefined) return a;
|
|
205
|
+
return new Set([...a, ...b]);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Does this microstep of `machine` stop `reg`'s invocation: `machine` is on the way up from the
|
|
209
|
+
* Agent and is done, or the actor (or child Machine) on the way down is no longer its child. It is
|
|
210
|
+
* `"fault"` when a Machine on the way up below `machine` has status `error`: `machine` caught that
|
|
211
|
+
* fault. A Failure is no fault (ADR-0073), but it never gets here: its relay records the exit
|
|
212
|
+
* (`failedAt`) before a Machine above takes it. The invoker's own microstep has no Machine below it
|
|
213
|
+
* on the way up, so the error of a child of the invoker is never a fault here. */
|
|
214
|
+
function leaves(
|
|
215
|
+
reg: Registration,
|
|
216
|
+
agent: AnyActorRef,
|
|
217
|
+
machine: AnyActorRef,
|
|
218
|
+
snapshot: AnyMachineSnapshot,
|
|
219
|
+
): "exit" | "fault" | false {
|
|
220
|
+
let below = agent;
|
|
221
|
+
let faulted = false;
|
|
222
|
+
for (let at: AnyActorRef | undefined = reg.invoker; at !== undefined; below = at, at = at._parent) {
|
|
223
|
+
if (at === machine) {
|
|
224
|
+
if (snapshot.status !== "done" && childrenOf(snapshot)[below.id] === below) return false;
|
|
225
|
+
return faulted ? "fault" : "exit";
|
|
226
|
+
}
|
|
227
|
+
if (statusOf(at) === "error") faulted = true;
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function childrenOf(snapshot: unknown): Record<string, AnyActorRef | undefined> {
|
|
233
|
+
return (snapshot as { children?: Record<string, AnyActorRef | undefined> } | undefined)?.children ?? {};
|
|
234
|
+
}
|
package/src/chart.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { z } from "zod";
|
|
|
23
23
|
import { defineEvent, type EventAudience, type EventDef } from "@jr2/agent-protocol";
|
|
24
24
|
import type { AgentDefinition, ThinkingLevel, WorkspaceAccess } from "./agent.ts";
|
|
25
25
|
import { inputKeysOf, isEffect } from "./effect.ts";
|
|
26
|
+
import { FAILURE_GUARD } from "./fail.ts";
|
|
26
27
|
import { compileChart, durationMs, type LentParts, type OpenBodyDeclaration } from "./open-body.ts";
|
|
27
28
|
import { SENT_BY } from "./party.ts";
|
|
28
29
|
import { loadWarningsOf } from "./vocabulary.ts";
|
|
@@ -38,7 +39,8 @@ export type ChartJson = string | number | boolean | null | ChartJson[] | { [key:
|
|
|
38
39
|
*/
|
|
39
40
|
export type ChartValue = ChartJson;
|
|
40
41
|
|
|
41
|
-
/** A guard: a lent guard by name, with or without params, `jr2.sentBy`,
|
|
42
|
+
/** A guard: a lent guard by name, with or without params, `jr2.sentBy`, `jr2.failure`, or
|
|
43
|
+
* `not`/`and`/`or`. */
|
|
42
44
|
export type ChartGuard =
|
|
43
45
|
| string
|
|
44
46
|
| { type: string; params?: ChartValue }
|
|
@@ -71,7 +73,7 @@ export type ChartTransition =
|
|
|
71
73
|
/** One transition or a list, in xstate's order. */
|
|
72
74
|
export type ChartTransitions = ChartTransition | ChartTransition[];
|
|
73
75
|
|
|
74
|
-
/** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate`. */
|
|
76
|
+
/** An invoke: a lent Agent, Effect or Machine, a minted Agent, or the kit's `gate` or `fail`. */
|
|
75
77
|
export type ChartInvoke = {
|
|
76
78
|
src: string;
|
|
77
79
|
id?: string;
|
|
@@ -158,6 +160,10 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
158
160
|
? ' `src: "gate"` is the kit\'s Gate: the Chart waits there for an outside answer (a human, a webhook), ' +
|
|
159
161
|
'one of its `events` with `audience: "external"`, not an Agent\'s Turn.'
|
|
160
162
|
: "";
|
|
163
|
+
const fail = narrow?.fail
|
|
164
|
+
? ' `src: "fail"` with `input: { reason }` ends the run as a Failure: work that did not succeed, with ' +
|
|
165
|
+
`the reason for a person; an \`onError\` with the guard \`${FAILURE_GUARD}\` catches it. A final state means done.`
|
|
166
|
+
: "";
|
|
161
167
|
const agents = mints
|
|
162
168
|
? `A state runs an Agent with \`invoke: { src, input: { prompt } }\`: \`src\` is a lent Agent's name${lent}. ` +
|
|
163
169
|
"This Chart mints no Agent of its own: there is no `agents`."
|
|
@@ -165,7 +171,7 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
165
171
|
"a key of `agents`; a lent Agent needs no entry in `agents`, which mints new ones.";
|
|
166
172
|
return (
|
|
167
173
|
`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 ` +
|
|
174
|
+
`${agents}${gate}${fail} An Agent's Turn ends only when it ` +
|
|
169
175
|
"picks one of the Chart's `events`: declare each pick in `events` with an `input` schema " +
|
|
170
176
|
'(`{ "type": "object", "properties": { "<field>": { "type": "string" } }, "required": ["<field>"] }`; a field not in ' +
|
|
171
177
|
"`required` may be absent), and " +
|
|
@@ -233,6 +239,7 @@ type Narrow = {
|
|
|
233
239
|
srcs: string[];
|
|
234
240
|
lentAgents: string[];
|
|
235
241
|
gate: boolean;
|
|
242
|
+
fail: boolean;
|
|
236
243
|
guards: string[];
|
|
237
244
|
actions: string[];
|
|
238
245
|
delays: string[];
|
|
@@ -353,16 +360,25 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
353
360
|
})
|
|
354
361
|
.describe('A context path without the "context." root, like "round" or "ticket.notes".');
|
|
355
362
|
|
|
356
|
-
// `jr2.sentBy` is the kit's routing guard (ADR-0015), and
|
|
363
|
+
// `jr2.sentBy` is the kit's routing guard (ADR-0015), and `jr2.failure` its test for a Failure on
|
|
364
|
+
// an `onError` (ADR-0073): every Chart has both, and neither is lent.
|
|
357
365
|
const sentByArm = z.strictObject({ type: z.literal(SENT_BY), params: z.strictObject({ id: z.string() }) });
|
|
366
|
+
const failureArm = z.strictObject({ type: z.literal(FAILURE_GUARD) });
|
|
358
367
|
const lentGuardName = narrow
|
|
359
|
-
? enumOf(narrow.guards)
|
|
368
|
+
? enumOf([...narrow.guards, FAILURE_GUARD])
|
|
360
369
|
: z.string().refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` });
|
|
370
|
+
const paramGuardName = narrow
|
|
371
|
+
? enumOf(narrow.guards)
|
|
372
|
+
: z
|
|
373
|
+
.string()
|
|
374
|
+
.refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` })
|
|
375
|
+
.refine((t) => t !== FAILURE_GUARD, { message: `"${FAILURE_GUARD}" takes no params` });
|
|
361
376
|
const guard: z.ZodType<ChartGuard> = z
|
|
362
377
|
.union([
|
|
363
378
|
lentGuardName,
|
|
364
379
|
sentByArm,
|
|
365
|
-
|
|
380
|
+
failureArm,
|
|
381
|
+
z.strictObject({ type: paramGuardName, params: template.optional() }),
|
|
366
382
|
z.strictObject({
|
|
367
383
|
get not() {
|
|
368
384
|
return guard;
|
|
@@ -381,7 +397,7 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
381
397
|
])
|
|
382
398
|
.meta({
|
|
383
399
|
id: `${ID_PREFIX}ChartGuard`,
|
|
384
|
-
description:
|
|
400
|
+
description: `A lent guard by name, \`jr2.sentBy\`, \`${FAILURE_GUARD}\`, or not/and/or of guards.`,
|
|
385
401
|
}) as never;
|
|
386
402
|
|
|
387
403
|
const lentActionName = narrow
|
|
@@ -425,9 +441,14 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
425
441
|
: z.string();
|
|
426
442
|
const invoke = z
|
|
427
443
|
.strictObject({
|
|
444
|
+
// A narrowed grammar names `fail` only where the body lends it: a model reads only what it can use.
|
|
428
445
|
src: src.describe(
|
|
429
|
-
|
|
430
|
-
"
|
|
446
|
+
narrow && !narrow.fail
|
|
447
|
+
? "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
|
|
448
|
+
"webhook) where the body lends it; or a key of this Chart's `agents`."
|
|
449
|
+
: "A lent Agent, Effect or Machine by its name; the kit's `gate` (waits for an outside answer: a human, a " +
|
|
450
|
+
"webhook) or `fail` (ends the run as a Failure, with `input: { reason }`) where the body lends it; or a " +
|
|
451
|
+
"key of this Chart's `agents`.",
|
|
431
452
|
),
|
|
432
453
|
id: name("invoke id").optional(),
|
|
433
454
|
input: template.optional().describe("An Agent takes `{ prompt }`; an Effect or a Machine takes its own input."),
|
|
@@ -526,9 +547,17 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
526
547
|
.describe(chartBriefing(narrow)) as unknown as z.ZodType<ChartText>;
|
|
527
548
|
}
|
|
528
549
|
|
|
550
|
+
// A door field or a pick param that IS this schema is a Chart field: `isChartSchema` tells, and
|
|
551
|
+
// jr2 reads a Chart wrapper's one-shot door off it (ADR-0070).
|
|
529
552
|
/**
|
|
530
|
-
* The Chart grammar
|
|
531
|
-
*
|
|
553
|
+
* The Chart grammar, as a zod schema. Make a field of the run input of a Chart wrapper this schema,
|
|
554
|
+
* as in `z.object({ topic: z.string(), chart: chartSchema })`. Then a run input can carry a Chart,
|
|
555
|
+
* and the wrapper can keep Charts under names: Chart files in `workflows/<wrapper>/`, and runtime
|
|
556
|
+
* Charts. For the pick of a planner, use `chartSchemaFor`, which knows the names that the Open body
|
|
557
|
+
* lends.
|
|
558
|
+
*
|
|
559
|
+
* @see docs/guides/run-a-chart-by-name.md
|
|
560
|
+
* @see docs/concepts/charts.md
|
|
532
561
|
*/
|
|
533
562
|
export const chartSchema: z.ZodType<ChartText> = guarded(grammar());
|
|
534
563
|
|
|
@@ -578,13 +607,38 @@ export function chartBriefingOf(schema: unknown): string | undefined {
|
|
|
578
607
|
|
|
579
608
|
const narrowedSchemas = new WeakMap<OpenBodyDeclaration, z.ZodType<ChartText>>();
|
|
580
609
|
|
|
610
|
+
// Either way it is a Chart field (`isChartSchema`), and the door still admits the Chart against
|
|
611
|
+
// the lent set the body has at its start. One schema per declaration (ADR-0070).
|
|
581
612
|
/**
|
|
582
|
-
* The Chart grammar
|
|
583
|
-
*
|
|
584
|
-
* pick
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
613
|
+
* The Chart grammar for one Open body, as a zod schema. The names that the Open body lends, and the
|
|
614
|
+
* models and the Workspace accesses that it allows, are the only values that the schema takes.
|
|
615
|
+
* Make it the parameter of the pick of a planner. The model then gets the grammar as the
|
|
616
|
+
* parameters of its tool, and the Briefing tells it how a Chart runs. The door still checks each
|
|
617
|
+
* Chart before it runs.
|
|
618
|
+
*
|
|
619
|
+
* @param decl - The declaration of the Open body that the Chart binds.
|
|
620
|
+
* @returns The schema of a Chart for that Open body.
|
|
621
|
+
*
|
|
622
|
+
* @example
|
|
623
|
+
* ```ts
|
|
624
|
+
* import { z } from "zod";
|
|
625
|
+
* import { chartSchemaFor, defineEvent, type OpenBodyDeclaration } from "@jr2/orchestrator";
|
|
626
|
+
*
|
|
627
|
+
* const declaration: OpenBodyDeclaration = {
|
|
628
|
+
* lends: { agents: ["writer"], gate: true },
|
|
629
|
+
* allows: { models: ["anthropic/claude-haiku-5-5"], workspace: ["none"] },
|
|
630
|
+
* ceilings: { turns: 12, wallClock: "30m", transitions: 500, fanOut: 2, size: 65536 },
|
|
631
|
+
* };
|
|
632
|
+
*
|
|
633
|
+
* export const runChart = defineEvent({
|
|
634
|
+
* name: "run_chart",
|
|
635
|
+
* description: "Run this Chart. Its outcome is the answer to this call.",
|
|
636
|
+
* input: z.object({ chart: chartSchemaFor(declaration) }),
|
|
637
|
+
* });
|
|
638
|
+
* ```
|
|
639
|
+
*
|
|
640
|
+
* @see docs/guides/let-an-agent-write-a-chart.md
|
|
641
|
+
* @see docs/concepts/charts.md
|
|
588
642
|
*/
|
|
589
643
|
export function chartSchemaFor(decl: OpenBodyDeclaration): z.ZodType<ChartText> {
|
|
590
644
|
const hit = narrowedSchemas.get(decl);
|
|
@@ -607,6 +661,7 @@ function lendsOf(decl: OpenBodyDeclaration) {
|
|
|
607
661
|
actions: decl.lends.actions ?? [],
|
|
608
662
|
delays: decl.lends.delays ?? [],
|
|
609
663
|
gate: decl.lends.gate === true,
|
|
664
|
+
fail: decl.lends.fail === true,
|
|
610
665
|
};
|
|
611
666
|
}
|
|
612
667
|
|
|
@@ -624,9 +679,10 @@ export function narrowedChartSchema(decl: OpenBodyDeclaration): Record<string, u
|
|
|
624
679
|
function narrowOf(decl: OpenBodyDeclaration): Narrow {
|
|
625
680
|
const l = lendsOf(decl);
|
|
626
681
|
return {
|
|
627
|
-
srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : [])],
|
|
682
|
+
srcs: [...l.agents, ...l.effects, ...l.machines, ...(l.gate ? ["gate"] : []), ...(l.fail ? ["fail"] : [])],
|
|
628
683
|
lentAgents: l.agents,
|
|
629
684
|
gate: l.gate,
|
|
685
|
+
fail: l.fail,
|
|
630
686
|
guards: l.guards,
|
|
631
687
|
actions: l.actions,
|
|
632
688
|
delays: l.delays,
|
|
@@ -867,6 +923,9 @@ function issueLines(
|
|
|
867
923
|
|
|
868
924
|
const pathText = (path: readonly PropertyKey[]): string => (path.length ? path.map(String).join(".") : "(chart)");
|
|
869
925
|
|
|
926
|
+
/** What an invoke's `src` names (rule 3). */
|
|
927
|
+
type InvokeKind = "agent" | "minted" | "effect" | "machine" | "gate" | "fail";
|
|
928
|
+
|
|
870
929
|
/** One state of the Chart, indexed for the checks. */
|
|
871
930
|
type Node = {
|
|
872
931
|
key: string;
|
|
@@ -1064,11 +1123,12 @@ class Door {
|
|
|
1064
1123
|
private lentText(): string {
|
|
1065
1124
|
const actors = this.lentActors().map((a) => `${a.name} (${a.kind})`);
|
|
1066
1125
|
const minted = Object.keys(this.chart.agents ?? {});
|
|
1067
|
-
|
|
1126
|
+
const kit = [...(this.lends.gate ? ["gate"] : []), ...(this.lends.fail ? ["fail"] : [])];
|
|
1127
|
+
return `lent: ${names(actors)}; kit: ${kit.length ? kit.join(", ") : "none"}; minted: ${names(minted)}`;
|
|
1068
1128
|
}
|
|
1069
1129
|
|
|
1070
1130
|
/** What an invoke's `src` names, or undefined after a refusal line. */
|
|
1071
|
-
private srcKind(src: string, path: string):
|
|
1131
|
+
private srcKind(src: string, path: string): InvokeKind | undefined {
|
|
1072
1132
|
const lentNow = (name: string) => name in this.lent.actors;
|
|
1073
1133
|
const declared = [...this.lends.agents, ...this.lends.effects, ...this.lends.machines];
|
|
1074
1134
|
if (declared.includes(src) && !lentNow(src)) {
|
|
@@ -1089,6 +1149,14 @@ class Door {
|
|
|
1089
1149
|
);
|
|
1090
1150
|
return undefined;
|
|
1091
1151
|
}
|
|
1152
|
+
if (src === "fail") {
|
|
1153
|
+
if (this.lends.fail) return "fail";
|
|
1154
|
+
this.fail(
|
|
1155
|
+
3,
|
|
1156
|
+
`${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()}`,
|
|
1157
|
+
);
|
|
1158
|
+
return undefined;
|
|
1159
|
+
}
|
|
1092
1160
|
if (Object.hasOwn(this.chart.agents ?? {}, src)) return "minted";
|
|
1093
1161
|
this.fail(3, `${path}: "${src}" is not a lent actor or an Agent of this Chart; ${this.lentText()}`);
|
|
1094
1162
|
return undefined;
|
|
@@ -1103,11 +1171,16 @@ class Door {
|
|
|
1103
1171
|
this.sentBys.push({ id: (guard.params as { id: string }).id, path: `${path}.params.id` });
|
|
1104
1172
|
return;
|
|
1105
1173
|
}
|
|
1174
|
+
if (guard.type === FAILURE_GUARD) {
|
|
1175
|
+
if (guard.params !== undefined) this.fail(3, `${path}.params: "${FAILURE_GUARD}" takes no params`);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1106
1178
|
this.checkGuardName(guard.type, `${path}.type`);
|
|
1107
1179
|
if (guard.params !== undefined) this.checkRefs(guard.params, `${path}.params`, scope);
|
|
1108
1180
|
}
|
|
1109
1181
|
|
|
1110
1182
|
private checkGuardName(name: string, path: string): void {
|
|
1183
|
+
if (name === FAILURE_GUARD) return;
|
|
1111
1184
|
const lent = this.lends.guards.filter((g) => g in this.lent.guards);
|
|
1112
1185
|
if (this.lends.guards.includes(name) && !(name in this.lent.guards)) {
|
|
1113
1186
|
this.fail(
|
|
@@ -1117,7 +1190,7 @@ class Door {
|
|
|
1117
1190
|
} else if (!this.lends.guards.includes(name)) {
|
|
1118
1191
|
this.fail(
|
|
1119
1192
|
3,
|
|
1120
|
-
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id })`,
|
|
1193
|
+
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id }, and ${FAILURE_GUARD})`,
|
|
1121
1194
|
);
|
|
1122
1195
|
}
|
|
1123
1196
|
}
|
|
@@ -1262,7 +1335,7 @@ class Door {
|
|
|
1262
1335
|
// --- rule 5: minted Agents ------------------------------------------------------------------------
|
|
1263
1336
|
|
|
1264
1337
|
private checkAgents(): void {
|
|
1265
|
-
const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate"]);
|
|
1338
|
+
const lentNames = new Set([...this.lends.agents, ...this.lends.effects, ...this.lends.machines, "gate", "fail"]);
|
|
1266
1339
|
for (const [slot, a] of Object.entries(this.chart.agents ?? {})) {
|
|
1267
1340
|
const path = `agents.${slot}`;
|
|
1268
1341
|
if (lentNames.has(slot)) {
|
|
@@ -1835,15 +1908,12 @@ class Door {
|
|
|
1835
1908
|
|
|
1836
1909
|
// --- rule 8: lent input shapes --------------------------------------------------------------------
|
|
1837
1910
|
|
|
1838
|
-
private checkInvokeInput(
|
|
1839
|
-
inv: ChartInvoke,
|
|
1840
|
-
kind: "agent" | "minted" | "effect" | "machine" | "gate",
|
|
1841
|
-
path: string,
|
|
1842
|
-
): void {
|
|
1911
|
+
private checkInvokeInput(inv: ChartInvoke, kind: InvokeKind, path: string): void {
|
|
1843
1912
|
const ipath = `${path}.input`;
|
|
1844
1913
|
const input = inv.input;
|
|
1845
1914
|
const isObject = typeof input === "object" && input !== null && !Array.isArray(input) && !("$ref" in input);
|
|
1846
1915
|
if (kind === "machine") return;
|
|
1916
|
+
if (kind === "fail") return this.checkFailInput(input, isObject, ipath);
|
|
1847
1917
|
if (kind === "gate") {
|
|
1848
1918
|
if (input === undefined) return;
|
|
1849
1919
|
if (!isObject) return this.fail(8, `${ipath}: a Gate's input is an object of gate (a name) and meta`);
|
|
@@ -1864,6 +1934,23 @@ class Door {
|
|
|
1864
1934
|
if (kind === "effect") {
|
|
1865
1935
|
const logic = this.lent.actors[inv.src];
|
|
1866
1936
|
const keys = isEffect(logic) ? inputKeysOf(logic.effect) : undefined;
|
|
1937
|
+
if (keys === undefined && isEffect(logic) && logic.effect.kind === "dispatch") {
|
|
1938
|
+
// A dispatch with no input schema has no keys to hold the Chart to: its input is the run's
|
|
1939
|
+
// body, and the callee's door is the check (ADR-0071). Its values are what any Effect's
|
|
1940
|
+
// input slot takes.
|
|
1941
|
+
if (input === undefined) return;
|
|
1942
|
+
if (!isObject) {
|
|
1943
|
+
return this.fail(
|
|
1944
|
+
8,
|
|
1945
|
+
`${ipath}: the lent dispatch "${inv.src}" takes an object, the run's body — the callee's door checks it`,
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
for (const [k, v] of Object.entries(input as Record<string, unknown>)) {
|
|
1949
|
+
if (typeof v !== "string" && !isRef(v))
|
|
1950
|
+
this.fail(8, `${ipath}.${k}: an Effect's input slot takes a string or a reference`);
|
|
1951
|
+
}
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1867
1954
|
if (keys === undefined) {
|
|
1868
1955
|
return this.fail(
|
|
1869
1956
|
8,
|
|
@@ -1895,6 +1982,24 @@ class Door {
|
|
|
1895
1982
|
this.checkAgentInput(inv.src, kind, input, isObject, ipath);
|
|
1896
1983
|
}
|
|
1897
1984
|
|
|
1985
|
+
/** The input of `fail` (ADR-0073): `{ reason, detail? }`, `reason` a text for a person (a string
|
|
1986
|
+
* or a reference), `detail` any value. */
|
|
1987
|
+
private checkFailInput(input: unknown, isObject: boolean, ipath: string): void {
|
|
1988
|
+
if (!isObject) {
|
|
1989
|
+
return this.fail(8, `${ipath}: \`fail\` takes { reason, detail? }: reason is a text for a person`);
|
|
1990
|
+
}
|
|
1991
|
+
for (const key of Object.keys(input as Record<string, unknown>)) {
|
|
1992
|
+
if (key !== "reason" && key !== "detail") {
|
|
1993
|
+
this.fail(8, `${ipath}.${key}: \`fail\` takes reason and detail only`);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
const reason = (input as Record<string, unknown>).reason;
|
|
1997
|
+
if (reason === undefined) this.fail(8, `${ipath}.reason: \`fail\` needs a reason, a text for a person`);
|
|
1998
|
+
else if ((typeof reason !== "string" || reason === "") && !isRef(reason)) {
|
|
1999
|
+
this.fail(8, `${ipath}.reason: a reason is a string that is not empty, or a reference`);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
1898
2003
|
/**
|
|
1899
2004
|
* An Agent invoke's input is a Turn's Frame and Dials (ADR-0057): `prompt` (a string), and
|
|
1900
2005
|
* optionally `cwd`, `continue: true`, a `model` the Open body allows, and a `thinkingLevel`.
|
|
@@ -1953,9 +2058,10 @@ class Door {
|
|
|
1953
2058
|
* The JSON Schema keywords a Chart event's payload may use (ADR-0070, rule 4). The Orchestrator
|
|
1954
2059
|
* checks every pick against this schema in its own process, before the Allowed check, so a keyword
|
|
1955
2060
|
* whose cost the door cannot bound is refused by name: `pattern` (a backtracking regex),
|
|
1956
|
-
* `patternProperties`, `$ref`, the combinators and the conditionals.
|
|
2061
|
+
* `patternProperties`, `$ref`, the combinators and the conditionals. It is also the subset a
|
|
2062
|
+
* dispatch's slot schema and its callee's door are compared within (ADR-0071, door-compat.ts).
|
|
1957
2063
|
*/
|
|
1958
|
-
const PAYLOAD_KEYWORDS = new Set([
|
|
2064
|
+
export const PAYLOAD_KEYWORDS: ReadonlySet<string> = new Set([
|
|
1959
2065
|
"type",
|
|
1960
2066
|
"properties",
|
|
1961
2067
|
"required",
|
|
@@ -1981,6 +2087,39 @@ const PAYLOAD_KEYWORDS = new Set([
|
|
|
1981
2087
|
* the payload name the event a Chart receives (registration.ts builds `{ type, ...payload }`).
|
|
1982
2088
|
*/
|
|
1983
2089
|
function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
|
|
2090
|
+
const out = subsetFaults(
|
|
2091
|
+
schema,
|
|
2092
|
+
(key) =>
|
|
2093
|
+
`"${key}" is not a keyword a Chart event's payload may use; it may use ${[...PAYLOAD_KEYWORDS].join(", ")}`,
|
|
2094
|
+
);
|
|
2095
|
+
const props = (schema as { properties?: unknown } | null)?.properties;
|
|
2096
|
+
if (typeof props === "object" && props !== null && Object.hasOwn(props, "type")) {
|
|
2097
|
+
out.push({
|
|
2098
|
+
path: ["properties", "type"],
|
|
2099
|
+
message: "a payload field named `type` is refused: jr2 writes the event's type, and the payload never names it",
|
|
2100
|
+
});
|
|
2101
|
+
}
|
|
2102
|
+
return out;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* The faults of a door's JSON Schema, or of a dispatch slot's (ADR-0071): every keyword not in
|
|
2107
|
+
* {@link PAYLOAD_KEYWORDS} and a list-form `items` — the payload rules without the Chart's own one
|
|
2108
|
+
* (a field named `type` is a door's to have). A schema with a fault is reported "not compared":
|
|
2109
|
+
* outside the subset, a comparison would be a guess.
|
|
2110
|
+
*/
|
|
2111
|
+
export function doorSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; message: string }> {
|
|
2112
|
+
return subsetFaults(
|
|
2113
|
+
schema,
|
|
2114
|
+
(key) => `"${key}" is outside the JSON Schema subset jr2 compares (${[...PAYLOAD_KEYWORDS].join(", ")})`,
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
/** The subset's faults: `outside` words the fault of a keyword not in it, for whose rule it is. */
|
|
2119
|
+
function subsetFaults(
|
|
2120
|
+
schema: unknown,
|
|
2121
|
+
outside: (key: string) => string,
|
|
2122
|
+
): Array<{ path: PropertyKey[]; message: string }> {
|
|
1984
2123
|
const out: Array<{ path: PropertyKey[]; message: string }> = [];
|
|
1985
2124
|
const walk = (node: unknown, path: PropertyKey[]): void => {
|
|
1986
2125
|
if (typeof node === "boolean" && path.length && path[path.length - 1] === "additionalProperties") return;
|
|
@@ -1993,7 +2132,7 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
|
|
|
1993
2132
|
if (!PAYLOAD_KEYWORDS.has(key)) {
|
|
1994
2133
|
out.push({
|
|
1995
2134
|
path: [...path, key],
|
|
1996
|
-
message:
|
|
2135
|
+
message: outside(key),
|
|
1997
2136
|
});
|
|
1998
2137
|
continue;
|
|
1999
2138
|
}
|
|
@@ -2012,13 +2151,6 @@ function payloadSchemaFaults(schema: unknown): Array<{ path: PropertyKey[]; mess
|
|
|
2012
2151
|
}
|
|
2013
2152
|
};
|
|
2014
2153
|
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
2154
|
return out;
|
|
2023
2155
|
}
|
|
2024
2156
|
|