@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/notices.ts
CHANGED
|
@@ -13,8 +13,17 @@ import type { Notice } from "./wire.ts";
|
|
|
13
13
|
|
|
14
14
|
/** Where an admission may hear notices: its continued conversation's base Instance ID (ADR-0057)
|
|
15
15
|
* and its Workspace's Sandbox name. Either may be absent — a fresh Turn has no conversation to be
|
|
16
|
-
* told about, and a Menu-only Agent works in no Workspace.
|
|
17
|
-
|
|
16
|
+
* told about, and a Menu-only Agent works in no Workspace. `only`, when set, narrows the take to
|
|
17
|
+
* the notices raised by these sources: what a nudge or a wake carries (ADR-0062). `except` leaves
|
|
18
|
+
* out the notices raised by these: what a re-admission of the same Turn was told already;
|
|
19
|
+
* `exceptPrefix` every notice whose source starts so — all guard kills, when that is not known. */
|
|
20
|
+
export type NoticeScope = {
|
|
21
|
+
conversation?: string;
|
|
22
|
+
workspace?: string;
|
|
23
|
+
only?: readonly string[];
|
|
24
|
+
except?: readonly string[];
|
|
25
|
+
exceptPrefix?: string;
|
|
26
|
+
};
|
|
18
27
|
|
|
19
28
|
/** One pending notice: the notice, and the conversation or Workspace it goes to. `source` names
|
|
20
29
|
* the event that raised it, so the same event raised twice — two Turns lost to one kernel kill, a
|
|
@@ -26,7 +35,13 @@ export type PendingNotice = { id: string; notice: Notice; to: string; source?: s
|
|
|
26
35
|
export type NoticeLedgerState = { pending: PendingNotice[]; seen: string[] };
|
|
27
36
|
|
|
28
37
|
/** What one admission takes: the notices it carries, and their ids for the ledger write. */
|
|
29
|
-
export type TakenNotices = {
|
|
38
|
+
export type TakenNotices = {
|
|
39
|
+
ids: string[];
|
|
40
|
+
notices: Notice[];
|
|
41
|
+
/** Every notice taken, with the source it was raised with — what `restore` puts back when no
|
|
42
|
+
* model heard the admission. */
|
|
43
|
+
raised?: Array<{ notice: Notice; source?: string }>;
|
|
44
|
+
};
|
|
30
45
|
|
|
31
46
|
export class NoticeLedger {
|
|
32
47
|
readonly #pending: PendingNotice[];
|
|
@@ -41,30 +56,68 @@ export class NoticeLedger {
|
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
/** Raise a notice for `to` (a conversation's base id, or a Sandbox name — the notice's own
|
|
44
|
-
* `scope` says which).
|
|
45
|
-
*
|
|
46
|
-
|
|
59
|
+
* `scope` says which). `older` are the sources of earlier events that this one tells the same as —
|
|
60
|
+
* a burst of guard kills, read at once: they are marked raised and give no notice of their own.
|
|
61
|
+
* Answers whether anything changed — a notice pending, or a source marked — so the caller saves
|
|
62
|
+
* only then. */
|
|
63
|
+
raise(notice: Notice, to: string, source?: string, older: readonly string[] = []): boolean {
|
|
64
|
+
let changed = false;
|
|
65
|
+
for (const seen of older) {
|
|
66
|
+
if (this.#seen.has(seen)) continue;
|
|
67
|
+
this.#seen.add(seen);
|
|
68
|
+
changed = true;
|
|
69
|
+
}
|
|
47
70
|
if (source !== undefined) {
|
|
48
|
-
if (this.#seen.has(source)) return
|
|
71
|
+
if (this.#seen.has(source)) return changed;
|
|
49
72
|
this.#seen.add(source);
|
|
73
|
+
} else {
|
|
74
|
+
// A notice with no source is one notice with a pending twin that has none either. A notice
|
|
75
|
+
// WITH a source keeps its own entry, so a take by source (`only`, `except`) finds it; equal
|
|
76
|
+
// ones are one notice in the admission (`take`).
|
|
77
|
+
const same = JSON.stringify(notice);
|
|
78
|
+
if (this.#pending.some((p) => p.source === undefined && p.to === to && JSON.stringify(p.notice) === same)) {
|
|
79
|
+
return changed;
|
|
80
|
+
}
|
|
50
81
|
}
|
|
51
|
-
// The same news, still unheard, is one notice: the guard writes one kill to the stream of
|
|
52
|
-
// every conversation running in the pod, and each Turn following one reads it.
|
|
53
|
-
const same = JSON.stringify(notice);
|
|
54
|
-
if (this.#pending.some((p) => p.to === to && JSON.stringify(p.notice) === same)) return false;
|
|
55
82
|
this.#pending.push({ id: this.#newId(), notice, to, ...(source === undefined ? {} : { source }) });
|
|
56
83
|
return true;
|
|
57
84
|
}
|
|
58
85
|
|
|
59
|
-
/** Reserve every unreserved notice in this admission's scope, in the order raised.
|
|
86
|
+
/** Reserve every unreserved notice in this admission's scope, in the order raised. Equal notices —
|
|
87
|
+
* two kills that tell the same thing — are said once, and every id is reserved. */
|
|
60
88
|
take(scope: NoticeScope): TakenNotices {
|
|
61
89
|
const taken = this.#pending.filter(
|
|
62
90
|
(p) =>
|
|
63
91
|
!this.#reserved.has(p.id) &&
|
|
92
|
+
(scope.only === undefined || (p.source !== undefined && scope.only.includes(p.source))) &&
|
|
93
|
+
(scope.except === undefined || p.source === undefined || !scope.except.includes(p.source)) &&
|
|
94
|
+
(scope.exceptPrefix === undefined || p.source === undefined || !p.source.startsWith(scope.exceptPrefix)) &&
|
|
64
95
|
(p.notice.scope === "conversation" ? p.to === scope.conversation : p.to === scope.workspace),
|
|
65
96
|
);
|
|
66
97
|
for (const p of taken) this.#reserved.add(p.id);
|
|
67
|
-
|
|
98
|
+
const said = new Set<string>();
|
|
99
|
+
const notices = taken.flatMap((p) => {
|
|
100
|
+
const key = JSON.stringify(p.notice);
|
|
101
|
+
if (said.has(key)) return [];
|
|
102
|
+
said.add(key);
|
|
103
|
+
return [p.notice];
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
ids: taken.map((p) => p.id),
|
|
107
|
+
notices,
|
|
108
|
+
raised: taken.map((p) => ({ notice: p.notice, ...(p.source === undefined ? {} : { source: p.source }) })),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Put back a notice an admission carried that no model heard, with the source it was raised
|
|
113
|
+
* with — `seen` already holds it, so a raise would drop it. Nothing changes when a notice with
|
|
114
|
+
* that source is pending already. Answers whether anything changed. */
|
|
115
|
+
restore(notice: Notice, to: string, source?: string): boolean {
|
|
116
|
+
if (source === undefined) return this.raise(notice, to);
|
|
117
|
+
if (this.#pending.some((p) => p.source === source && p.to === to)) return false;
|
|
118
|
+
this.#seen.add(source);
|
|
119
|
+
this.#pending.push({ id: this.#newId(), notice, to, source });
|
|
120
|
+
return true;
|
|
68
121
|
}
|
|
69
122
|
|
|
70
123
|
/** The admission that carried them was ledgered: they are delivered. */
|
package/src/open-body.ts
CHANGED
|
@@ -44,27 +44,31 @@ import {
|
|
|
44
44
|
type ChartTransitions,
|
|
45
45
|
} from "./chart.ts";
|
|
46
46
|
import { inputKeysOf, isEffect, type Duration } from "./effect.ts";
|
|
47
|
+
import { fail, FAILURE_GUARD, failureGuard } from "./fail.ts";
|
|
47
48
|
import { agent } from "./harness-client.ts";
|
|
48
49
|
import { SENT_BY } from "./party.ts";
|
|
49
50
|
import { wrapperBodyOf } from "./parts.ts";
|
|
50
51
|
import { actorPath, boundRun } from "./registration.ts";
|
|
51
|
-
import { jr2Setup } from "./setup.ts";
|
|
52
|
+
import { COMPILED_CHART, jr2Setup } from "./setup.ts";
|
|
52
53
|
import { attachBoundMachine, boundMachineFor } from "./vocabulary.ts";
|
|
53
54
|
|
|
55
|
+
// ADR-0070: "counters are the Open body's".
|
|
54
56
|
/**
|
|
55
|
-
* The
|
|
56
|
-
*
|
|
57
|
+
* The limits that an Open body sets on its Chart. Each one is a positive bound. When the Chart gets
|
|
58
|
+
* to one, the Open body stops it and ends with `{ outcome: "ceiling", ceiling, detail }`.
|
|
57
59
|
*/
|
|
58
60
|
export type Ceilings = {
|
|
59
|
-
/** The Agent Turns
|
|
61
|
+
/** The Agent Turns that start under the Open body, at all levels. The Turns of a lent Machine
|
|
62
|
+
* count too. */
|
|
60
63
|
turns: number;
|
|
61
|
-
/** The
|
|
64
|
+
/** The time from the start of the Open body, such as `"30m"`. At most about 596 hours. */
|
|
62
65
|
wallClock: Duration;
|
|
63
|
-
/** The
|
|
66
|
+
/** The transitions of the Chart in the whole run. xstate has no guard against a loop, so this is
|
|
67
|
+
* the guard. */
|
|
64
68
|
transitions: number;
|
|
65
|
-
/** The children
|
|
69
|
+
/** The children that run under the Open body at one time. */
|
|
66
70
|
fanOut: number;
|
|
67
|
-
/** The Chart
|
|
71
|
+
/** The size of the Chart, as JSON, in bytes. At most 1048576 (1 MiB). */
|
|
68
72
|
size: number;
|
|
69
73
|
};
|
|
70
74
|
|
|
@@ -106,8 +110,11 @@ export function jsonOver(value: unknown, cap: number): boolean {
|
|
|
106
110
|
return false;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
/** The
|
|
110
|
-
*
|
|
113
|
+
/** The parts of the Chart wrapper that a Chart can use, by their keys in `actors`, `guards`,
|
|
114
|
+
* `actions` and `delays`. `gate: true` lends the Gate, so that a Chart can wait for an answer from
|
|
115
|
+
* outside the run. `fail: true` lends the Part that fails the run, so that a Chart can end its run
|
|
116
|
+
* as a Failure (ADR-0073): the Machine decides which ends a Chart can choose. A Chart can use no
|
|
117
|
+
* other part. */
|
|
111
118
|
export type Lends = {
|
|
112
119
|
agents?: string[];
|
|
113
120
|
effects?: string[];
|
|
@@ -116,13 +123,23 @@ export type Lends = {
|
|
|
116
123
|
actions?: string[];
|
|
117
124
|
delays?: string[];
|
|
118
125
|
gate?: boolean;
|
|
126
|
+
fail?: boolean;
|
|
119
127
|
};
|
|
120
128
|
|
|
121
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* What an Open body declares: the parts that it lends a Chart, what the Agents of a Chart can
|
|
131
|
+
* have, and the limits of a Chart. Give it to {@link openBody}, and to `chartSchemaFor` for the
|
|
132
|
+
* pick of a planner.
|
|
133
|
+
*
|
|
134
|
+
* @see docs/concepts/charts.md
|
|
135
|
+
*/
|
|
122
136
|
export type OpenBodyDeclaration = {
|
|
137
|
+
/** The parts that a Chart can use. */
|
|
123
138
|
lends: Lends;
|
|
124
|
-
/** What a Chart
|
|
139
|
+
/** What the Agents that a Chart adds can have: a model from `models`, and a Workspace access
|
|
140
|
+
* from `workspace`. With no model, a Chart cannot add an Agent. */
|
|
125
141
|
allows: { models: string[]; workspace: WorkspaceAccess[] };
|
|
142
|
+
/** The limits of one Chart. */
|
|
126
143
|
ceilings: Ceilings;
|
|
127
144
|
};
|
|
128
145
|
|
|
@@ -151,6 +168,8 @@ const reservedName = (kind: (typeof LEND_KINDS)[number], v: string): string | un
|
|
|
151
168
|
if (v.includes(".")) return `"${v}" holds a dot; a lent name is a slot key, and dotted names are jr2's and xstate's`;
|
|
152
169
|
if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "gate")
|
|
153
170
|
return `"gate" is the kit's Gate; lend a slot of another name`;
|
|
171
|
+
if ((kind === "agents" || kind === "effects" || kind === "machines") && v === "fail")
|
|
172
|
+
return `"fail" is the kit's Part that fails the run, lent as \`fail: true\`; lend a slot of another name`;
|
|
154
173
|
if (kind === "actions" && (GENERIC_ACTIONS as readonly string[]).includes(v))
|
|
155
174
|
return `"${v}" is a generic action every Chart has; lend an action of another name`;
|
|
156
175
|
if (kind === "delays" && /^(0|[1-9][0-9]*)$/.test(v))
|
|
@@ -201,8 +220,12 @@ export function declarationFaults(decl: unknown): string[] {
|
|
|
201
220
|
if (typeof (lends as { gate?: unknown }).gate !== "boolean") {
|
|
202
221
|
faults.push("lends.gate: the kit's Gate is lent whole: true or false");
|
|
203
222
|
}
|
|
223
|
+
} else if (key === "fail") {
|
|
224
|
+
if (typeof (lends as { fail?: unknown }).fail !== "boolean") {
|
|
225
|
+
faults.push("lends.fail: the kit's Part that fails the run is lent whole: true or false");
|
|
226
|
+
}
|
|
204
227
|
} else if (!(LEND_KINDS as readonly string[]).includes(key)) {
|
|
205
|
-
faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")} and
|
|
228
|
+
faults.push(`lends.${key}: an Open body lends ${LEND_KINDS.join(", ")}, gate and fail only`);
|
|
206
229
|
}
|
|
207
230
|
}
|
|
208
231
|
for (const kind of LEND_KINDS) {
|
|
@@ -261,6 +284,7 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
|
|
|
261
284
|
if (v !== undefined) lends[kind] = Object.freeze([...v]) as string[];
|
|
262
285
|
}
|
|
263
286
|
if (decl.lends.gate !== undefined) lends.gate = decl.lends.gate;
|
|
287
|
+
if (decl.lends.fail !== undefined) lends.fail = decl.lends.fail;
|
|
264
288
|
return Object.freeze({
|
|
265
289
|
lends: Object.freeze(lends),
|
|
266
290
|
allows: Object.freeze({
|
|
@@ -271,11 +295,78 @@ function frozen(decl: OpenBodyDeclaration): OpenBodyDeclaration {
|
|
|
271
295
|
});
|
|
272
296
|
}
|
|
273
297
|
|
|
298
|
+
// It validates the declaration now, so a bad one fails where the Machine is written, and returns
|
|
299
|
+
// actor logic branded with it. `jr2Setup` resolves every lent name against its own `actors`,
|
|
300
|
+
// `guards`, `actions` and `delays` at createMachine, and stamps the result with `attachLent`
|
|
301
|
+
// (ADR-0070).
|
|
274
302
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
303
|
+
* Make an Open body: the actor slot that a Chart binds. Put it in `actors`, and invoke it from a
|
|
304
|
+
* state with `{ chart, ...input }`. `chart` is the text of a Chart, or `{ name }` for a named
|
|
305
|
+
* Chart of this wrapper. The other keys are the input of the Chart, which it reads as
|
|
306
|
+
* `{{input.<key>}}`. A Machine with an Open body is a Chart wrapper.
|
|
307
|
+
*
|
|
308
|
+
* Each Agent Turn of the Chart runs where the Open body runs: in the Sandbox of its `workspace()`,
|
|
309
|
+
* or on the Instance Harness. The Open body ends with an {@link OpenBodyOutput}.
|
|
310
|
+
*
|
|
311
|
+
* It refuses a declaration that is not correct: `openBody: <problem>; <problem>`, for example
|
|
312
|
+
* `ceilings.turns: missing; an Open body states every ceiling`. `createMachine` refuses a lent
|
|
313
|
+
* name that the Machine does not have: `machine "<id>": Open body "<slot>": lends.agents: "<name>"
|
|
314
|
+
* is no actor slot of this Machine; its actors: <slots>`.
|
|
315
|
+
*
|
|
316
|
+
* @param decl - What a Chart can use, and its limits (see {@link OpenBodyDeclaration}).
|
|
317
|
+
* @returns The actor logic of the slot.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* import { assign } from "xstate";
|
|
322
|
+
* import { z } from "zod";
|
|
323
|
+
* import { agent, chartSchema, fail, jr2Setup, openBody, type OpenBodyOutput } from "@jr2/orchestrator";
|
|
324
|
+
*
|
|
325
|
+
* // `chart` is a Chart, so a run input can carry one, and Charts can have names.
|
|
326
|
+
* const door = z.object({ topic: z.string(), chart: chartSchema });
|
|
327
|
+
* type Input = z.infer<typeof door>;
|
|
328
|
+
* type Context = Input & { result?: OpenBodyOutput };
|
|
329
|
+
*
|
|
330
|
+
* export const machine = jr2Setup({
|
|
331
|
+
* types: {} as { context: Context; input: Input },
|
|
332
|
+
* actors: {
|
|
333
|
+
* writer: agent({
|
|
334
|
+
* model: "anthropic/claude-haiku-5-5",
|
|
335
|
+
* workspace: "none",
|
|
336
|
+
* instructions: "You write clear, short text for engineers.",
|
|
337
|
+
* }),
|
|
338
|
+
* body: openBody({
|
|
339
|
+
* lends: { agents: ["writer"] },
|
|
340
|
+
* allows: { models: [], workspace: [] },
|
|
341
|
+
* ceilings: { turns: 6, wallClock: "15m", transitions: 200, fanOut: 1, size: 65536 },
|
|
342
|
+
* }),
|
|
343
|
+
* fail: fail(),
|
|
344
|
+
* },
|
|
345
|
+
* }).createMachine({
|
|
346
|
+
* id: "notes",
|
|
347
|
+
* input: door,
|
|
348
|
+
* context: ({ input }) => ({ ...input }),
|
|
349
|
+
* // The fault of `writer` when a Chart does not route it: the Open body sends it here.
|
|
350
|
+
* on: { "agent.fault": ".failed" },
|
|
351
|
+
* initial: "running",
|
|
352
|
+
* states: {
|
|
353
|
+
* running: {
|
|
354
|
+
* invoke: {
|
|
355
|
+
* src: "body",
|
|
356
|
+
* input: ({ context }: { context: Context }) => ({ chart: context.chart, topic: context.topic }),
|
|
357
|
+
* onDone: { target: "done", actions: assign({ result: ({ event }) => event.output as OpenBodyOutput }) },
|
|
358
|
+
* },
|
|
359
|
+
* },
|
|
360
|
+
* done: { type: "final" },
|
|
361
|
+
* failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
362
|
+
* },
|
|
363
|
+
* });
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* @see docs/concepts/charts.md
|
|
367
|
+
* @see docs/guides/run-a-chart-by-name.md
|
|
368
|
+
* @see docs/guides/let-an-agent-write-a-chart.md
|
|
369
|
+
* @see docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md
|
|
279
370
|
*/
|
|
280
371
|
export function openBody(decl: OpenBodyDeclaration): OpenBodyLogic {
|
|
281
372
|
return openBodyWith(decl);
|
|
@@ -358,7 +449,16 @@ export function lentOf(logic: OpenBodyLogic): LentParts | undefined {
|
|
|
358
449
|
/** What an Open body is invoked with: the Chart, by text or by name, and its `input` (the rest). */
|
|
359
450
|
export type OpenBodyInput = { chart: ChartText | { name: string } } & Record<string, unknown>;
|
|
360
451
|
|
|
361
|
-
/**
|
|
452
|
+
/**
|
|
453
|
+
* The output of an Open body. Route on `outcome` in the `onDone` of the state that invokes it:
|
|
454
|
+
*
|
|
455
|
+
* - `done`: the Chart got to a final state. `output` is the output of the Chart.
|
|
456
|
+
* - `refused`: the door refused the Chart. `report` has one line for each problem. Give it to the
|
|
457
|
+
* Agent that wrote the Chart, so that it can send a corrected one.
|
|
458
|
+
* - `ceiling`: the Chart got to a ceiling, and the Open body stopped it. `ceiling` names it.
|
|
459
|
+
*
|
|
460
|
+
* @see docs/concepts/charts.md
|
|
461
|
+
*/
|
|
362
462
|
export type OpenBodyOutput =
|
|
363
463
|
| { outcome: "done"; output: unknown }
|
|
364
464
|
| { outcome: "refused"; report: string[] }
|
|
@@ -863,13 +963,16 @@ export type CompileOptions = OpenBodyOptions & {
|
|
|
863
963
|
|
|
864
964
|
/**
|
|
865
965
|
* Build the Machine a Chart describes (ADR-0070), with `jr2Setup` over: the lent actors by their
|
|
866
|
-
* slot names, `agent(def)` for each Agent the Chart mints,
|
|
867
|
-
* and `jr2.
|
|
868
|
-
* resolves at invoke time, so the Machine holds the
|
|
966
|
+
* slot names, `agent(def)` for each Agent the Chart mints, the kit's `gate`, and `fail()` where the
|
|
967
|
+
* body lends it; the lent guards, `jr2.sentBy` and `jr2.failure`; the lent actions and the four
|
|
968
|
+
* generic ones; the lent delays. Every reference resolves at invoke time, so the Machine holds the
|
|
969
|
+
* text's shape and no run's data. The Chart's
|
|
869
970
|
* input is the context key {@link CHART_INPUT_KEY}; a done Chart outputs `{ outcome: "done",
|
|
870
971
|
* output }`, its `output` resolved (or, without one, its final state's). The transitions ceiling
|
|
871
972
|
* is xstate's `maxIterations` as well, so a guarded `always` loop that never ends in one macrostep
|
|
872
|
-
* still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here
|
|
973
|
+
* still ends. `jr2Setup`'s own refusals (a parallel state below an Agent's) throw here, but not its
|
|
974
|
+
* `agent.fault` refusal: in a Chart that route is optional, and the body sends an `agent.fault` that
|
|
975
|
+
* no transition of the Chart takes to the Machine that invoked it (ADR-0074).
|
|
873
976
|
*/
|
|
874
977
|
export function compileChart(
|
|
875
978
|
admitted: AdmittedChart,
|
|
@@ -882,6 +985,8 @@ export function compileChart(
|
|
|
882
985
|
const minted = mapValues(admitted.agents, (def) => def);
|
|
883
986
|
const actors: Record<string, UnknownActorLogic> = chartActors(lent.actors);
|
|
884
987
|
for (const [slot, def] of Object.entries(minted)) actors[slot] = mint(slot, def);
|
|
988
|
+
// The kit's Part that fails the run, where the body lends it (ADR-0073): no slot of the wrapper's.
|
|
989
|
+
if (decl.lends.fail === true) actors.fail = fail();
|
|
885
990
|
// The slots an invoke runs an Agent Turn in: the lent Agents and the Chart's own.
|
|
886
991
|
const agentSlots = new Set([
|
|
887
992
|
...Object.keys(lent.actors).filter((slot) => isAgent(lent.actors[slot])),
|
|
@@ -905,11 +1010,15 @@ export function compileChart(
|
|
|
905
1010
|
: (event as { output?: unknown } | undefined)?.output,
|
|
906
1011
|
}) satisfies OpenBodyOutput,
|
|
907
1012
|
options: { maxIterations: decl.ceilings.transitions },
|
|
1013
|
+
// In a Chart the `agent.fault` route is optional (ADR-0070): the Open body sends an `agent.fault`
|
|
1014
|
+
// that no transition takes to its parent, so `createMachine` does not refuse a state with none.
|
|
1015
|
+
[COMPILED_CHART]: true,
|
|
908
1016
|
};
|
|
909
1017
|
return jr2Setup({
|
|
910
1018
|
events: admitted.events,
|
|
911
1019
|
actors,
|
|
912
|
-
|
|
1020
|
+
// `jr2.failure` is in every Chart, as `jr2.sentBy` is: a predicate, with no capability (ADR-0073).
|
|
1021
|
+
guards: { ...(lent.guards as Record<string, unknown>), [FAILURE_GUARD]: failureGuard } as never,
|
|
913
1022
|
actions: { ...(lent.actions as Record<string, unknown>), ...GENERIC } as never,
|
|
914
1023
|
delays: lent.delays as never,
|
|
915
1024
|
}).createMachine(config as never) as unknown as AnyStateMachine;
|
|
@@ -1097,6 +1206,20 @@ function watchTurns(self: AnyActorRef, turns: number, ceiling: number): TurnWatc
|
|
|
1097
1206
|
return watch;
|
|
1098
1207
|
}
|
|
1099
1208
|
|
|
1209
|
+
/**
|
|
1210
|
+
* Does a transition of the Chart take this event now? xstate's own selection, guards and all. A
|
|
1211
|
+
* guard that throws (a reference that resolved to nothing) counts as a take: the step then runs
|
|
1212
|
+
* the guard again, and settles the body `refused` with the reference named.
|
|
1213
|
+
*/
|
|
1214
|
+
function takes(machine: AnyStateMachine, snapshot: BodySnapshot, event: AnyEventObject): boolean {
|
|
1215
|
+
try {
|
|
1216
|
+
return machine.getTransitionData(snapshot as never, event as never).length > 0;
|
|
1217
|
+
} catch {
|
|
1218
|
+
runFault = undefined;
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1100
1223
|
/** The actor logic of an Open body: a custom ActorLogic that delegates to the compiled Chart. */
|
|
1101
1224
|
export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOptions = {}): UnknownActorLogic {
|
|
1102
1225
|
const wallMs = durationMs(decl.ceilings.wallClock) ?? Number.POSITIVE_INFINITY;
|
|
@@ -1392,6 +1515,19 @@ export function openBodyLogic(decl: OpenBodyDeclaration, options: OpenBodyOption
|
|
|
1392
1515
|
ceiling("transitions", transitionsDetail(fields.counters.transitions), snapshot, fields, scope),
|
|
1393
1516
|
);
|
|
1394
1517
|
}
|
|
1518
|
+
// An `agent.fault` that no transition of the Chart takes goes to the Machine that invoked the
|
|
1519
|
+
// body, unchanged (ADR-0074): in a Chart the route is optional, and the parent's `agent.fault`
|
|
1520
|
+
// route is the route for a Chart's Agents too. The body does not step the Chart: the parent's
|
|
1521
|
+
// route decides, and its exit stops the body. With no parent route that takes it, the run
|
|
1522
|
+
// host faults the run, as for any `agent.fault` that no transition took.
|
|
1523
|
+
if (event.type === "agent.fault" && !takes(machine, snapshot, event)) {
|
|
1524
|
+
// Relayed from the body, as `sendBack` relays from an Agent: the run host reads a relayed
|
|
1525
|
+
// event's source, so a pick held in the parent is answered as for an Agent of its own.
|
|
1526
|
+
const parent = scope.self._parent;
|
|
1527
|
+
const system = scope.system as unknown as { _relay(from: unknown, to: unknown, e: AnyEventObject): void };
|
|
1528
|
+
if (parent) scope.defer(() => system._relay(scope.self, parent, event));
|
|
1529
|
+
return snapshot;
|
|
1530
|
+
}
|
|
1395
1531
|
return release(
|
|
1396
1532
|
scope.self,
|
|
1397
1533
|
step(
|
package/src/open-effect.ts
CHANGED
|
@@ -25,18 +25,82 @@
|
|
|
25
25
|
import type { ActorLogic, AnyActorRef, EventObject } from "xstate";
|
|
26
26
|
import { open } from "./open.ts";
|
|
27
27
|
import type { Arg, EffectLogic, EffectParts, EffectSnapshot } from "./effect.ts";
|
|
28
|
-
import type { Text } from "./http-effect.ts";
|
|
28
|
+
import type { Secret, Text } from "./http-effect.ts";
|
|
29
29
|
|
|
30
|
-
/** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name
|
|
31
|
-
|
|
30
|
+
/** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name,
|
|
31
|
+
* a dispatch's target (its url and token together, ADR-0071). */
|
|
32
|
+
export type OpenEffectPart = "url" | "argv" | "secret" | "target";
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// What a composer binds an Effect's Open parts to, each key one part (ADR-0054); a dispatch's
|
|
35
|
+
// target is its url and token, bound as one (ADR-0071). One object for an
|
|
36
|
+
// Effect with two Open parts. `customize()` reads each Effect's own binding type off its logic
|
|
37
|
+
// (`BindingOf`, customize.ts), so this loose shape is what the implementation walks.
|
|
38
|
+
/**
|
|
39
|
+
* The values that bind the Open parts of one Effect. A packaged Machine leaves a part of an Effect
|
|
40
|
+
* Open when it cannot know it. That part is the url of an `http` Effect, the argv of a `command`,
|
|
41
|
+
* the name of a secret, written `secret(open)`, or the target of a `dispatch`: the url of another
|
|
42
|
+
* Orchestrator and the secret that holds its Instance token, bound as one. You bind it once, where you register the
|
|
43
|
+
* Machine, with `customize(machine, { effects: { <slot>: binding } })`. Each key binds one Open
|
|
44
|
+
* part. For an Effect with two Open parts, one object holds both keys.
|
|
45
|
+
*
|
|
46
|
+
* The binding is a value, never a function of the run: no run chooses an Effect's url, argv or
|
|
47
|
+
* secret. `customize()` builds the Effect again with the bound parts, so a bound part gets every
|
|
48
|
+
* check that a part written in the definition gets.
|
|
49
|
+
*
|
|
50
|
+
* `customize()` refuses:
|
|
51
|
+
* - an empty binding, or a key for a part that is not Open: `customize(): the <kind> Effect
|
|
52
|
+
* "<slot>" leaves its <parts> Open — bind it with <binding> (ADR-0054)`
|
|
53
|
+
* - an Effect with no Open part: ``customize(): the <kind> Effect "<slot>" of machine "<id>"
|
|
54
|
+
* leaves nothing Open — its parts are fixed in its definition, and only a part a package left
|
|
55
|
+
* `open` is bound, once (ADR-0054).``
|
|
56
|
+
* - `open` as a value: ``customize(): the <kind> Effect "<slot>"'s <part> is bound once, to a
|
|
57
|
+
* value — not `open` again: no run chooses an Effect's <part> (ADR-0054)``. A function as a
|
|
58
|
+
* value gives the same text, with `a function of the run` for `` `open` again ``.
|
|
59
|
+
* - a secret that is not a string: `customize(): the <kind> Effect "<slot>"'s secret is bound to
|
|
60
|
+
* a secret's name, a string — the file the Effector reads it from: { secret: "<name>" }
|
|
61
|
+
* (ADR-0054, ADR-0068)`
|
|
62
|
+
*
|
|
63
|
+
* `jr2 up` refuses a registered Machine with an Open part that nobody bound, and prints the
|
|
64
|
+
* `customize` line that binds it.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { customize, http, jr2Setup, open, type EffectBinding } from "@jr2/orchestrator";
|
|
69
|
+
*
|
|
70
|
+
* const notifier = jr2Setup({
|
|
71
|
+
* actors: { notify: http({ method: "POST", url: open }) },
|
|
72
|
+
* }).createMachine({
|
|
73
|
+
* id: "notifier",
|
|
74
|
+
* initial: "sending",
|
|
75
|
+
* states: {
|
|
76
|
+
* sending: { invoke: { src: "notify", onDone: "done" } },
|
|
77
|
+
* done: { type: "final" },
|
|
78
|
+
* },
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* const notify = { url: "https://hooks.example.com/build" } satisfies EffectBinding;
|
|
82
|
+
*
|
|
83
|
+
* export const machine = customize(notifier, { effects: { notify } });
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @see docs/guides/bind-open-parts.md
|
|
87
|
+
* @see docs/concepts/effects.md
|
|
88
|
+
* @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
|
|
89
|
+
* @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
|
|
90
|
+
*/
|
|
36
91
|
export type EffectBinding = {
|
|
92
|
+
/** The url of an `http` Effect whose url is Open: a string, or a `text` template with a secret
|
|
93
|
+
* in its path, such as `` text`https://hooks.example.com/services/${secret("hook")}` ``. Its
|
|
94
|
+
* scheme and host must be literal. */
|
|
37
95
|
readonly url?: string | Text;
|
|
96
|
+
/** The argv of a `command` whose argv is Open: one entry for each argument, never shell text.
|
|
97
|
+
* An entry is a string or an `arg("<param>")` that the invoke's input fills. It must not be
|
|
98
|
+
* empty. */
|
|
38
99
|
readonly argv?: readonly (string | Arg)[];
|
|
100
|
+
/** The name of the secret that `secret(open)` stands for: an entry of `secrets` in
|
|
101
|
+
* `jr2.config.ts`. The Effector reads it from a file of that name. */
|
|
39
102
|
readonly secret?: string;
|
|
103
|
+
readonly target?: { readonly url: string; readonly token: Secret };
|
|
40
104
|
};
|
|
41
105
|
|
|
42
106
|
declare const openPart: unique symbol;
|
|
@@ -46,10 +110,16 @@ declare const openPart: unique symbol;
|
|
|
46
110
|
* `effects` offers exactly the Open slots, each with the binding its parts take — the intersection
|
|
47
111
|
* of each part's, for an Effect with two. Phantom, like `JR2Wrapper`: the property exists in the
|
|
48
112
|
* type alone.
|
|
113
|
+
*
|
|
114
|
+
* @internal
|
|
49
115
|
*/
|
|
50
116
|
export type OpenEffect<P extends OpenEffectPart> = { readonly [openPart]: P };
|
|
51
117
|
|
|
52
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* The phantom for these Open parts, or nothing for none: what a constructor's type adds.
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
53
123
|
export type OpenOf<P extends OpenEffectPart> = [P] extends [never] ? unknown : OpenEffect<P>;
|
|
54
124
|
|
|
55
125
|
/** The binder an Open Effect carries, under a `Symbol.for` key so a second copy of this module (an
|
|
@@ -58,11 +128,13 @@ const BIND: unique symbol = Symbol.for("jr2.effect.bind");
|
|
|
58
128
|
|
|
59
129
|
type Binder = (binding: EffectBinding) => EffectLogic<unknown, unknown>;
|
|
60
130
|
|
|
61
|
-
/** Which parts of these parts are Open, in binding order: the url or the
|
|
131
|
+
/** Which parts of these parts are Open, in binding order: the url, the argv or the target, then the
|
|
132
|
+
* secret. A dispatch's target is one part: its token is never `secret(open)` (ADR-0071). */
|
|
62
133
|
export function openPartsOf(parts: EffectParts): OpenEffectPart[] {
|
|
63
134
|
const out: OpenEffectPart[] = [];
|
|
64
135
|
if (parts.kind === "http" && parts.url === open) out.push("url");
|
|
65
136
|
if (parts.kind === "command" && parts.argv === open) out.push("argv");
|
|
137
|
+
if (parts.kind === "dispatch" && parts.target === open) out.push("target");
|
|
66
138
|
if (parts.kind !== "push" && parts.openSecret) out.push("secret");
|
|
67
139
|
return out;
|
|
68
140
|
}
|
|
@@ -78,6 +150,7 @@ const BINDING_SAMPLE: Record<OpenEffectPart, string> = {
|
|
|
78
150
|
url: 'url: "https://<host>/<path>"',
|
|
79
151
|
argv: 'argv: ["<program>", "<arg>"]',
|
|
80
152
|
secret: 'secret: "<name>"',
|
|
153
|
+
target: 'target: { url: "https://<host>", token: secret("<name>") }',
|
|
81
154
|
};
|
|
82
155
|
|
|
83
156
|
/** One binding object for these Open parts — `{ url: "…", secret: "<name>" }` for two. */
|
package/src/open.ts
CHANGED
|
@@ -5,13 +5,66 @@
|
|
|
5
5
|
// and a `workspace()`'s Repo Slots (parts.ts, which already imports agent.ts). A module with
|
|
6
6
|
// nothing but the sentinel in it is what lets both import it and neither import the other.
|
|
7
7
|
|
|
8
|
+
// `Symbol.for`, so an Instance's own copy of this module and the CLI's walk agree on it — a
|
|
9
|
+
// packaged Machine may be built against one and walked by the other.
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* package cannot know
|
|
12
|
-
*
|
|
11
|
+
* Leave a part of a Machine Open: for the Instance that registers the Machine to bind with
|
|
12
|
+
* `customize()`. Write it where a value goes, in a Machine that you publish as a package, for a
|
|
13
|
+
* part that the package cannot know or pay for:
|
|
14
|
+
* - a Repo Slot: `repos: { app: open }`, or the whole map: `repos: open`;
|
|
15
|
+
* - an Agent's model: `agent({ model: open, … })`;
|
|
16
|
+
* - an Effect's url or argv: `http({ url: open, … })`, `command(open)`;
|
|
17
|
+
* - the secret of an Effect's auth, of a header, or of a command's `env`: `secret(open)`.
|
|
13
18
|
*
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
19
|
+
* `jr2 up` refuses a Workflow with an Open part that nobody bound, and prints the `customize()`
|
|
20
|
+
* line that binds it: ``refusing: workflow "<name>" leaves <part> open — bind it where the Machine
|
|
21
|
+
* is registered: export const machine = customize(<import>, …) (<adr>)``. A run never sees an
|
|
22
|
+
* Open part.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { z } from "zod";
|
|
27
|
+
* import { agent, customize, defineEvent, fail, jr2Setup, open, workspace } from "@jr2/orchestrator";
|
|
28
|
+
*
|
|
29
|
+
* const finish = defineEvent({
|
|
30
|
+
* name: "finish",
|
|
31
|
+
* description: "Call this when `node --test` passes.",
|
|
32
|
+
* input: z.object({}),
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* const body = jr2Setup({
|
|
36
|
+
* events: [finish],
|
|
37
|
+
* actors: { coder: agent({ model: open, instructions: "You fix the failing test." }), fail: fail() },
|
|
38
|
+
* }).createMachine({
|
|
39
|
+
* id: "fix",
|
|
40
|
+
* on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
|
|
41
|
+
* initial: "coding",
|
|
42
|
+
* states: {
|
|
43
|
+
* coding: {
|
|
44
|
+
* invoke: { src: "coder", input: { prompt: "Make `node --test` pass." } },
|
|
45
|
+
* on: { finish: "done" },
|
|
46
|
+
* },
|
|
47
|
+
* done: { type: "final" },
|
|
48
|
+
* failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
|
|
49
|
+
* lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
50
|
+
* },
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // What a package exports: the repository and the model are Open.
|
|
54
|
+
* export const fix = workspace(body, {
|
|
55
|
+
* repos: { app: open },
|
|
56
|
+
* spec: () => ({ branch: "jr2/fix" }),
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // What an Instance's `workflows/fix.ts` exports: the same Machine, its Open parts bound.
|
|
60
|
+
* export const machine = customize(fix, {
|
|
61
|
+
* repos: { app: { url: "https://github.com/you/app.git" } },
|
|
62
|
+
* agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @see docs/guides/bind-open-parts.md
|
|
67
|
+
* @see docs/concepts/composition.md
|
|
68
|
+
* @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
|
|
16
69
|
*/
|
|
17
70
|
export const open: unique symbol = Symbol.for("jr2.open");
|