@jr2/orchestrator 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +56 -20
- package/docs/concepts/architecture.md +30 -17
- package/docs/concepts/charts.md +6 -3
- package/docs/concepts/converge.md +3 -0
- package/docs/concepts/effects.md +86 -31
- package/docs/concepts/machines-and-workflows.md +9 -0
- package/docs/concepts/pools-and-sources.md +6 -6
- package/docs/concepts/secrets-and-trust.md +126 -42
- package/docs/concepts/the-menu.md +3 -0
- package/docs/concepts/workspaces.md +129 -51
- package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
- 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 +17 -4
- package/docs/guides/bind-open-parts.md +5 -4
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +1 -1
- package/docs/guides/let-an-agent-write-a-chart.md +5 -1
- package/docs/guides/look-inside-a-sandbox.md +18 -15
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +19 -18
- package/docs/guides/set-an-agents-access-and-dials.md +67 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/use-a-hosted-provider.md +6 -0
- package/docs/guides/watch-a-run.md +47 -24
- package/docs/guides/write-a-workflow.md +1 -1
- package/docs/llms.txt +33 -12
- package/docs/reference/authoring-surface/agent.md +34 -11
- package/docs/reference/authoring-surface/answer.md +19 -3
- package/docs/reference/authoring-surface/arg.md +27 -5
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/command.md +36 -21
- package/docs/reference/authoring-surface/fail.md +17 -4
- package/docs/reference/authoring-surface/file.md +36 -16
- package/docs/reference/authoring-surface/http.md +108 -8
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +1 -1
- package/docs/reference/authoring-surface/open.md +36 -5
- package/docs/reference/authoring-surface/paramsof.md +22 -4
- package/docs/reference/authoring-surface/pool.md +4 -4
- package/docs/reference/authoring-surface/push.md +30 -12
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +25 -4
- package/docs/reference/authoring-surface/source.md +3 -3
- package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
- 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 +5 -6
- 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 +22 -4
- package/docs/reference/authoring-surface/type-customize.md +4 -2
- package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- 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-hostinjectedinput.md +2 -4
- 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 +7 -1
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- 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-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 +2 -2
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
- package/docs/reference/authoring-surface/type-reposlot.md +5 -5
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
- package/docs/reference/authoring-surface/type-workspaced.md +29 -8
- 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 +65 -37
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +30 -10
- package/docs/reference/cli.md +1 -1
- package/docs/reference/config.md +13 -4
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
- 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/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +2 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
- 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 +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +3 -0
- package/docs/reference/glossary.md +51 -12
- package/docs/reference/http-api.md +45 -7
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +5 -4
- package/package.json +3 -3
- package/src/actor.ts +161 -35
- package/src/agent.ts +6 -6
- package/src/ambient.ts +3 -5
- package/src/chart.ts +186 -19
- package/src/charts.ts +9 -2
- package/src/config.ts +16 -6
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +251 -77
- package/src/customize.ts +11 -3
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +343 -127
- package/src/effector-wire.ts +6 -3
- package/src/fail.ts +17 -4
- package/src/harness-client.ts +74 -31
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +88 -32
- package/src/http.ts +56 -17
- package/src/index.ts +15 -1
- package/src/internal.ts +1 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +3 -4
- package/src/model-keys.ts +24 -0
- package/src/names.ts +19 -6
- package/src/notices.ts +443 -54
- package/src/open-body.ts +85 -19
- package/src/open.ts +36 -5
- package/src/parts.ts +40 -28
- package/src/party.ts +33 -0
- package/src/pick.ts +41 -7
- package/src/pool.ts +44 -23
- package/src/registration.ts +53 -11
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +106 -20
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +63 -16
- package/src/secret-placement.ts +9 -2
- package/src/setup.ts +86 -6
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +10 -10
- package/src/workspace.ts +264 -253
- 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/src/run-host.ts
CHANGED
|
@@ -34,7 +34,7 @@ import { SENDER_KEY, type EventDef, type EventSemantics, type Sender } from "@jr
|
|
|
34
34
|
import { partiesAt, seatOf, senderText } from "./party.ts";
|
|
35
35
|
import { placeAnswers, type AnswerSeat } from "./pick.ts";
|
|
36
36
|
import { inputSchemaOf } from "./vocabulary.ts";
|
|
37
|
-
import type { EchoEvent, EchoStatusChild, LiveConversation, PickAnswer } from "./wire.ts";
|
|
37
|
+
import type { EchoEvent, EchoStatusChild, LiveConversation, Notice, PickAnswer } from "./wire.ts";
|
|
38
38
|
import { answerOf, childErrorOf, replyOf, type Hold } from "./holds.ts";
|
|
39
39
|
import { PickCauses } from "./cause.ts";
|
|
40
40
|
import { restOf, runningBelow } from "./rest.ts";
|
|
@@ -404,7 +404,7 @@ type Sending = {
|
|
|
404
404
|
|
|
405
405
|
/**
|
|
406
406
|
* Whose pick started a child (ADR-0067): the Agent's slot, and the invoke id of the Agent's state the
|
|
407
|
-
* pick was made at, which tells one slot
|
|
407
|
+
* pick was made at, which tells apart two states that invoke one slot, one after the other. `invoke` is absent when the event that
|
|
408
408
|
* started the child named the Agent and not where (a pick taken outside its own send).
|
|
409
409
|
*/
|
|
410
410
|
type StartedBy = { agent: string; invoke?: string };
|
|
@@ -536,8 +536,17 @@ export type RunStatus = RunRecord & {
|
|
|
536
536
|
* direction, read off the caller's own ledger rows. A foreign dispatch's run is a run of the
|
|
537
537
|
* Orchestrator its target names. Absent when it started none. */
|
|
538
538
|
dispatched?: Dispatched[];
|
|
539
|
+
/** The Worktrees this run's cuts made, oldest first (ADR-0075), read off its ledger's settled
|
|
540
|
+
* `ready` rows, so an Agent's `git switch` does not change them. A re-cut of one path and branch
|
|
541
|
+
* is one entry. Behind the token, like `context`: a branch name is the workflow's data. Absent
|
|
542
|
+
* when the run cut none. */
|
|
543
|
+
worktrees?: RunWorktree[];
|
|
539
544
|
};
|
|
540
545
|
|
|
546
|
+
/** One Worktree a `worktree()` Effect of this run cut (ADR-0075): its Repo Slot, its path, and the
|
|
547
|
+
* branch of the cut. `branch` is `null` for a detached Worktree. */
|
|
548
|
+
export type RunWorktree = { slot: string; path: string; branch: string | null };
|
|
549
|
+
|
|
541
550
|
/** One run a `dispatch` Effect of this run started (ADR-0071): its slot, the run, and when the
|
|
542
551
|
* ledger row was written. */
|
|
543
552
|
export type Dispatched = { slot: string; run: string; at: string };
|
|
@@ -668,6 +677,9 @@ export type AgentSurfaceView = {
|
|
|
668
677
|
instanceId: string;
|
|
669
678
|
runId: string;
|
|
670
679
|
sandbox?: string;
|
|
680
|
+
/** The slot this surface belongs to (ADR-0076): its Agent's actor path and agent name — the slot
|
|
681
|
+
* the Harness must state to speak here. Absent on a registration no pod hosts. */
|
|
682
|
+
slot?: string;
|
|
671
683
|
menu: SurfaceEvent[];
|
|
672
684
|
allowed: string[];
|
|
673
685
|
};
|
|
@@ -961,8 +973,10 @@ type RunBlob = {
|
|
|
961
973
|
* The pending NOTICES (ADR-0062): what the next Turn in a scope must be told — a memory kill in
|
|
962
974
|
* its Workspace, a conversation that is new. The Orchestrator is their only keeper, so they ride
|
|
963
975
|
* this blob beside the two ledgers, for the same reasons: the run's exact lifetime, and the
|
|
964
|
-
* snapshot's atomicity. A notice
|
|
965
|
-
* it, so a restart before that save delivers it again and a restart
|
|
976
|
+
* snapshot's atomicity. A notice is cleared for a conversation in the save that ledgers the
|
|
977
|
+
* admission that carried it there, so a restart before that save delivers it again and a restart
|
|
978
|
+
* after does not. A conversation notice then leaves it; a Workspace notice stays, with the
|
|
979
|
+
* conversations that heard it, for the Workspace's other conversations.
|
|
966
980
|
*/
|
|
967
981
|
notices?: NoticeLedgerState;
|
|
968
982
|
/**
|
|
@@ -1740,6 +1754,7 @@ export class RunHost {
|
|
|
1740
1754
|
instanceId,
|
|
1741
1755
|
runId: reg.runId,
|
|
1742
1756
|
sandbox: reg.sandbox,
|
|
1757
|
+
...(reg.slot !== undefined ? { slot: reg.slot } : {}),
|
|
1743
1758
|
menu: [...menuOf(reg).values()].map((def) => ({
|
|
1744
1759
|
name: def.name,
|
|
1745
1760
|
description: def.description,
|
|
@@ -1757,6 +1772,19 @@ export class RunHost {
|
|
|
1757
1772
|
this.table.lookup(agentAddress(instanceId))?.served?.();
|
|
1758
1773
|
}
|
|
1759
1774
|
|
|
1775
|
+
/** The notices this Turn's conversation hears at a step boundary (ADR-0062): its pending notices
|
|
1776
|
+
* in their audience, reserved for this read until the Harness acknowledges them
|
|
1777
|
+
* ({@link noticesHeard}) or the Turn ends. Empty for a registration that hears none. */
|
|
1778
|
+
async boundaryNotices(instanceId: string): Promise<Array<{ id: string; notice: Notice }>> {
|
|
1779
|
+
return (await this.table.lookup(agentAddress(instanceId))?.boundaryNotices?.()) ?? [];
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
/** The Harness appended these step-boundary notices to the Turn (ADR-0062): they are heard in its
|
|
1783
|
+
* conversation, ledgered in one save. Answers how many were this conversation's reservations. */
|
|
1784
|
+
noticesHeard(instanceId: string, ids: readonly string[]): number {
|
|
1785
|
+
return this.table.lookup(agentAddress(instanceId))?.noticesHeard?.(ids) ?? 0;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1760
1788
|
/**
|
|
1761
1789
|
* Deliver one event from an Agent's Harness (`POST /agents/:iid/events` — ADR-0013). Validation
|
|
1762
1790
|
* and delivery are the table's; this only agent-scopes the address and mints the receipt. (A
|
|
@@ -1890,6 +1918,7 @@ export class RunHost {
|
|
|
1890
1918
|
agent: invoking.sender?.id ?? "",
|
|
1891
1919
|
since,
|
|
1892
1920
|
...(invoking.sandbox !== undefined ? { sandbox: invoking.sandbox } : {}),
|
|
1921
|
+
...(invoking.slot !== undefined ? { slot: invoking.slot } : {}),
|
|
1893
1922
|
...(invoking.invokeId !== undefined ? { invokeId: invoking.invokeId } : {}),
|
|
1894
1923
|
...(conversation !== undefined ? { conversation } : {}),
|
|
1895
1924
|
...(sending && sending.chain.length > 0 ? { chain: [...sending.chain] } : {}),
|
|
@@ -2073,8 +2102,8 @@ export class RunHost {
|
|
|
2073
2102
|
* its own send, else the one outstanding. On a transition below no Agent's state (`null`): the
|
|
2074
2103
|
* event that took the transition names the Agent — the sender of a pick, or the Agent whose pick
|
|
2075
2104
|
* started the child whose done or error event it is — and that Agent's pick is the one shaped, and
|
|
2076
|
-
* no other; none when that Agent has no pick being answered. When one slot is invoked
|
|
2077
|
-
*
|
|
2105
|
+
* no other; none when that Agent has no pick being answered. When one slot is invoked by two
|
|
2106
|
+
* states, the event names an invoke too, and only the pick at that invoke is shaped: the sending
|
|
2078
2107
|
* pick, or the pick at the invoke whose pick started the child; none when that pick is not being
|
|
2079
2108
|
* answered (its Turn ended, and its child runs on). An event that names no Agent (a timer,
|
|
2080
2109
|
* a Gate's answer, an outside send) shapes the pick in its own send. Outside one, it shapes the one
|
|
@@ -2106,7 +2135,7 @@ export class RunHost {
|
|
|
2106
2135
|
const { agent } = named;
|
|
2107
2136
|
const own = sending && sending.reg.sender?.id === agent ? sending : undefined;
|
|
2108
2137
|
// That Agent's picks being answered: the one in its own send, and the ones outstanding at
|
|
2109
|
-
// its other invokes (one slot invoked
|
|
2138
|
+
// its other invokes (one slot invoked by two states). The event names the invoke of the
|
|
2110
2139
|
// pick that sent it or started its child, and only the pick at that invoke is shaped; none
|
|
2111
2140
|
// when that pick is not being answered, and each other pick keeps its default answer.
|
|
2112
2141
|
const held = open.filter((hold) => hold.agent === agent && hold.instanceId !== own?.reg.id);
|
|
@@ -2177,10 +2206,16 @@ export class RunHost {
|
|
|
2177
2206
|
return this.parked.has(`${instanceId} ${deliveryId}`);
|
|
2178
2207
|
}
|
|
2179
2208
|
|
|
2180
|
-
/** The
|
|
2181
|
-
|
|
2209
|
+
/** The pod and slot that may read a delivery's answer (ADR-0013, ADR-0076), or undefined for an
|
|
2210
|
+
* unknown one. */
|
|
2211
|
+
deliveryScope(instanceId: string, deliveryId: string): { sandbox?: string; slot?: string } | undefined {
|
|
2182
2212
|
const found = this.holdAt(instanceId, deliveryId);
|
|
2183
|
-
return
|
|
2213
|
+
return (
|
|
2214
|
+
found && {
|
|
2215
|
+
...(found.hold.sandbox !== undefined ? { sandbox: found.hold.sandbox } : {}),
|
|
2216
|
+
...(found.hold.slot !== undefined ? { slot: found.hold.slot } : {}),
|
|
2217
|
+
}
|
|
2218
|
+
);
|
|
2184
2219
|
}
|
|
2185
2220
|
|
|
2186
2221
|
/**
|
|
@@ -2507,6 +2542,7 @@ export class RunHost {
|
|
|
2507
2542
|
? { output: snap.output }
|
|
2508
2543
|
: {}),
|
|
2509
2544
|
...dispatchedField(blob.effects ?? {}),
|
|
2545
|
+
...worktreesField(blob.effects ?? {}),
|
|
2510
2546
|
};
|
|
2511
2547
|
}
|
|
2512
2548
|
|
|
@@ -2762,6 +2798,7 @@ export class RunHost {
|
|
|
2762
2798
|
? { output: snap.output }
|
|
2763
2799
|
: {}),
|
|
2764
2800
|
...dispatchedField(run.effects),
|
|
2801
|
+
...worktreesField(run.effects),
|
|
2765
2802
|
};
|
|
2766
2803
|
}
|
|
2767
2804
|
|
|
@@ -2830,10 +2867,20 @@ export class RunHost {
|
|
|
2830
2867
|
// moment the Harness admits it, and the ledger hits the store in the same RunBlob save. An
|
|
2831
2868
|
// admission arriving around stop/untrack still lands in `agents` but skips the save,
|
|
2832
2869
|
// exactly like the persist scheduler's tracked-run guard.
|
|
2833
|
-
recordAdmission: (instanceId, admission, delivered = []) => {
|
|
2870
|
+
recordAdmission: (instanceId, admission, delivered = [], taker, member) => {
|
|
2834
2871
|
agents[instanceId] = admission;
|
|
2835
|
-
// The notices this admission carried are delivered by THIS save
|
|
2836
|
-
|
|
2872
|
+
// The notices this admission carried are delivered by THIS save, in the conversation it
|
|
2873
|
+
// rode (ADR-0062): a Workspace notice stays pending for the rest of its audience. And the
|
|
2874
|
+
// conversation is a member of its Workspace's audiences from now on, in the same save.
|
|
2875
|
+
notices.delivered(delivered, taker);
|
|
2876
|
+
if (member !== undefined && taker !== undefined) {
|
|
2877
|
+
notices.admitted(member.workspace, {
|
|
2878
|
+
taker,
|
|
2879
|
+
slot: member.slot,
|
|
2880
|
+
continued: member.continued,
|
|
2881
|
+
since: new Date().toISOString(),
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2837
2884
|
const run = this.runs.get(record.runId);
|
|
2838
2885
|
if (run && run.agents === agents) this.persist(run);
|
|
2839
2886
|
},
|
|
@@ -2845,6 +2892,9 @@ export class RunHost {
|
|
|
2845
2892
|
// invoke would otherwise restore into the burned conversation.
|
|
2846
2893
|
epochOf: (conversation) => epochs[conversation] ?? 0,
|
|
2847
2894
|
bumpEpoch: (conversation, reason) => {
|
|
2895
|
+
// The burned conversation can take no Turn again: it leaves every audience (ADR-0062).
|
|
2896
|
+
const burned = epochs[conversation] ? `${conversation}/${epochs[conversation]}` : conversation;
|
|
2897
|
+
notices.forget(burned);
|
|
2848
2898
|
epochs[conversation] = (epochs[conversation] ?? 0) + 1;
|
|
2849
2899
|
// The next `continue` lands on a virgin conversation and is told so, with the fault's own
|
|
2850
2900
|
// words (ADR-0062) — raised in the SAME save as the bump, so no restart can split them.
|
|
@@ -2854,18 +2904,36 @@ export class RunHost {
|
|
|
2854
2904
|
},
|
|
2855
2905
|
// The notices' other halves (ADR-0062). A raise is saved at the write, like the ledgers; a
|
|
2856
2906
|
// take is a reservation in memory, delivered by `recordAdmission` or released on failure.
|
|
2857
|
-
raiseNotice: (notice, to, source, older) => {
|
|
2858
|
-
if (!notices.raise(notice, to, source, older)) return;
|
|
2907
|
+
raiseNotice: (notice, to, source, older, raiseOpts) => {
|
|
2908
|
+
if (!notices.raise(notice, to, source, older, raiseOpts)) return;
|
|
2859
2909
|
const run = this.runs.get(record.runId);
|
|
2860
2910
|
if (run && run.notices === notices) this.persist(run);
|
|
2861
2911
|
},
|
|
2862
|
-
restoreNotice: (notice, to, source) => {
|
|
2863
|
-
if (!notices.restore(notice, to, source)) return;
|
|
2912
|
+
restoreNotice: (notice, to, source, taker) => {
|
|
2913
|
+
if (!notices.restore(notice, to, source, taker)) return;
|
|
2864
2914
|
const run = this.runs.get(record.runId);
|
|
2865
2915
|
if (run && run.notices === notices) this.persist(run);
|
|
2866
2916
|
},
|
|
2867
2917
|
takeNotices: (scope) => notices.take(scope),
|
|
2868
|
-
releaseNotices: (ids) => notices.release(ids),
|
|
2918
|
+
releaseNotices: (ids, taker) => notices.release(ids, taker),
|
|
2919
|
+
peekNotices: (scope) => notices.peek(scope),
|
|
2920
|
+
acknowledgeNotices: (ids, taker) => {
|
|
2921
|
+
const heard = notices.acknowledge(ids, taker);
|
|
2922
|
+
const run = this.runs.get(record.runId);
|
|
2923
|
+
if (heard > 0 && run && run.notices === notices) this.persist(run);
|
|
2924
|
+
return heard;
|
|
2925
|
+
},
|
|
2926
|
+
noticesTold: (told) => {
|
|
2927
|
+
let changed = false;
|
|
2928
|
+
for (const { source, takers } of told) changed = notices.told(source, takers) || changed;
|
|
2929
|
+
const run = this.runs.get(record.runId);
|
|
2930
|
+
if (changed && run && run.notices === notices) this.persist(run);
|
|
2931
|
+
},
|
|
2932
|
+
noticeTurnEnded: (workspace, takers) => {
|
|
2933
|
+
if (!notices.ended(workspace, takers)) return;
|
|
2934
|
+
const run = this.runs.get(record.runId);
|
|
2935
|
+
if (run && run.notices === notices) this.persist(run);
|
|
2936
|
+
},
|
|
2869
2937
|
// Absorbed-retry attempts go straight to the run's observers (SSE/CLI watch) — they are
|
|
2870
2938
|
// feed events, not machine events (ADR-0016: the workflow sees only the terminal fault).
|
|
2871
2939
|
telemetry: (event) => {
|
|
@@ -3331,8 +3399,6 @@ function atOrBelow(actor: AnyActorRef, ref: AnyActorRef): boolean {
|
|
|
3331
3399
|
return false;
|
|
3332
3400
|
}
|
|
3333
3401
|
|
|
3334
|
-
/** A status's bound fields (ADR-0070): `bound`, and the hashes of the Charts bound, distinct and
|
|
3335
|
-
* sorted. Both absent when the snapshot binds no Chart. */
|
|
3336
3402
|
/** The runs a run's `dispatch` Effects started (ADR-0071), from its ledger: an intent row the
|
|
3337
3403
|
* start's answer re-wrote with its run, a settled row whose output names one (`started`,
|
|
3338
3404
|
* `settled`), and an aborted row that kept it. A row that never learned its run names none. */
|
|
@@ -3350,6 +3416,26 @@ function dispatchedField(effects: Record<string, EffectRow>): { dispatched?: Dis
|
|
|
3350
3416
|
return out.length ? { dispatched: out } : {};
|
|
3351
3417
|
}
|
|
3352
3418
|
|
|
3419
|
+
/** The Worktrees a run's cuts made (ADR-0075), from the settled `ready` rows of its ledger, in the
|
|
3420
|
+
* order of each Worktree's first cut. A cut also reads a Worktree, so one path and branch has many
|
|
3421
|
+
* rows and one entry. */
|
|
3422
|
+
function worktreesField(effects: Record<string, EffectRow>): { worktrees?: RunWorktree[] } {
|
|
3423
|
+
const rows = Object.values(effects).flatMap((row) =>
|
|
3424
|
+
row.state === "settled" && row.cut !== undefined && row.repo !== undefined
|
|
3425
|
+
? [{ at: row.at, slot: row.repo, ...row.cut }]
|
|
3426
|
+
: [],
|
|
3427
|
+
);
|
|
3428
|
+
rows.sort((a, b) => (a.at < b.at ? -1 : a.at > b.at ? 1 : 0));
|
|
3429
|
+
const out = new Map<string, RunWorktree>();
|
|
3430
|
+
for (const { slot, path, branch } of rows) {
|
|
3431
|
+
const key = JSON.stringify([path, branch]);
|
|
3432
|
+
if (!out.has(key)) out.set(key, { slot, path, branch });
|
|
3433
|
+
}
|
|
3434
|
+
return out.size ? { worktrees: [...out.values()] } : {};
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
/** A status's bound fields (ADR-0070): `bound`, and the hashes of the Charts bound, distinct and
|
|
3438
|
+
* sorted. Both absent when the snapshot binds no Chart. */
|
|
3353
3439
|
function boundFields(snap: unknown): { bound?: true; chartHashes?: string[] } {
|
|
3354
3440
|
const hashes = [...new Set(boundChartRoutesOf(snap).map((route) => route.chartHash))].sort();
|
|
3355
3441
|
return hashes.length ? { bound: true, chartHashes: hashes } : {};
|