@jr2/orchestrator 0.6.0 → 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 +86 -9
- package/docs/concepts/architecture.md +41 -28
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +7 -5
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +4 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +50 -22
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +5 -4
- package/docs/concepts/pools-and-sources.md +187 -11
- package/docs/concepts/secrets-and-trust.md +73 -33
- package/docs/concepts/the-menu.md +11 -0
- package/docs/concepts/workspaces.md +6 -5
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +43 -34
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- package/docs/guides/add-tools-to-the-sandbox.md +55 -11
- 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 +24 -8
- 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/fix-a-failed-converge.md +18 -6
- 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 +9 -6
- package/docs/guides/look-inside-a-sandbox.md +101 -31
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-one-worker-per-item.md +15 -9
- package/docs/guides/set-an-agents-access-and-dials.md +2 -0
- 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 +1 -1
- package/docs/guides/watch-a-run.md +127 -121
- 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 +17 -7
- package/docs/llms.txt +81 -15
- package/docs/reference/authoring-surface/agent.md +1 -1
- package/docs/reference/authoring-surface/answer.md +1 -1
- package/docs/reference/authoring-surface/arg.md +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs/reference/authoring-surface/file.md +1 -1
- 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 +1 -1
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +1 -1
- package/docs/reference/authoring-surface/jr2setup.md +13 -3
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +5 -3
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +1 -1
- package/docs/reference/authoring-surface/pool.md +77 -9
- package/docs/reference/authoring-surface/push.md +1 -1
- package/docs/reference/authoring-surface/readyof.md +1 -1
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +1 -1
- 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 +56 -1
- 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 +14 -1
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- package/docs/reference/authoring-surface/type-anyframepath.md +1 -1
- package/docs/reference/authoring-surface/type-anyworktreepath.md +1 -1
- package/docs/reference/authoring-surface/type-binding.md +1 -1
- 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-commandoutput.md +1 -1
- package/docs/reference/authoring-surface/type-customize.md +16 -1
- 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 +1 -1
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- package/docs/reference/authoring-surface/type-framepath.md +1 -1
- 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 +1 -1
- package/docs/reference/authoring-surface/type-httpoutput.md +1 -1
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +1 -1
- 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-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- package/docs/reference/authoring-surface/type-poolnextinput.md +1 -1
- package/docs/reference/authoring-surface/type-poolspec.md +11 -6
- package/docs/reference/authoring-surface/type-pushoutput.md +1 -1
- 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 +1 -1
- 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-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-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 +16 -7
- 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 +1 -1
- package/docs/reference/authoring-surface/type-worktreefound.md +3 -3
- package/docs/reference/authoring-surface/type-worktreeinput.md +1 -1
- package/docs/reference/authoring-surface/type-worktreeoptions.md +1 -1
- package/docs/reference/authoring-surface/type-worktreeoutput.md +1 -1
- package/docs/reference/authoring-surface/type-worktreepath.md +1 -1
- package/docs/reference/authoring-surface/type-worktreeready.md +1 -1
- package/docs/reference/authoring-surface/type-worktreerefused.md +4 -4
- package/docs/reference/authoring-surface/workspace.md +1 -1
- package/docs/reference/authoring-surface/worktree.md +1 -1
- package/docs/reference/authoring-surface.md +88 -167
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +235 -2
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +71 -11
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +14 -10
- package/docs/reference/decisions/0006-agent-control-surface.md +4 -4
- 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 +9 -2
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +20 -14
- 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 +18 -14
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -12
- 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 +32 -18
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +14 -1
- 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 +116 -34
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +38 -37
- 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 +7 -6
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +23 -7
- 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 -18
- 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 +4 -3
- 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 +5 -4
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +19 -19
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- 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 +7 -0
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +13 -7
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +38 -30
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +64 -17
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +2 -1
- 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 +51 -23
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +94 -54
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +101 -47
- 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 +17 -13
- 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/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +4 -3
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +37 -10
- 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 +6 -4
- package/docs/reference/glossary.md +96 -42
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +976 -149
- package/docs/why.md +2 -1
- package/package.json +10 -3
- package/src/actor.ts +36 -10
- package/src/agent.ts +50 -0
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +52 -7
- package/src/config.ts +550 -1
- package/src/conversations.ts +453 -0
- package/src/custodian.ts +8 -2
- package/src/customize.ts +143 -4
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +2 -1
- package/src/effect.ts +60 -7
- package/src/effector-wire.ts +272 -27
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +10 -4
- package/src/held-secrets.ts +72 -23
- package/src/holds.ts +2 -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 +76 -38
- package/src/http.ts +889 -94
- package/src/index.ts +16 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +22 -3
- package/src/machine-doc.ts +26 -1
- package/src/mcp-effect.ts +424 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +64 -2
- package/src/notices.ts +138 -10
- package/src/open-body.ts +25 -11
- package/src/open-effect.ts +17 -4
- package/src/open.ts +4 -2
- package/src/parts.ts +87 -9
- package/src/party.ts +2 -1
- package/src/pick.ts +2 -2
- package/src/pool.ts +1142 -163
- package/src/registration.ts +203 -13
- package/src/run-host.ts +1509 -171
- package/src/run-list.ts +191 -0
- package/src/sandbox-kube.ts +53 -5
- package/src/secret-placement.ts +239 -33
- package/src/server.ts +53 -4
- package/src/setup.ts +72 -32
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +263 -22
- package/src/wire.ts +17 -0
- package/src/workspace.ts +50 -6
- package/src/worktree.ts +3 -3
- 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/run-list.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// The run listing (ADR-0009): what `GET /runs` reads of its query, the summary of one run, and the
|
|
2
|
+
// cursor of a page. The live runs come from the host's registry and the ended runs from the store's
|
|
3
|
+
// listing columns (`SnapshotStore.ended`); `RunHost.history` joins the two halves in one order,
|
|
4
|
+
// newest start first, so a page is the same whichever half its runs are in.
|
|
5
|
+
|
|
6
|
+
import type { RunStarter } from "./callers.ts";
|
|
7
|
+
import { periodMs } from "./duration.ts";
|
|
8
|
+
import type { DispatchedBy } from "./registration.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* One run as `GET /runs` lists it (ADR-0009): a summary, not the whole `RunStatus`. `status` is
|
|
12
|
+
* `active` for a live run, else the store's word for how it ended. `reason` says why, where the
|
|
13
|
+
* Machine did not: the host's reason (`drifted`, `lost`, ADR-0030), the Failure's reason
|
|
14
|
+
* (`failed`, ADR-0073) or the fault (`error`). `endedAt` is the terminal write of an ended run.
|
|
15
|
+
* `openGates` counts the Gates open now, so it is 0 for an ended run. `placing` names the child
|
|
16
|
+
* paths of the Workspaces that wait for capacity (ADR-0064), and `dispatchedBy` the run that
|
|
17
|
+
* started this one (ADR-0071).
|
|
18
|
+
*/
|
|
19
|
+
export type RunSummary = {
|
|
20
|
+
runId: string;
|
|
21
|
+
workflow: string;
|
|
22
|
+
status: ShownStatus;
|
|
23
|
+
reason?: string;
|
|
24
|
+
startedBy?: RunStarter;
|
|
25
|
+
startedAt: string;
|
|
26
|
+
endedAt?: string;
|
|
27
|
+
assembly?: string;
|
|
28
|
+
openGates: number;
|
|
29
|
+
placing?: string[];
|
|
30
|
+
dispatchedBy?: DispatchedBy;
|
|
31
|
+
/** The space guard deleted the run's kept conversations (ADR-0065); the row stays. */
|
|
32
|
+
swept?: "conversation swept: no space";
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** A page of the listing: the runs, and the cursor of the next page when there is one. */
|
|
36
|
+
export type RunPage = { runs: RunSummary[]; next?: string };
|
|
37
|
+
|
|
38
|
+
/** How a run ended, as the store writes it (ADR-0065, ADR-0025, ADR-0030, ADR-0073, ADR-0074). */
|
|
39
|
+
export type EndedStatus = "done" | "failed" | "error" | "cancelled" | "lost" | "drifted";
|
|
40
|
+
|
|
41
|
+
/** The statuses of an ended run, as the store writes them. xstate's `stopped` is not one: it is
|
|
42
|
+
* mechanism, and a cancel ends the run `cancelled` (ADR-0025). */
|
|
43
|
+
export const ENDED_STATUSES: readonly EndedStatus[] = ["done", "failed", "error", "cancelled", "lost", "drifted"];
|
|
44
|
+
|
|
45
|
+
/** The status of a live run: the Machine's own word. */
|
|
46
|
+
export const LIVE_STATUS = "active";
|
|
47
|
+
|
|
48
|
+
/** A run status as jr2 shows it, on every route and feed: {@link LIVE_STATUS} while the run is
|
|
49
|
+
* live, else how it ended. The store's own `live` and xstate's `stopped` are never shown. */
|
|
50
|
+
export type ShownStatus = typeof LIVE_STATUS | EndedStatus;
|
|
51
|
+
|
|
52
|
+
/** A word that the store wrote for an ended run. */
|
|
53
|
+
export function isEndedStatus(word: string): word is EndedStatus {
|
|
54
|
+
return (ENDED_STATUSES as readonly string[]).includes(word);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The shown status of a store row: `live` shows as {@link LIVE_STATUS}, and an ended word as
|
|
59
|
+
* itself. jr2 writes no other word. A row with another word is a defect in the store, so it shows
|
|
60
|
+
* as `error`: it is not live, and nothing can say how it ended.
|
|
61
|
+
*/
|
|
62
|
+
export function shownOfRow(word: string): ShownStatus {
|
|
63
|
+
if (word === "live") return LIVE_STATUS;
|
|
64
|
+
return isEndedStatus(word) ? word : "error";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** How many runs a page holds when the query names no `limit`, and the most it can name. */
|
|
68
|
+
export const RUNS_PAGE_DEFAULT = 50;
|
|
69
|
+
export const RUNS_PAGE_MAX = 500;
|
|
70
|
+
|
|
71
|
+
/** The query of `GET /runs`, read. `ended` is the ended statuses to list, or `undefined` for none. */
|
|
72
|
+
export type RunListQuery = {
|
|
73
|
+
live: boolean;
|
|
74
|
+
ended?: readonly EndedStatus[];
|
|
75
|
+
workflow?: string;
|
|
76
|
+
since?: string;
|
|
77
|
+
after?: { startedAt: string; runId: string };
|
|
78
|
+
limit: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** An ISO-8601 date, or a date and time with an optional zone. */
|
|
82
|
+
const ISO_TIME = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Read the query of `GET /runs`, or the refusal's text. `status` is `live` (the default), `ended`,
|
|
86
|
+
* `all`, or a comma list of statuses and the two words; `since` is a duration back from `now`
|
|
87
|
+
* (`"2h"`, `"7d"`) or an ISO-8601 time; `cursor` is a page's `next`; `limit` is 1 to
|
|
88
|
+
* {@link RUNS_PAGE_MAX}.
|
|
89
|
+
*/
|
|
90
|
+
export function parseRunListQuery(
|
|
91
|
+
q: { status?: string; workflow?: string; since?: string; cursor?: string; limit?: string },
|
|
92
|
+
now: number,
|
|
93
|
+
): RunListQuery | { error: string } {
|
|
94
|
+
let live = false;
|
|
95
|
+
const ended = new Set<EndedStatus>();
|
|
96
|
+
for (const word of (q.status ?? "live").split(",").map((w) => w.trim())) {
|
|
97
|
+
if (word === "live" || word === LIVE_STATUS) live = true;
|
|
98
|
+
else if (word === "ended") for (const s of ENDED_STATUSES) ended.add(s);
|
|
99
|
+
else if (word === "all") {
|
|
100
|
+
live = true;
|
|
101
|
+
for (const s of ENDED_STATUSES) ended.add(s);
|
|
102
|
+
} else if (isEndedStatus(word)) ended.add(word);
|
|
103
|
+
else {
|
|
104
|
+
return {
|
|
105
|
+
error: `status "${word}" is not one jr2 lists — use live, ended, all, or a comma list of ${[LIVE_STATUS, ...ENDED_STATUSES].join(", ")}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
let since: string | undefined;
|
|
110
|
+
if (q.since !== undefined) {
|
|
111
|
+
const back = periodMs(q.since);
|
|
112
|
+
// Only an ISO-8601 shape reaches `Date.parse`: it reads other text leniently, so `7` would be
|
|
113
|
+
// a time in 2001 and list the whole history.
|
|
114
|
+
const at = back !== undefined ? now - back : ISO_TIME.test(q.since) ? Date.parse(q.since) : Number.NaN;
|
|
115
|
+
if (!Number.isFinite(at)) {
|
|
116
|
+
return { error: `since "${q.since}" is not a duration (such as 2h or 7d) or an ISO-8601 time` };
|
|
117
|
+
}
|
|
118
|
+
// A duration longer than the clock goes back lists every run, as the sweep's `Math.max(0, …)`:
|
|
119
|
+
// a time before 1970 is outside what `Date` can print at its far end.
|
|
120
|
+
since = new Date(Math.max(at, 0)).toISOString();
|
|
121
|
+
}
|
|
122
|
+
let after: RunListQuery["after"];
|
|
123
|
+
if (q.cursor !== undefined) {
|
|
124
|
+
after = decodeCursor(q.cursor);
|
|
125
|
+
if (!after) return { error: "cursor is not one that GET /runs gave — use the next of the page before" };
|
|
126
|
+
}
|
|
127
|
+
let limit = RUNS_PAGE_DEFAULT;
|
|
128
|
+
if (q.limit !== undefined) {
|
|
129
|
+
limit = Number(q.limit);
|
|
130
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > RUNS_PAGE_MAX) {
|
|
131
|
+
return { error: `limit is a whole number from 1 to ${RUNS_PAGE_MAX}` };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
live,
|
|
136
|
+
...(ended.size ? { ended: [...ended] } : {}),
|
|
137
|
+
...(q.workflow !== undefined && q.workflow !== "" ? { workflow: q.workflow } : {}),
|
|
138
|
+
...(since !== undefined ? { since } : {}),
|
|
139
|
+
...(after ? { after } : {}),
|
|
140
|
+
limit,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The cursor after `run`: its start and its id, which order the listing. Opaque to a reader. */
|
|
145
|
+
export function encodeCursor(run: { startedAt: string; runId: string }): string {
|
|
146
|
+
return Buffer.from(JSON.stringify([run.startedAt, run.runId])).toString("base64url");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** A cursor read back, or undefined when it is not one that {@link encodeCursor} made. */
|
|
150
|
+
export function decodeCursor(cursor: string): { startedAt: string; runId: string } | undefined {
|
|
151
|
+
try {
|
|
152
|
+
const value = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")) as unknown;
|
|
153
|
+
if (!Array.isArray(value) || value.length !== 2) return undefined;
|
|
154
|
+
const [startedAt, runId] = value as unknown[];
|
|
155
|
+
if (typeof startedAt !== "string" || typeof runId !== "string") return undefined;
|
|
156
|
+
if (!Number.isFinite(Date.parse(startedAt))) return undefined;
|
|
157
|
+
return { startedAt, runId };
|
|
158
|
+
} catch {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The listing's order: the newest start first, then the larger run id. */
|
|
164
|
+
export function newestFirst(a: RunSummary, b: RunSummary): number {
|
|
165
|
+
if (a.startedAt !== b.startedAt) return a.startedAt < b.startedAt ? 1 : -1;
|
|
166
|
+
return a.runId < b.runId ? 1 : a.runId > b.runId ? -1 : 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Whether `run` comes after the cursor `after` in the order. */
|
|
170
|
+
export function isAfter(
|
|
171
|
+
run: { startedAt: string; runId: string },
|
|
172
|
+
after: { startedAt: string; runId: string },
|
|
173
|
+
): boolean {
|
|
174
|
+
return run.startedAt < after.startedAt || (run.startedAt === after.startedAt && run.runId < after.runId);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The child paths of every Workspace in `placing` (ADR-0064), at any depth, off the state values
|
|
178
|
+
* alone. The root in `placing` is the empty path. */
|
|
179
|
+
export function placingOf(value: unknown, children: readonly PlacingChild[]): string[] {
|
|
180
|
+
return [...(value === "placing" ? [""] : []), ...placingIn(children, [])];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The part of a `RunChild` that {@link placingOf} reads. */
|
|
184
|
+
export type PlacingChild = { id: string; value: unknown; children: readonly PlacingChild[] };
|
|
185
|
+
|
|
186
|
+
function placingIn(children: readonly PlacingChild[], path: string[]): string[] {
|
|
187
|
+
return children.flatMap((child) => {
|
|
188
|
+
const here = [...path, child.id];
|
|
189
|
+
return [...(child.value === "placing" ? [here.join("/")] : []), ...placingIn(child.children, here)];
|
|
190
|
+
});
|
|
191
|
+
}
|
package/src/sandbox-kube.ts
CHANGED
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
// volume runtime the kit's Harness image, as an `image` volume → /opt/jr2
|
|
46
46
|
// volume conversations an emptyDir → /run/jr2/conversations, the Harness container's alone
|
|
47
47
|
// (ADR-0031: a Harness restart inside the pod keeps its conversations)
|
|
48
|
+
// volume shell an emptyDir → /run/jr2/shell, the Harness container's alone: the
|
|
49
|
+
// socket of a person's shell (`jr2 workspace shell`, ADR-0076)
|
|
48
50
|
// initContainer preflight the USER'S image + that volume → ADR-0037's probe, the thing that
|
|
49
51
|
// proves a registry ref, whose first appearance is this provision
|
|
50
52
|
// container harness the Sandbox Image, command overridden, /work + /opt/jr2 mounted
|
|
@@ -105,6 +107,8 @@ import {
|
|
|
105
107
|
CONVERSATIONS_DIR,
|
|
106
108
|
CONVERSATIONS_DIR_ENV,
|
|
107
109
|
CONVERSATIONS_VOLUME,
|
|
110
|
+
SHELL_DIR,
|
|
111
|
+
SHELL_VOLUME,
|
|
108
112
|
HELD_MOUNT,
|
|
109
113
|
CA_CONFIGMAP,
|
|
110
114
|
heldManifestKey,
|
|
@@ -115,6 +119,15 @@ import {
|
|
|
115
119
|
PRIORITY_CLASS_SANDBOX,
|
|
116
120
|
REPOS_MOUNT,
|
|
117
121
|
sandboxCustodianConfigMap,
|
|
122
|
+
ANNOTATION_HOLD,
|
|
123
|
+
ANNOTATION_KEEPALIVE,
|
|
124
|
+
ANNOTATION_WORKSPACE_PATH,
|
|
125
|
+
HOLD_HELD,
|
|
126
|
+
HOLD_RELEASED,
|
|
127
|
+
LABEL_SANDBOX_RUN,
|
|
128
|
+
LABEL_SANDBOX_WORKFLOW,
|
|
129
|
+
LABEL_SANDBOX_WORKSPACE,
|
|
130
|
+
workspaceLabel,
|
|
118
131
|
} from "./names.ts";
|
|
119
132
|
import {
|
|
120
133
|
CONFIGMAPS,
|
|
@@ -770,6 +783,7 @@ export function kubeSandbox(opts: KubeSandboxOptions = {}): SandboxPort {
|
|
|
770
783
|
name: string;
|
|
771
784
|
runId: string;
|
|
772
785
|
workflow: string;
|
|
786
|
+
path: string;
|
|
773
787
|
image?: string;
|
|
774
788
|
user?: string;
|
|
775
789
|
workGroup?: number;
|
|
@@ -802,8 +816,16 @@ export function kubeSandbox(opts: KubeSandboxOptions = {}): SandboxPort {
|
|
|
802
816
|
metadata: {
|
|
803
817
|
name: req.name,
|
|
804
818
|
namespace: ns,
|
|
805
|
-
// The run↔workspace link `jr2
|
|
806
|
-
|
|
819
|
+
// The run↔workspace link the `jr2 workspace` verbs read (ADR-0009/0012), without the host:
|
|
820
|
+
// the run, the Workflow and a selector form of the path as labels, the exact path as an
|
|
821
|
+
// annotation. The hold mark is `held` from this first write (ADR-0021); the Lease writes it
|
|
822
|
+
// again with each renewal, and `released` when it stops for good.
|
|
823
|
+
labels: {
|
|
824
|
+
[LABEL_SANDBOX_RUN]: req.runId,
|
|
825
|
+
[LABEL_SANDBOX_WORKFLOW]: req.workflow,
|
|
826
|
+
...(req.path ? { [LABEL_SANDBOX_WORKSPACE]: workspaceLabel(req.path) } : {}),
|
|
827
|
+
},
|
|
828
|
+
annotations: { ...(req.path ? { [ANNOTATION_WORKSPACE_PATH]: req.path } : {}), [ANNOTATION_HOLD]: HOLD_HELD },
|
|
807
829
|
},
|
|
808
830
|
spec: {
|
|
809
831
|
// The Sandbox Image, unmodified (ADR-0037) — the user's tools, its own USER and HOME, and
|
|
@@ -888,6 +910,9 @@ export function kubeSandbox(opts: KubeSandboxOptions = {}): SandboxPort {
|
|
|
888
910
|
// that Workspace is `Lost` (ADR-0021) — and mounted below into the Harness container
|
|
889
911
|
// ALONE: the User Container must not read an Agent's history.
|
|
890
912
|
{ name: CONVERSATIONS_VOLUME, emptyDir: {} },
|
|
913
|
+
// The shell socket's folder (ADR-0076): the Harness listens there for `jr2 workspace
|
|
914
|
+
// shell`, in the Harness container ALONE, where no process that it confines makes a file.
|
|
915
|
+
{ name: SHELL_VOLUME, emptyDir: {} },
|
|
891
916
|
// Only for ADR-0037's fallback seat: uid 1000 on a stranger's base has no home at all.
|
|
892
917
|
...seat.homeVolume,
|
|
893
918
|
// The Custodian's (its values, leaves and config), and the trust ConfigMap (ADR-0020).
|
|
@@ -902,6 +927,7 @@ export function kubeSandbox(opts: KubeSandboxOptions = {}): SandboxPort {
|
|
|
902
927
|
{ name: "work", mountPath: WORK_ROOT },
|
|
903
928
|
RUNTIME_VOLUME_MOUNT,
|
|
904
929
|
{ name: CONVERSATIONS_VOLUME, mountPath: CONVERSATIONS_DIR },
|
|
930
|
+
{ name: SHELL_VOLUME, mountPath: SHELL_DIR },
|
|
905
931
|
...seat.homeMount,
|
|
906
932
|
...custodian.harnessMounts,
|
|
907
933
|
],
|
|
@@ -1448,13 +1474,35 @@ export function kubeSandbox(opts: KubeSandboxOptions = {}): SandboxPort {
|
|
|
1448
1474
|
leaseIntervalMs,
|
|
1449
1475
|
|
|
1450
1476
|
async renew(name) {
|
|
1451
|
-
// A write, and only that (ADR-0021): the stamp the operator's idle GC reads
|
|
1452
|
-
//
|
|
1477
|
+
// A write, and only that (ADR-0021): the stamp the operator's idle GC reads, and the hold
|
|
1478
|
+
// mark, written again so a restored run's Lease marks the Sandbox `held`. What the workspace
|
|
1479
|
+
// IS comes from the watch, never from this answer.
|
|
1453
1480
|
await kube().patch(SANDBOXES, ns, name, {
|
|
1454
|
-
metadata: { annotations: {
|
|
1481
|
+
metadata: { annotations: { [ANNOTATION_KEEPALIVE]: new Date().toISOString(), [ANNOTATION_HOLD]: HOLD_HELD } },
|
|
1455
1482
|
});
|
|
1456
1483
|
},
|
|
1457
1484
|
|
|
1485
|
+
async releaseHold(name) {
|
|
1486
|
+
// The Lease stopped for good (ADR-0021): no run holds this Sandbox now, and `jr2 workspace rm`
|
|
1487
|
+
// may remove it. A write, and only that.
|
|
1488
|
+
await kube().patch(SANDBOXES, ns, name, { metadata: { annotations: { [ANNOTATION_HOLD]: HOLD_RELEASED } } });
|
|
1489
|
+
},
|
|
1490
|
+
|
|
1491
|
+
async releaseRun(runId) {
|
|
1492
|
+
// A restore ended this run without hosting it (ADR-0021): no Lease runs, so each of its
|
|
1493
|
+
// Sandboxes gets the mark here. The list is the cluster's, not the watch's cache, which may
|
|
1494
|
+
// not have listed yet at boot.
|
|
1495
|
+
const list = await kube().list<SandboxObject>(SANDBOXES, ns, { labelSelector: `${LABEL_SANDBOX_RUN}=${runId}` });
|
|
1496
|
+
await Promise.all(
|
|
1497
|
+
list.items.flatMap((sb) => {
|
|
1498
|
+
const name = sb.metadata?.name;
|
|
1499
|
+
return name
|
|
1500
|
+
? [kube().patch(SANDBOXES, ns, name, { metadata: { annotations: { [ANNOTATION_HOLD]: HOLD_RELEASED } } })]
|
|
1501
|
+
: [];
|
|
1502
|
+
}),
|
|
1503
|
+
);
|
|
1504
|
+
},
|
|
1505
|
+
|
|
1458
1506
|
continuity(name, listener) {
|
|
1459
1507
|
// The watch's word on this name (ADR-0063): the first list, then every event. A dropped
|
|
1460
1508
|
// watch says nothing, so this says nothing either — unknown is never loss.
|