@jr2/orchestrator 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console/components/app.ts +55 -15
- package/console/components/bottom-drawer.ts +259 -0
- package/console/components/drawer.ts +14 -6
- package/console/components/fleet.ts +36 -13
- package/console/components/machine-pane.ts +61 -5
- package/console/components/nav.ts +54 -8
- package/console/conversation.ts +161 -0
- package/console/login.ts +123 -0
- package/console/main.ts +600 -60
- package/console/store.ts +611 -64
- package/console/style.css +231 -0
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +142 -29
- package/docs/concepts/architecture.md +60 -34
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +13 -8
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +7 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +133 -50
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +14 -4
- package/docs/concepts/pools-and-sources.md +193 -17
- package/docs/concepts/secrets-and-trust.md +186 -62
- package/docs/concepts/the-menu.md +14 -0
- package/docs/concepts/workspaces.md +135 -56
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
- package/docs/get-started/install.md +4 -0
- package/docs/guides/add-tools-to-the-sandbox.md +72 -15
- package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
- package/docs/guides/answer-a-gate.md +74 -37
- package/docs/guides/bind-open-parts.md +29 -12
- package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
- package/docs/guides/cancel-a-run.md +26 -16
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +19 -7
- package/docs/guides/give-a-caller-a-scope.md +203 -0
- package/docs/guides/give-an-agent-mcp-tools.md +422 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
- package/docs/guides/let-an-agent-write-a-chart.md +14 -7
- package/docs/guides/look-inside-a-sandbox.md +115 -42
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +34 -27
- package/docs/guides/set-an-agents-access-and-dials.md +69 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/start-a-run-for-each-alert.md +290 -0
- package/docs/guides/start-a-run-from-a-run.md +18 -16
- package/docs/guides/start-a-run.md +24 -18
- package/docs/guides/steer-an-agent.md +332 -0
- package/docs/guides/use-a-hosted-provider.md +7 -1
- package/docs/guides/watch-a-run.md +146 -117
- package/docs/guides/write-a-hook-kind.md +238 -0
- package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
- package/docs/guides/write-a-workflow.md +18 -8
- package/docs/llms.txt +110 -23
- package/docs/reference/authoring-surface/agent.md +35 -12
- package/docs/reference/authoring-surface/answer.md +20 -4
- package/docs/reference/authoring-surface/arg.md +28 -6
- package/docs/reference/authoring-surface/basic.md +1 -1
- package/docs/reference/authoring-surface/bearer.md +1 -1
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/carry.md +23 -0
- package/docs/reference/authoring-surface/chartschema.md +1 -1
- package/docs/reference/authoring-surface/chartschemafor.md +1 -1
- package/docs/reference/authoring-surface/command.md +37 -22
- package/docs/reference/authoring-surface/customize.md +4 -1
- package/docs/reference/authoring-surface/defineconfig.md +1 -1
- package/docs/reference/authoring-surface/defineevent.md +1 -1
- package/docs/reference/authoring-surface/dispatch.md +5 -4
- package/docs/reference/authoring-surface/fail.md +18 -5
- package/docs/reference/authoring-surface/file.md +37 -17
- package/docs/reference/authoring-surface/frompick.md +1 -1
- package/docs/reference/authoring-surface/gate.md +118 -0
- package/docs/reference/authoring-surface/group-agents.md +15 -0
- package/docs/reference/authoring-surface/group-charts.md +16 -0
- package/docs/reference/authoring-surface/group-composition.md +25 -0
- package/docs/reference/authoring-surface/group-config.md +17 -0
- package/docs/reference/authoring-surface/group-effects.md +48 -0
- package/docs/reference/authoring-surface/group-failure.md +13 -0
- package/docs/reference/authoring-surface/group-gates.md +16 -0
- package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
- package/docs/reference/authoring-surface/group-steering.md +12 -0
- package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
- package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
- package/docs/reference/authoring-surface/http.md +109 -9
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +14 -4
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +41 -8
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +23 -5
- package/docs/reference/authoring-surface/pool.md +79 -11
- package/docs/reference/authoring-surface/push.md +31 -13
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +26 -5
- package/docs/reference/authoring-surface/sentby.md +1 -1
- package/docs/reference/authoring-surface/skip.md +22 -0
- package/docs/reference/authoring-surface/source.md +59 -4
- package/docs/reference/authoring-surface/steer.md +93 -0
- package/docs/reference/authoring-surface/text.md +1 -1
- package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- package/docs/reference/authoring-surface/type-agentslots.md +4 -1
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +6 -7
- package/docs/reference/authoring-surface/type-caller.md +40 -0
- package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
- package/docs/reference/authoring-surface/type-carry.md +19 -0
- package/docs/reference/authoring-surface/type-chainlink.md +3 -3
- package/docs/reference/authoring-surface/type-commandinput.md +4 -3
- package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
- package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
- package/docs/reference/authoring-surface/type-customize.md +20 -3
- package/docs/reference/authoring-surface/type-delivered.md +17 -0
- package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
- package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- package/docs/reference/authoring-surface/type-filebody.md +4 -8
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
- package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
- package/docs/reference/authoring-surface/type-gatelook.md +37 -0
- package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
- package/docs/reference/authoring-surface/type-gatereply.md +19 -0
- package/docs/reference/authoring-surface/type-gateslot.md +21 -0
- package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
- package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
- package/docs/reference/authoring-surface/type-gateslots.md +12 -0
- package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
- package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +5 -1
- package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
- package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
- package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
- package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
- package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
- package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
- package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
- package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
- package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +11 -6
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
- package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
- package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
- package/docs/reference/authoring-surface/type-reposlot.md +6 -6
- package/docs/reference/authoring-surface/type-reread.md +18 -0
- package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-sender.md +26 -1
- package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
- package/docs/reference/authoring-surface/type-size.md +1 -1
- package/docs/reference/authoring-surface/type-skip.md +19 -0
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
- package/docs/reference/authoring-surface/type-sourcein.md +25 -0
- package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
- package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
- package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
- package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
- package/docs/reference/authoring-surface/type-steerargs.md +20 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
- package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
- package/docs/reference/authoring-surface/type-workspaced.md +30 -9
- package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
- package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +66 -38
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +88 -147
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +248 -6
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
- package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
- package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
- package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
- package/docs/reference/decisions.md +9 -4
- package/docs/reference/glossary.md +138 -45
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +988 -123
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +7 -5
- package/package.json +10 -3
- package/src/actor.ts +194 -42
- package/src/agent.ts +56 -6
- package/src/ambient.ts +3 -5
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +238 -26
- package/src/charts.ts +9 -2
- package/src/config.ts +566 -7
- package/src/conversations.ts +453 -0
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +257 -77
- package/src/customize.ts +154 -7
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +52 -4
- package/src/effect.ts +401 -132
- package/src/effector-wire.ts +278 -30
- package/src/fail.ts +17 -4
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +84 -35
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +197 -39
- package/src/holds.ts +5 -2
- package/src/hook-kinds/discord.ts +92 -0
- package/src/hook-kinds/door.ts +107 -0
- package/src/hook-kinds/github.ts +89 -0
- package/src/hook-kinds/gitlab.ts +72 -0
- package/src/hook-kinds/hook.ts +7 -0
- package/src/hook-kinds/index.ts +465 -0
- package/src/hook-kinds/slack.ts +151 -0
- package/src/hook-kinds/webhook.ts +113 -0
- package/src/hooks.ts +506 -0
- package/src/http-effect.ts +164 -70
- package/src/http.ts +933 -99
- package/src/index.ts +30 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +23 -3
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +29 -5
- package/src/mcp-effect.ts +424 -0
- package/src/model-keys.ts +24 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +82 -7
- package/src/notices.ts +576 -59
- package/src/open-body.ts +110 -30
- package/src/open-effect.ts +17 -4
- package/src/open.ts +40 -7
- package/src/parts.ts +125 -35
- package/src/party.ts +35 -1
- package/src/pick.ts +43 -9
- package/src/pool.ts +1172 -172
- package/src/registration.ts +249 -17
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +1614 -190
- package/src/run-list.ts +191 -0
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +116 -21
- package/src/secret-placement.ts +248 -35
- package/src/server.ts +53 -4
- package/src/setup.ts +158 -38
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +362 -46
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +27 -10
- package/src/workspace.ts +314 -259
- package/src/worktree.ts +249 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
- package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/chart.ts
CHANGED
|
@@ -19,11 +19,13 @@
|
|
|
19
19
|
// (open-body.ts), so that `jr2Setup`'s own refusals and load warnings are lines of the same report.
|
|
20
20
|
|
|
21
21
|
import { createHash } from "node:crypto";
|
|
22
|
+
import { posix } from "node:path";
|
|
22
23
|
import { z } from "zod";
|
|
23
24
|
import { defineEvent, type EventAudience, type EventDef } from "@jr2/agent-protocol";
|
|
24
25
|
import type { AgentDefinition, ThinkingLevel, WorkspaceAccess } from "./agent.ts";
|
|
25
|
-
import { inputKeysOf, isEffect } from "./effect.ts";
|
|
26
|
+
import { inputKeysOf, inputShapeOf, isEffect, type InputShape } from "./effect.ts";
|
|
26
27
|
import { FAILURE_GUARD } from "./fail.ts";
|
|
28
|
+
import { isGateSlot } from "./gate.ts";
|
|
27
29
|
import { compileChart, durationMs, type LentParts, type OpenBodyDeclaration } from "./open-body.ts";
|
|
28
30
|
import { SENT_BY } from "./party.ts";
|
|
29
31
|
import { loadWarningsOf } from "./vocabulary.ts";
|
|
@@ -140,6 +142,10 @@ const MECHANISM_EVENTS: Record<string, { fields: Record<string, boolean> }> = {
|
|
|
140
142
|
* placement; see {@link checkAgentInput}). */
|
|
141
143
|
const AGENT_INPUT_KEYS = ["prompt", "cwd", "continue", "model", "thinkingLevel"] as const;
|
|
142
144
|
|
|
145
|
+
/** The kit's test for a cut that settled `ready`, in every Chart beside `jr2.failure`: a cut's own
|
|
146
|
+
* outcomes are output, so a Chart routes on them, and a Chart has no other way to read one. */
|
|
147
|
+
export const READY_GUARD = "jr2.ready";
|
|
148
|
+
|
|
143
149
|
/**
|
|
144
150
|
* The Chart briefing (ADR-0070): jr2's text on how a Chart runs, for whoever writes one. It is the
|
|
145
151
|
* grammar's root description, so it rides `GET /workflows/<wrapper>/chart-schema` and a pick's tool
|
|
@@ -164,6 +170,16 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
164
170
|
? ' `src: "fail"` with `input: { reason }` ends the run as a Failure: work that did not succeed, with ' +
|
|
165
171
|
`the reason for a person; an \`onError\` with the guard \`${FAILURE_GUARD}\` catches it. A final state means done.`
|
|
166
172
|
: "";
|
|
173
|
+
// A declaration is names alone, so this says "a lent Effect that cuts".
|
|
174
|
+
const cut =
|
|
175
|
+
narrow === undefined || narrow.lendsEffects
|
|
176
|
+
? ' In a Workspace with Repo Slots, work happens in a Worktree. A lent Effect that cuts one takes `input: { path: "/work/<slot>/<name>", ' +
|
|
177
|
+
'branch: "<branch>" }`: `<name>` is one folder name that you choose, and it is not the branch. The Open body sets the cut\'s `expect`, ' +
|
|
178
|
+
"so its input has no `expect`. Give its `onDone` two edges: the guard " +
|
|
179
|
+
`\`${READY_GUARD}\` on the one that goes on, and one with no guard for a refused cut. Then write that same literal path as \`cwd\` in an ` +
|
|
180
|
+
"Agent's `input` and as `worktree` in an Effect's `input`: each Turn and each Effect there must say where it works. " +
|
|
181
|
+
"The slot's folder `/work/<slot>` is not a Worktree."
|
|
182
|
+
: "";
|
|
167
183
|
const agents = mints
|
|
168
184
|
? `A state runs an Agent with \`invoke: { src, input: { prompt } }\`: \`src\` is a lent Agent's name${lent}. ` +
|
|
169
185
|
"This Chart mints no Agent of its own: there is no `agents`."
|
|
@@ -171,7 +187,7 @@ export function chartBriefing(narrow?: Narrow): string {
|
|
|
171
187
|
"a key of `agents`; a lent Agent needs no entry in `agents`, which mints new ones.";
|
|
172
188
|
return (
|
|
173
189
|
`A Chart is a workflow written as data: states, the events its Agents pick${mints ? "" : ", and the Agents it mints"}. ` +
|
|
174
|
-
`${agents}${gate}${fail} An Agent's Turn ends only when it ` +
|
|
190
|
+
`${agents}${gate}${fail}${cut} An Agent's Turn ends only when it ` +
|
|
175
191
|
"picks one of the Chart's `events`: declare each pick in `events` with an `input` schema " +
|
|
176
192
|
'(`{ "type": "object", "properties": { "<field>": { "type": "string" } }, "required": ["<field>"] }`; a field not in ' +
|
|
177
193
|
"`required` may be absent), and " +
|
|
@@ -238,6 +254,7 @@ export function pathSegments(path: string): Array<string | number> {
|
|
|
238
254
|
type Narrow = {
|
|
239
255
|
srcs: string[];
|
|
240
256
|
lentAgents: string[];
|
|
257
|
+
lendsEffects: boolean;
|
|
241
258
|
gate: boolean;
|
|
242
259
|
fail: boolean;
|
|
243
260
|
guards: string[];
|
|
@@ -363,16 +380,16 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
363
380
|
// `jr2.sentBy` is the kit's routing guard (ADR-0015), and `jr2.failure` its test for a Failure on
|
|
364
381
|
// an `onError` (ADR-0073): every Chart has both, and neither is lent.
|
|
365
382
|
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) });
|
|
383
|
+
const failureArm = z.strictObject({ type: z.union([z.literal(FAILURE_GUARD), z.literal(READY_GUARD)]) });
|
|
367
384
|
const lentGuardName = narrow
|
|
368
|
-
? enumOf([...narrow.guards, FAILURE_GUARD])
|
|
385
|
+
? enumOf([...narrow.guards, FAILURE_GUARD, READY_GUARD])
|
|
369
386
|
: z.string().refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` });
|
|
370
387
|
const paramGuardName = narrow
|
|
371
388
|
? enumOf(narrow.guards)
|
|
372
389
|
: z
|
|
373
390
|
.string()
|
|
374
391
|
.refine((t) => t !== SENT_BY, { message: `"${SENT_BY}" takes params { id }` })
|
|
375
|
-
.refine((t) => t !== FAILURE_GUARD, { message:
|
|
392
|
+
.refine((t) => t !== FAILURE_GUARD && t !== READY_GUARD, { message: "that guard takes no params" });
|
|
376
393
|
const guard: z.ZodType<ChartGuard> = z
|
|
377
394
|
.union([
|
|
378
395
|
lentGuardName,
|
|
@@ -397,7 +414,7 @@ function grammar(narrow?: Narrow): z.ZodType<ChartText> {
|
|
|
397
414
|
])
|
|
398
415
|
.meta({
|
|
399
416
|
id: `${ID_PREFIX}ChartGuard`,
|
|
400
|
-
description: `A lent guard by name, \`jr2.sentBy\`, \`${FAILURE_GUARD}\`, or not/and/or of guards.`,
|
|
417
|
+
description: `A lent guard by name, \`jr2.sentBy\`, \`${FAILURE_GUARD}\`, \`${READY_GUARD}\`, or not/and/or of guards.`,
|
|
401
418
|
}) as never;
|
|
402
419
|
|
|
403
420
|
const lentActionName = narrow
|
|
@@ -657,6 +674,7 @@ function lendsOf(decl: OpenBodyDeclaration) {
|
|
|
657
674
|
agents: decl.lends.agents ?? [],
|
|
658
675
|
effects: decl.lends.effects ?? [],
|
|
659
676
|
machines: decl.lends.machines ?? [],
|
|
677
|
+
gates: decl.lends.gates ?? [],
|
|
660
678
|
guards: decl.lends.guards ?? [],
|
|
661
679
|
actions: decl.lends.actions ?? [],
|
|
662
680
|
delays: decl.lends.delays ?? [],
|
|
@@ -679,8 +697,16 @@ export function narrowedChartSchema(decl: OpenBodyDeclaration): Record<string, u
|
|
|
679
697
|
function narrowOf(decl: OpenBodyDeclaration): Narrow {
|
|
680
698
|
const l = lendsOf(decl);
|
|
681
699
|
return {
|
|
682
|
-
srcs: [
|
|
700
|
+
srcs: [
|
|
701
|
+
...l.agents,
|
|
702
|
+
...l.effects,
|
|
703
|
+
...l.machines,
|
|
704
|
+
...l.gates,
|
|
705
|
+
...(l.gate ? ["gate"] : []),
|
|
706
|
+
...(l.fail ? ["fail"] : []),
|
|
707
|
+
],
|
|
683
708
|
lentAgents: l.agents,
|
|
709
|
+
lendsEffects: l.effects.length > 0,
|
|
684
710
|
gate: l.gate,
|
|
685
711
|
fail: l.fail,
|
|
686
712
|
guards: l.guards,
|
|
@@ -836,8 +862,22 @@ export type AdmittedChart = {
|
|
|
836
862
|
agents: Record<string, AgentDefinition>;
|
|
837
863
|
/** The bytes of the text's canonical JSON. */
|
|
838
864
|
size: number;
|
|
865
|
+
/** The invoke ids of the Chart's Gates (ADR-0011), as `jr2Setup` names them: the names that the
|
|
866
|
+
* `answer` scope can name them by (ADR-0078). */
|
|
867
|
+
gates: string[];
|
|
839
868
|
};
|
|
840
869
|
|
|
870
|
+
/**
|
|
871
|
+
* Why a Chart's `cwd` is not a place to work, or undefined when it can be: a Repo Slot's folder is no
|
|
872
|
+
* Worktree. There, an Agent's search hits `default/`, and a relative write lands outside each
|
|
873
|
+
* Worktree (ADR-0057, ADR-0075).
|
|
874
|
+
*/
|
|
875
|
+
export function slotFolderFault(cwd: string, slots: readonly string[]): string | undefined {
|
|
876
|
+
const slot = posix.normalize(cwd).match(/^\/work\/([^/]+)\/?$/)?.[1];
|
|
877
|
+
if (slot === undefined || !slots.includes(slot)) return undefined;
|
|
878
|
+
return `"${cwd}" is a Repo Slot's folder, not a Worktree; \`cwd\` is a Worktree that a cut made, "/work/${slot}/<name>", or "/work/${slot}/default" to read, or "/work"`;
|
|
879
|
+
}
|
|
880
|
+
|
|
841
881
|
/** What the door knows beyond the declaration and the lent set. */
|
|
842
882
|
export type AdmitOptions = {
|
|
843
883
|
/**
|
|
@@ -846,6 +886,11 @@ export type AdmitOptions = {
|
|
|
846
886
|
* `input.<key>` reference is checked against it; absent, `input` references are not checked.
|
|
847
887
|
*/
|
|
848
888
|
inputKeys?: readonly string[];
|
|
889
|
+
/** The Repo Slots of the Workspace around the Open body, where the door can read them. Given and
|
|
890
|
+
* not empty, a Turn's `cwd` and an Effect's `worktree` are required, a literal path's slot is
|
|
891
|
+
* checked, and a literal `cwd` cannot be a slot's folder. Absent: the start of a Turn or an Effect
|
|
892
|
+
* says the same of a missing `cwd`, of a Worktree path, and of a `cwd` that is a slot's folder. */
|
|
893
|
+
repoSlots?: readonly string[];
|
|
849
894
|
};
|
|
850
895
|
|
|
851
896
|
const MAX_STATES = 200;
|
|
@@ -991,6 +1036,13 @@ export function admitChart(
|
|
|
991
1036
|
return new Door(chart, decl, lent, options).admit(size);
|
|
992
1037
|
}
|
|
993
1038
|
|
|
1039
|
+
/** What an Effect's input takes, for a line of the door. */
|
|
1040
|
+
function shapeText(shape: InputShape): string {
|
|
1041
|
+
const need = [...shape.required, ...(shape.oneOf ? [`exactly one of ${shape.oneOf.join(", ")}`] : [])].join(", ");
|
|
1042
|
+
const may = shape.optional.length ? `${need ? ", and " : ""}optionally ${shape.optional.join(", ")}` : "";
|
|
1043
|
+
return need || may ? `${need}${may}` : "nothing";
|
|
1044
|
+
}
|
|
1045
|
+
|
|
994
1046
|
/** The checks from rule 3 on, over a Chart the grammar admitted. */
|
|
995
1047
|
class Door {
|
|
996
1048
|
private readonly lines: Line[] = [];
|
|
@@ -1024,6 +1076,8 @@ class Door {
|
|
|
1024
1076
|
private readonly invokeKinds = new Map<ChartInvoke, NonNullable<ReturnType<Door["srcKind"]>>>();
|
|
1025
1077
|
/** Every `jr2.sentBy` id, for the party check. */
|
|
1026
1078
|
private readonly sentBys: Array<{ id: string; path: string }> = [];
|
|
1079
|
+
/** The Gates' invoke ids, which the party check finds. */
|
|
1080
|
+
private readonly gateIds = new Set<string>();
|
|
1027
1081
|
|
|
1028
1082
|
private readonly chart: ChartText;
|
|
1029
1083
|
private readonly decl: OpenBodyDeclaration;
|
|
@@ -1086,7 +1140,14 @@ class Door {
|
|
|
1086
1140
|
const events = Object.entries(this.chart.events ?? {}).map(([name, ev]) => this.eventDef(name, ev)!);
|
|
1087
1141
|
const agents: Record<string, AgentDefinition> = {};
|
|
1088
1142
|
for (const [slot, a] of Object.entries(this.chart.agents ?? {})) agents[slot] = { ...a };
|
|
1089
|
-
const admitted: AdmittedChart = {
|
|
1143
|
+
const admitted: AdmittedChart = {
|
|
1144
|
+
text: this.chart,
|
|
1145
|
+
hash: chartHash(this.chart),
|
|
1146
|
+
events,
|
|
1147
|
+
agents,
|
|
1148
|
+
size,
|
|
1149
|
+
gates: [...this.gateIds],
|
|
1150
|
+
};
|
|
1090
1151
|
this.trialCompile(admitted);
|
|
1091
1152
|
return admitted;
|
|
1092
1153
|
}
|
|
@@ -1112,11 +1173,12 @@ class Door {
|
|
|
1112
1173
|
// --- rule 3: names --------------------------------------------------------------------------------
|
|
1113
1174
|
|
|
1114
1175
|
/** The lent actors by kind, as the lent set has them now. */
|
|
1115
|
-
private lentActors(): Array<{ name: string; kind: "Agent" | "Effect" | "Machine" }> {
|
|
1116
|
-
const out: Array<{ name: string; kind: "Agent" | "Effect" | "Machine" }> = [];
|
|
1176
|
+
private lentActors(): Array<{ name: string; kind: "Agent" | "Effect" | "Machine" | "Gate slot" }> {
|
|
1177
|
+
const out: Array<{ name: string; kind: "Agent" | "Effect" | "Machine" | "Gate slot" }> = [];
|
|
1117
1178
|
for (const name of this.lends.agents) if (name in this.lent.actors) out.push({ name, kind: "Agent" });
|
|
1118
1179
|
for (const name of this.lends.effects) if (name in this.lent.actors) out.push({ name, kind: "Effect" });
|
|
1119
1180
|
for (const name of this.lends.machines) if (name in this.lent.actors) out.push({ name, kind: "Machine" });
|
|
1181
|
+
for (const name of this.lends.gates) if (name in this.lent.actors) out.push({ name, kind: "Gate slot" });
|
|
1120
1182
|
return out;
|
|
1121
1183
|
}
|
|
1122
1184
|
|
|
@@ -1130,7 +1192,7 @@ class Door {
|
|
|
1130
1192
|
/** What an invoke's `src` names, or undefined after a refusal line. */
|
|
1131
1193
|
private srcKind(src: string, path: string): InvokeKind | undefined {
|
|
1132
1194
|
const lentNow = (name: string) => name in this.lent.actors;
|
|
1133
|
-
const declared = [...this.lends.agents, ...this.lends.effects, ...this.lends.machines];
|
|
1195
|
+
const declared = [...this.lends.agents, ...this.lends.effects, ...this.lends.machines, ...this.lends.gates];
|
|
1134
1196
|
if (declared.includes(src) && !lentNow(src)) {
|
|
1135
1197
|
this.fail(
|
|
1136
1198
|
3,
|
|
@@ -1141,6 +1203,22 @@ class Door {
|
|
|
1141
1203
|
if (this.lends.agents.includes(src)) return "agent";
|
|
1142
1204
|
if (this.lends.effects.includes(src)) return "effect";
|
|
1143
1205
|
if (this.lends.machines.includes(src)) return "machine";
|
|
1206
|
+
if (this.lends.gates.includes(src)) {
|
|
1207
|
+
// A lent Gate slot (ADR-0011, ADR-0070): its handlers and its look give its `delivers`, which
|
|
1208
|
+
// the Chart's Machine must take. One that is not in the Chart's Vocabulary could never arrive.
|
|
1209
|
+
const slot = this.lent.actors[src];
|
|
1210
|
+
const outside = isGateSlot(slot)
|
|
1211
|
+
? slot.gateSlot.delivers.map((d) => d.name).filter((name) => !this.eventSchemas.has(name))
|
|
1212
|
+
: [];
|
|
1213
|
+
if (outside.length) {
|
|
1214
|
+
this.fail(
|
|
1215
|
+
3,
|
|
1216
|
+
`${path}: the Gate slot "${src}" delivers ${outside.map((n) => `"${n}"`).join(", ")}, which ${outside.length > 1 ? "are" : "is"} not in the Chart's \`events\` (events: ${names([...this.eventSchemas.keys()])}); declare ${outside.length > 1 ? "them" : "it"} in \`events\` and handle ${outside.length > 1 ? "them" : "it"} on this state`,
|
|
1217
|
+
);
|
|
1218
|
+
return undefined;
|
|
1219
|
+
}
|
|
1220
|
+
return "gate";
|
|
1221
|
+
}
|
|
1144
1222
|
if (src === "gate") {
|
|
1145
1223
|
if (this.lends.gate) return "gate";
|
|
1146
1224
|
this.fail(
|
|
@@ -1171,8 +1249,8 @@ class Door {
|
|
|
1171
1249
|
this.sentBys.push({ id: (guard.params as { id: string }).id, path: `${path}.params.id` });
|
|
1172
1250
|
return;
|
|
1173
1251
|
}
|
|
1174
|
-
if (guard.type === FAILURE_GUARD) {
|
|
1175
|
-
if (guard.params !== undefined) this.fail(3, `${path}.params: "${
|
|
1252
|
+
if (guard.type === FAILURE_GUARD || guard.type === READY_GUARD) {
|
|
1253
|
+
if (guard.params !== undefined) this.fail(3, `${path}.params: "${guard.type}" takes no params`);
|
|
1176
1254
|
return;
|
|
1177
1255
|
}
|
|
1178
1256
|
this.checkGuardName(guard.type, `${path}.type`);
|
|
@@ -1180,7 +1258,7 @@ class Door {
|
|
|
1180
1258
|
}
|
|
1181
1259
|
|
|
1182
1260
|
private checkGuardName(name: string, path: string): void {
|
|
1183
|
-
if (name === FAILURE_GUARD) return;
|
|
1261
|
+
if (name === FAILURE_GUARD || name === READY_GUARD) return;
|
|
1184
1262
|
const lent = this.lends.guards.filter((g) => g in this.lent.guards);
|
|
1185
1263
|
if (this.lends.guards.includes(name) && !(name in this.lent.guards)) {
|
|
1186
1264
|
this.fail(
|
|
@@ -1190,7 +1268,7 @@ class Door {
|
|
|
1190
1268
|
} else if (!this.lends.guards.includes(name)) {
|
|
1191
1269
|
this.fail(
|
|
1192
1270
|
3,
|
|
1193
|
-
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id }, and ${
|
|
1271
|
+
`${path}: "${name}" is not a lent guard; lent guards: ${names(lent)} (and ${SENT_BY} with params { id }, ${FAILURE_GUARD} and ${READY_GUARD})`,
|
|
1194
1272
|
);
|
|
1195
1273
|
}
|
|
1196
1274
|
}
|
|
@@ -1335,7 +1413,14 @@ class Door {
|
|
|
1335
1413
|
// --- rule 5: minted Agents ------------------------------------------------------------------------
|
|
1336
1414
|
|
|
1337
1415
|
private checkAgents(): void {
|
|
1338
|
-
const lentNames = new Set([
|
|
1416
|
+
const lentNames = new Set([
|
|
1417
|
+
...this.lends.agents,
|
|
1418
|
+
...this.lends.effects,
|
|
1419
|
+
...this.lends.machines,
|
|
1420
|
+
...this.lends.gates,
|
|
1421
|
+
"gate",
|
|
1422
|
+
"fail",
|
|
1423
|
+
]);
|
|
1339
1424
|
for (const [slot, a] of Object.entries(this.chart.agents ?? {})) {
|
|
1340
1425
|
const path = `agents.${slot}`;
|
|
1341
1426
|
if (lentNames.has(slot)) {
|
|
@@ -1679,7 +1764,8 @@ class Door {
|
|
|
1679
1764
|
/** The parties' rules, as `jr2Setup` applies them (ADR-0015), each with its path in the Chart. */
|
|
1680
1765
|
private checkParties(): void {
|
|
1681
1766
|
const agents = new Set<string>();
|
|
1682
|
-
const gates =
|
|
1767
|
+
const gates = this.gateIds;
|
|
1768
|
+
const slotStates = new Map<string, Node[]>();
|
|
1683
1769
|
for (const node of this.nodes) {
|
|
1684
1770
|
const invokes = listAt(node.state.invoke, `${node.doc}.invoke`);
|
|
1685
1771
|
// A Gate's invoke id, as jr2Setup names an unnamed one: the state's key path.
|
|
@@ -1694,6 +1780,7 @@ class Door {
|
|
|
1694
1780
|
if (kind === "gate") gates.add(inv.id ?? (unnamed > 1 ? `${key}.${ordinal++}` : key));
|
|
1695
1781
|
if (kind !== "agent" && kind !== "minted") continue;
|
|
1696
1782
|
agents.add(inv.src);
|
|
1783
|
+
slotStates.set(inv.src, [...(slotStates.get(inv.src) ?? []), node]);
|
|
1697
1784
|
if (inv.onDone !== undefined) {
|
|
1698
1785
|
this.fail(
|
|
1699
1786
|
6,
|
|
@@ -1715,6 +1802,25 @@ class Door {
|
|
|
1715
1802
|
}
|
|
1716
1803
|
}
|
|
1717
1804
|
}
|
|
1805
|
+
// One Agent, two Turns at once: one sender and one conversation cannot hold both.
|
|
1806
|
+
// One line for each Agent: every state of it that can be active with another of them.
|
|
1807
|
+
for (const [slot, states] of slotStates) {
|
|
1808
|
+
const clash = new Set<Node>();
|
|
1809
|
+
for (let i = 0; i < states.length; i++) {
|
|
1810
|
+
for (let j = i + 1; j < states.length; j++) {
|
|
1811
|
+
const [a, b] = [states[i]!, states[j]!];
|
|
1812
|
+
const together =
|
|
1813
|
+
a === b || isAncestorOrSelf(a, b) || isAncestorOrSelf(b, a) || nearestCommon(a, b)?.kind === "parallel";
|
|
1814
|
+
if (together) clash.add(a).add(b);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
if (clash.size === 0) continue;
|
|
1818
|
+
const docs = [...clash].map((n) => n.doc).join(", ");
|
|
1819
|
+
this.fail(
|
|
1820
|
+
6,
|
|
1821
|
+
`${docs}: Agent "${slot}" would run two Turns at once: these states invoke it and can be active together (in parallel regions, one inside the other, or one state twice); an Agent runs one Turn at a time: invoke another Agent in one of them`,
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1718
1824
|
for (const { id, path } of this.sentBys) {
|
|
1719
1825
|
if (id.includes("{{")) {
|
|
1720
1826
|
this.fail(3, `${path}: a sentBy id is a literal party name, never a reference`);
|
|
@@ -1933,8 +2039,9 @@ class Door {
|
|
|
1933
2039
|
}
|
|
1934
2040
|
if (kind === "effect") {
|
|
1935
2041
|
const logic = this.lent.actors[inv.src];
|
|
1936
|
-
const
|
|
1937
|
-
|
|
2042
|
+
const parts = isEffect(logic) ? logic.effect : undefined;
|
|
2043
|
+
const keys = parts ? inputKeysOf(parts) : undefined;
|
|
2044
|
+
if (keys === undefined && parts?.kind === "dispatch") {
|
|
1938
2045
|
// A dispatch with no input schema has no keys to hold the Chart to: its input is the run's
|
|
1939
2046
|
// body, and the callee's door is the check (ADR-0071). Its values are what any Effect's
|
|
1940
2047
|
// input slot takes.
|
|
@@ -1957,31 +2064,114 @@ class Door {
|
|
|
1957
2064
|
`${ipath}: the lent Effect "${inv.src}" has no input keys the door can read (an Open argv nobody bound)`,
|
|
1958
2065
|
);
|
|
1959
2066
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2067
|
+
const shape = this.shapeHere(parts!);
|
|
2068
|
+
// A cut's `expect` is static, and a Chart cannot state it: an unstated one would let a planner's
|
|
2069
|
+
// name land on someone else's branch.
|
|
2070
|
+
if (parts?.kind === "command" && parts.cut && parts.cut.expect === undefined) {
|
|
2071
|
+
const fresh = this.lentNewCuts();
|
|
2072
|
+
this.fail(
|
|
2073
|
+
8,
|
|
2074
|
+
`${ipath}: the lent cut "${inv.src}" does not state \`expect\`, so a Chart cannot use it; ` +
|
|
2075
|
+
(fresh.length
|
|
2076
|
+
? `use a lent cut with \`expect: "new"\`: ${names(fresh)}`
|
|
2077
|
+
: `the Open body must state it (\`worktree({ expect: "new" })\` would); ${this.lentText()}`),
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
// A push publishes the branch of a cut's row, which stays in the run's ledger, so a Chart that
|
|
2081
|
+
// cuts and a later Chart that pushes are the same pair: the test is what is lent, not what this
|
|
2082
|
+
// Chart invokes.
|
|
2083
|
+
const force = this.lentForcePush();
|
|
2084
|
+
if (parts?.kind === "command" && parts.cut?.expect === "existing" && force !== undefined) {
|
|
2085
|
+
const fresh = this.lentNewCuts();
|
|
2086
|
+
this.fail(
|
|
2087
|
+
8,
|
|
2088
|
+
`${ipath}: the lent cut "${inv.src}" states \`expect: "existing"\` beside the lent force push "${force}", and together they can force-push any existing branch; ` +
|
|
2089
|
+
(fresh.length
|
|
2090
|
+
? `use a lent cut with \`expect: "new"\`: ${names(fresh)}`
|
|
2091
|
+
: `this Open body lends no cut with \`expect: "new"\`, so a Chart cannot cut here (\`worktree({ expect: "new" })\` would); ${this.lentText()}`),
|
|
2092
|
+
);
|
|
1963
2093
|
}
|
|
2094
|
+
if (input === undefined && shape.required.length === 0 && !shape.oneOf) return;
|
|
2095
|
+
const takes = shapeText(shape);
|
|
2096
|
+
if (!isObject) return this.fail(8, `${ipath}: the lent Effect "${inv.src}" takes an object: ${takes}`);
|
|
1964
2097
|
const given = Object.keys(input);
|
|
1965
|
-
const missing =
|
|
2098
|
+
const missing = shape.required.filter((k) => !given.includes(k));
|
|
1966
2099
|
const extra = given.filter((k) => !keys.includes(k));
|
|
1967
|
-
|
|
2100
|
+
const ofOne = shape.oneOf?.filter((k) => given.includes(k)).length ?? 1;
|
|
2101
|
+
if (missing.length || extra.length || ofOne !== 1) {
|
|
1968
2102
|
this.fail(
|
|
1969
2103
|
8,
|
|
1970
|
-
`${ipath}: the lent Effect "${inv.src}" takes
|
|
2104
|
+
`${ipath}: the lent Effect "${inv.src}" takes ${takes}` +
|
|
1971
2105
|
(missing.length ? `; missing: ${missing.join(", ")}` : "") +
|
|
1972
|
-
(extra.length ? `; not its input: ${extra.join(", ")}` : "")
|
|
2106
|
+
(extra.length ? `; not its input: ${extra.join(", ")}` : "") +
|
|
2107
|
+
(ofOne !== 1 ? `; give one of ${shape.oneOf!.join(", ")}` : ""),
|
|
1973
2108
|
);
|
|
1974
2109
|
}
|
|
2110
|
+
if (parts?.kind === "command" && parts.cut && given.includes("detach") && given.includes("from")) {
|
|
2111
|
+
this.fail(8, `${ipath}: a detached cut takes no \`from\``);
|
|
2112
|
+
}
|
|
1975
2113
|
for (const k of given.filter((g) => keys.includes(g))) {
|
|
1976
2114
|
const v = (input as Record<string, unknown>)[k];
|
|
1977
2115
|
if (typeof v !== "string" && !isRef(v))
|
|
1978
2116
|
this.fail(8, `${ipath}.${k}: an Effect's input slot takes a string or a reference`);
|
|
2117
|
+
else if ((k === "worktree" || k === "path") && typeof v === "string") this.checkLiteralPath(v, `${ipath}.${k}`);
|
|
1979
2118
|
}
|
|
1980
2119
|
return;
|
|
1981
2120
|
}
|
|
1982
2121
|
this.checkAgentInput(inv.src, kind, input, isObject, ipath);
|
|
1983
2122
|
}
|
|
1984
2123
|
|
|
2124
|
+
/** An Effect's shape here: where the door knows the Workspace has Repo Slots, `worktree` is
|
|
2125
|
+
* required on what can name one. Elsewhere the start says so (`drive`). */
|
|
2126
|
+
private shapeHere(parts: Parameters<typeof inputShapeOf>[0]): InputShape {
|
|
2127
|
+
const shape = inputShapeOf(parts)!;
|
|
2128
|
+
if (!this.options.repoSlots?.length || !shape.optional.includes("worktree")) return shape;
|
|
2129
|
+
return {
|
|
2130
|
+
...shape,
|
|
2131
|
+
required: [...shape.required, "worktree"],
|
|
2132
|
+
optional: shape.optional.filter((k) => k !== "worktree"),
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
/** The lent Effects, each with its parts. */
|
|
2137
|
+
private lentEffects(): Array<{ name: string; parts: Parameters<typeof inputShapeOf>[0] }> {
|
|
2138
|
+
return this.lends.effects.flatMap((name) => {
|
|
2139
|
+
const logic = this.lent.actors[name];
|
|
2140
|
+
return isEffect(logic) ? [{ name, parts: logic.effect }] : [];
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
/** The name of a lent push that forces, if one is lent. */
|
|
2145
|
+
private lentForcePush(): string | undefined {
|
|
2146
|
+
return this.lentEffects().find(({ parts }) => parts.kind === "push" && parts.force)?.name;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
/** The names of the lent cuts that make only a new branch. */
|
|
2150
|
+
private lentNewCuts(): string[] {
|
|
2151
|
+
return this.lentEffects()
|
|
2152
|
+
.filter(({ parts }) => parts.kind === "command" && parts.cut?.expect === "new")
|
|
2153
|
+
.map(({ name }) => name);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
/** A literal Worktree path: a wrong slot costs no pod. A path with a reference waits for its start. */
|
|
2157
|
+
private checkLiteralPath(value: string, path: string): void {
|
|
2158
|
+
if (referencesOf(value).length) return;
|
|
2159
|
+
const match = value.match(/^\/work\/([^/]+)\/([^/.-][^/]*)$/);
|
|
2160
|
+
const slots = this.options.repoSlots;
|
|
2161
|
+
if (match === null) this.fail(8, `${path}: "${value}" is not a Worktree path: /work/<slot>/<name>`);
|
|
2162
|
+
else if (slots !== undefined && !slots.includes(match[1]!)) {
|
|
2163
|
+
this.fail(8, `${path}: "${value}" names no Repo Slot of this Workspace; its Repo Slots: ${names([...slots])}`);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
/** A literal `cwd`, see {@link slotFolderFault}. A `cwd` with a reference waits for its value: the
|
|
2168
|
+
* Open body asks the same question when the Turn starts. */
|
|
2169
|
+
private checkCwd(value: string, path: string): void {
|
|
2170
|
+
if (referencesOf(value).length) return;
|
|
2171
|
+
const fault = slotFolderFault(value, this.options.repoSlots ?? []);
|
|
2172
|
+
if (fault !== undefined) this.fail(8, `${path}: ${fault}`);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
1985
2175
|
/** The input of `fail` (ADR-0073): `{ reason, detail? }`, `reason` a text for a person (a string
|
|
1986
2176
|
* or a reference), `detail` any value. */
|
|
1987
2177
|
private checkFailInput(input: unknown, isObject: boolean, ipath: string): void {
|
|
@@ -2000,6 +2190,15 @@ class Door {
|
|
|
2000
2190
|
}
|
|
2001
2191
|
}
|
|
2002
2192
|
|
|
2193
|
+
/** Does this Agent read `/work`? A `workspace: "none"` one frames nothing. */
|
|
2194
|
+
private worksInWorkspace(src: string, kind: "agent" | "minted"): boolean {
|
|
2195
|
+
if (kind === "agent") {
|
|
2196
|
+
const logic = this.lent.actors[src] as { definition?: { workspace?: unknown } } | undefined;
|
|
2197
|
+
return logic?.definition?.workspace !== "none";
|
|
2198
|
+
}
|
|
2199
|
+
return (this.chart.agents?.[src] as { workspace?: unknown } | undefined)?.workspace !== "none";
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2003
2202
|
/**
|
|
2004
2203
|
* An Agent invoke's input is a Turn's Frame and Dials (ADR-0057): `prompt` (a string), and
|
|
2005
2204
|
* optionally `cwd`, `continue: true`, a `model` the Open body allows, and a `thinkingLevel`.
|
|
@@ -2033,6 +2232,13 @@ class Door {
|
|
|
2033
2232
|
else if (typeof obj.prompt !== "string")
|
|
2034
2233
|
this.fail(8, `${ipath}.prompt: a prompt is a string (placeholders inside it are fine)`);
|
|
2035
2234
|
if (obj.cwd !== undefined && typeof obj.cwd !== "string") this.fail(8, `${ipath}.cwd: a cwd is a string`);
|
|
2235
|
+
if (obj.cwd === undefined && this.options.repoSlots?.length && this.worksInWorkspace(src, kind)) {
|
|
2236
|
+
this.fail(
|
|
2237
|
+
8,
|
|
2238
|
+
`${ipath}: ${who} works under a Workspace with Repo Slots, and a Turn there says where: \`cwd\` is a Worktree, or "/work"`,
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
if (typeof obj.cwd === "string" && this.worksInWorkspace(src, kind)) this.checkCwd(obj.cwd, `${ipath}.cwd`);
|
|
2036
2242
|
if (obj.continue !== undefined && obj.continue !== true)
|
|
2037
2243
|
this.fail(8, `${ipath}.continue: \`continue\` is true or absent`);
|
|
2038
2244
|
if (obj.model !== undefined) {
|
|
@@ -2164,6 +2370,12 @@ function wholeRefOf(v: unknown): string | undefined {
|
|
|
2164
2370
|
return isRef(v) ? (v as { $ref: string }).$ref : undefined;
|
|
2165
2371
|
}
|
|
2166
2372
|
|
|
2373
|
+
/** The nearest state at or above both `a` and `b`. */
|
|
2374
|
+
function nearestCommon(a: Node, b: Node): Node | undefined {
|
|
2375
|
+
for (let n: Node | undefined = a; n; n = n.parent) if (isAncestorOrSelf(n, b)) return n;
|
|
2376
|
+
return undefined;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2167
2379
|
/** Is `a` the state `b` or a state above it? */
|
|
2168
2380
|
function isAncestorOrSelf(a: Node, b: Node): boolean {
|
|
2169
2381
|
for (let n: Node | undefined = b; n; n = n.parent) if (n === a) return true;
|
package/src/charts.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
isOpenBody,
|
|
26
26
|
lentIn,
|
|
27
27
|
openBodyDeclarationOf,
|
|
28
|
+
repoSlotsAround,
|
|
28
29
|
type LentParts,
|
|
29
30
|
type OpenBodyDeclaration,
|
|
30
31
|
type OpenBodyLogic,
|
|
@@ -305,6 +306,8 @@ export type ChartWrapper = {
|
|
|
305
306
|
chartFields: string[];
|
|
306
307
|
/** The door's other fields: the Chart's `input` keys the door can see (ADR-0070 rule 7). */
|
|
307
308
|
inputKeys: string[];
|
|
309
|
+
/** The Repo Slots of the `workspace()` around the wrapper, where the door can read them. */
|
|
310
|
+
repoSlots?: readonly string[];
|
|
308
311
|
};
|
|
309
312
|
|
|
310
313
|
/**
|
|
@@ -333,7 +336,8 @@ export function chartWrapperOf(machine: AnyStateMachine): ChartWrapper | undefin
|
|
|
333
336
|
const chartFields = Object.keys(shape).filter((k) => isChartSchema(shape[k]));
|
|
334
337
|
// The door's other fields, less the body's placement: a Chart reads no url (open-body.ts).
|
|
335
338
|
const inputKeys = Object.keys(chartInputOf(shape)).filter((k) => !chartFields.includes(k));
|
|
336
|
-
|
|
339
|
+
const repoSlots = repoSlotsAround(machine);
|
|
340
|
+
return { body: bodies[0]!.slot, bodies, chartFields, inputKeys, ...(repoSlots ? { repoSlots } : {}) };
|
|
337
341
|
}
|
|
338
342
|
|
|
339
343
|
/**
|
|
@@ -347,7 +351,10 @@ export function admitUnder(wrapper: ChartWrapper, text: unknown): AdmittedChart
|
|
|
347
351
|
const report: string[] = [];
|
|
348
352
|
for (const body of wrapper.bodies) {
|
|
349
353
|
try {
|
|
350
|
-
return admitBodyChart(text, body.declaration, body.lent, {
|
|
354
|
+
return admitBodyChart(text, body.declaration, body.lent, {
|
|
355
|
+
inputKeys: wrapper.inputKeys,
|
|
356
|
+
...(wrapper.repoSlots ? { repoSlots: wrapper.repoSlots } : {}),
|
|
357
|
+
});
|
|
351
358
|
} catch (err) {
|
|
352
359
|
if (!(err instanceof ChartRefusal)) throw err;
|
|
353
360
|
const prefix = wrapper.bodies.length > 1 ? `body "${body.slot}": ` : "";
|