@astrosheep/keiyaku 4.5.19 → 4.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
- package/build/src/akuma/akuma-handle.d.ts +5 -1
- package/build/src/akuma/akuma-handle.js +4 -1
- package/build/src/akuma/akuma-instance.d.ts +7 -2
- package/build/src/akuma/akuma-instance.js +14 -7
- package/build/src/akuma/akuma-observe.d.ts +11 -0
- package/build/src/akuma/akuma-observe.js +23 -0
- package/build/src/akuma/akuma-product.d.ts +2 -0
- package/build/src/akuma/akuma-product.js +6 -0
- package/build/src/akuma/akuma.d.ts +20 -1
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +20 -0
- package/build/src/akuma/allowed.js +11 -0
- package/build/src/akuma/archetype.js +3 -0
- package/build/src/akuma/body.d.ts +25 -5
- package/build/src/akuma/body.js +70 -31
- package/build/src/akuma/call-request.d.ts +5 -0
- package/build/src/akuma/call-request.js +14 -4
- package/build/src/akuma/fleet-execution.d.ts +28 -0
- package/build/src/akuma/fleet-execution.js +36 -4
- package/build/src/akuma/fleet-observation.d.ts +54 -0
- package/build/src/akuma/fleet-request.d.ts +10 -0
- package/build/src/akuma/fleet-request.js +13 -3
- package/build/src/akuma/heart/facts.d.ts +2 -0
- package/build/src/akuma/heart/index.d.ts +1 -0
- package/build/src/akuma/heart/index.js +4 -1
- package/build/src/akuma/heart/rows.d.ts +2 -0
- package/build/src/akuma/heart/rows.js +7 -5
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +3 -1
- package/build/src/akuma/heart/tells.js +6 -5
- package/build/src/akuma/heart/timeline.js +1 -1
- package/build/src/akuma/provider-recipe.d.ts +1 -0
- package/build/src/akuma/provider-recipe.js +5 -0
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +1 -1
- package/build/src/akuma/providers/acp/core.js +3 -6
- package/build/src/akuma/providers/acp/index.js +3 -0
- package/build/src/akuma/providers/claude/index.js +10 -10
- package/build/src/akuma/providers/codex-app-server/index.js +12 -11
- package/build/src/akuma/providers/execution-environment.d.ts +6 -0
- package/build/src/akuma/providers/execution-environment.js +24 -0
- package/build/src/akuma/providers/grok-build/index.js +3 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
- package/build/src/akuma/providers/pi/events.js +7 -2
- package/build/src/akuma/providers/pi/index.js +5 -2
- package/build/src/akuma/request-observation.d.ts +45 -0
- package/build/src/akuma/request-observation.js +162 -0
- package/build/src/akuma/request-rendezvous.d.ts +6 -3
- package/build/src/akuma/request-rendezvous.js +80 -14
- package/build/src/akuma/request-serve.js +27 -4
- package/build/src/akuma/request-wire.d.ts +5 -0
- package/build/src/akuma/request-wire.js +2 -0
- package/build/src/akuma/turn-drive.d.ts +1 -0
- package/build/src/akuma/turn-drive.js +2 -0
- package/build/src/akuma-body.js +5 -0
- package/build/src/body/amend.js +6 -4
- package/build/src/body/decode.d.ts +3 -1
- package/build/src/body/decode.js +53 -17
- package/build/src/body/region.d.ts +9 -1
- package/build/src/body/region.js +87 -12
- package/build/src/body/render.js +1 -1
- package/build/src/body/verification.d.ts +3 -1
- package/build/src/body/verification.js +4 -1
- package/build/src/cli/accepted.js +19 -2
- package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
- package/build/src/cli/commands/akuma-invoke.js +134 -14
- package/build/src/cli/commands/akuma.js +24 -12
- package/build/src/cli/commands/contract-help.d.ts +15 -15
- package/build/src/cli/commands/contract-help.js +45 -19
- package/build/src/cli/commands/contract-invoke.d.ts +3 -0
- package/build/src/cli/commands/contract-invoke.js +25 -11
- package/build/src/cli/commands/contract.js +6 -4
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +1 -1
- package/build/src/cli/commands/status-set.js +15 -24
- package/build/src/cli/commands/task.js +18 -18
- package/build/src/cli/invoke.d.ts +3 -0
- package/build/src/cli/invoke.js +58 -23
- package/build/src/cli/main.js +5 -0
- package/build/src/cli/parse.d.ts +3 -0
- package/build/src/cli/parse.js +26 -4
- package/build/src/cli/render/akuma-activity.d.ts +100 -2
- package/build/src/cli/render/akuma-activity.js +485 -40
- package/build/src/cli/render/akuma.d.ts +10 -0
- package/build/src/cli/render/akuma.js +31 -21
- package/build/src/cli/render/audit.js +1 -1
- package/build/src/cli/render/board.js +1 -1
- package/build/src/cli/render/catalog.js +16 -13
- package/build/src/cli/render/contract-history.js +39 -10
- package/build/src/cli/render/contract-observation.js +10 -6
- package/build/src/cli/render/contract.js +109 -106
- package/build/src/cli/render/execution-progress.d.ts +26 -0
- package/build/src/cli/render/execution-progress.js +178 -0
- package/build/src/cli/render/kanshi-akuma.js +27 -39
- package/build/src/cli/render/kanshi.js +33 -26
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/render/receipt.d.ts +1 -1
- package/build/src/cli/render/receipt.js +6 -6
- package/build/src/cli/render/refusal.js +10 -7
- package/build/src/cli/render/region.js +1 -1
- package/build/src/cli/render/settings.js +12 -2
- package/build/src/cli/render/status-line.d.ts +30 -0
- package/build/src/cli/render/status-line.js +58 -0
- package/build/src/cli/render/status-set.js +1 -1
- package/build/src/cli/render/task.d.ts +2 -0
- package/build/src/cli/render/task.js +11 -7
- package/build/src/cli/render/terminal.d.ts +0 -1
- package/build/src/cli/render/terminal.js +47 -15
- package/build/src/cli/result.d.ts +1 -0
- package/build/src/cli/runtime.d.ts +2 -0
- package/build/src/cli/runtime.js +36 -4
- package/build/src/cli/usage.js +2 -2
- package/build/src/cli/version.d.ts +1 -0
- package/build/src/cli/version.js +25 -0
- package/build/src/git/hooks.d.ts +13 -1
- package/build/src/git/hooks.js +2 -1
- package/build/src/git/workspace.d.ts +1 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/identity/mint.d.ts +13 -0
- package/build/src/identity/mint.js +26 -0
- package/build/src/identity/normalize.d.ts +4 -0
- package/build/src/identity/normalize.js +30 -0
- package/build/src/index.d.ts +2 -0
- package/build/src/index.js +1 -0
- package/build/src/kanshi/read.js +17 -4
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/akuma-creation.d.ts +2 -0
- package/build/src/library/akuma-creation.js +34 -18
- package/build/src/library/audit.d.ts +2 -0
- package/build/src/library/audit.js +1 -0
- package/build/src/library/bind.js +18 -16
- package/build/src/library/composition.d.ts +1 -0
- package/build/src/library/contract-bind.d.ts +3 -1
- package/build/src/library/contract-bind.js +5 -3
- package/build/src/library/contract-execution.d.ts +2 -0
- package/build/src/library/contract-execution.js +15 -1
- package/build/src/library/contract-forwarding.d.ts +4 -0
- package/build/src/library/contract-forwarding.js +3 -0
- package/build/src/library/contract-handle.d.ts +7 -0
- package/build/src/library/contract-handle.js +25 -0
- package/build/src/library/contract-operations.d.ts +7 -2
- package/build/src/library/contract-operations.js +21 -1
- package/build/src/library/contract-types.d.ts +1 -0
- package/build/src/library/execution-result.d.ts +2 -2
- package/build/src/library/execution.d.ts +8 -0
- package/build/src/library/execution.js +102 -0
- package/build/src/library/fleet.d.ts +4 -1
- package/build/src/library/fleet.js +32 -8
- package/build/src/library/keiyaku.d.ts +2 -0
- package/build/src/library/mutation.js +15 -7
- package/build/src/plugin/akuma-signals.d.ts +6 -0
- package/build/src/plugin/akuma-signals.js +9 -1
- package/build/src/plugin/public.d.ts +6 -2
- package/build/src/plugin/runtime.d.ts +1 -3
- package/build/src/plugin/runtime.js +95 -103
- package/build/src/protocol/amend.js +4 -3
- package/build/src/protocol/attempt.d.ts +2 -0
- package/build/src/protocol/bind.js +41 -14
- package/build/src/protocol/completion.d.ts +3 -0
- package/build/src/protocol/completion.js +35 -11
- package/build/src/protocol/deliver.js +165 -92
- package/build/src/protocol/execution-observation.d.ts +181 -0
- package/build/src/protocol/execution-observation.js +45 -0
- package/build/src/protocol/intent.d.ts +18 -22
- package/build/src/protocol/intent.js +63 -41
- package/build/src/protocol/operations.d.ts +2 -0
- package/build/src/protocol/placement.js +5 -1
- package/build/src/protocol/progress.d.ts +5 -1
- package/build/src/protocol/progress.js +10 -1
- package/build/src/protocol/read/status.d.ts +1 -0
- package/build/src/protocol/read/status.js +10 -2
- package/build/src/protocol/reintegrate.d.ts +1 -1
- package/build/src/protocol/reintegrate.js +70 -43
- package/build/src/protocol/result-codec.js +27 -11
- package/build/src/protocol/review.js +53 -30
- package/build/src/protocol/run.d.ts +83 -36
- package/build/src/protocol/run.js +90 -75
- package/build/src/runtime/proc/run.d.ts +16 -4
- package/build/src/runtime/proc/run.js +64 -22
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settings.js +5 -14
- package/build/src/task/document.d.ts +5 -1
- package/build/src/task/document.js +6 -1
- package/build/src/verification/execution.d.ts +10 -2
- package/build/src/verification/execution.js +143 -42
- package/build/src/verification/observation.d.ts +41 -0
- package/build/src/verification/observation.js +29 -0
- package/package.json +3 -1
|
@@ -2,7 +2,7 @@ import { worktreeChangeId } from "../git/integration.js";
|
|
|
2
2
|
import { captureTender, dirtyTenderDelta, dirtyTenderRefusal, } from "../git/tender.js";
|
|
3
3
|
import { observeContractsForAdmissionAt } from "../git/observe.js";
|
|
4
4
|
import { unmergedWorkspacePaths, worktreePath } from "../git/workspace.js";
|
|
5
|
-
import {
|
|
5
|
+
import { contractStateWitness, privateStateSeatAttempt, sameWorktreeWitness, STALE_PRIVATE_STATE_PREPARATION, } from "./run.js";
|
|
6
6
|
import { dependencyKeySet } from "../core/subject.js";
|
|
7
7
|
import { contractState } from "../core/facts/observation.js";
|
|
8
8
|
import { gate } from "../core/facts/types.js";
|
|
@@ -58,14 +58,14 @@ export async function prepareReview(repository, stage) {
|
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
-
function preparedReviewCapture(input, state,
|
|
61
|
+
function preparedReviewCapture(input, state, changeId) {
|
|
62
62
|
return {
|
|
63
63
|
kind: "prepared",
|
|
64
64
|
data: {
|
|
65
65
|
gate: REVIEWED,
|
|
66
66
|
subject: dependencyKeySet([
|
|
67
67
|
{ kind: "document", value: state.terms.document.key },
|
|
68
|
-
{ kind: "change", value:
|
|
68
|
+
{ kind: "change", value: changeId },
|
|
69
69
|
]),
|
|
70
70
|
verdict: input.verdict,
|
|
71
71
|
...(input.summary === undefined ? {} : { summary: input.summary }),
|
|
@@ -88,32 +88,56 @@ async function recaptureReviewWorktree(input, state) {
|
|
|
88
88
|
});
|
|
89
89
|
return prepared.kind === "prepared" ? reviewWorktreeInput(prepared) : undefined;
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
/** Capture the reviewable worktree from one non-authoritative private-state read. */
|
|
92
|
+
async function prepareExternalReview(input) {
|
|
92
93
|
const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
|
|
93
94
|
const state = contractState(observation.decision, input.contractId);
|
|
94
95
|
if (state === null)
|
|
95
|
-
return {
|
|
96
|
+
return { kind: "unavailable" };
|
|
96
97
|
const prepared = await captureReviewableWorktree(input.scope, {
|
|
97
98
|
contractId: state.id,
|
|
98
99
|
coordinates: state.coordinates,
|
|
99
100
|
});
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
return {
|
|
102
|
+
kind: "artifacts",
|
|
103
|
+
witness: contractStateWitness(state),
|
|
104
|
+
artifacts: prepared.kind === "refused"
|
|
105
|
+
? { kind: "refused", refusal: prepared.refusal }
|
|
106
|
+
: {
|
|
107
|
+
kind: "captured",
|
|
108
|
+
...(prepared.data.workspace === undefined ? {} : { workspace: prepared.data.workspace }),
|
|
109
|
+
worktree: reviewWorktreeInput(prepared),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Assemble the in-custody attestation input from the fresh Contract and validated artifacts. */
|
|
114
|
+
async function assembleReviewAttempt(input, observation, external) {
|
|
115
|
+
const state = contractState(observation.decision, input.contractId);
|
|
116
|
+
if (external.kind === "unavailable")
|
|
117
|
+
return state === null ? { kind: "assembled" } : { kind: "stale" };
|
|
118
|
+
if (state === null)
|
|
119
|
+
return { kind: "stale" };
|
|
120
|
+
if (external.witness.head !== state.head)
|
|
121
|
+
return { kind: "stale" };
|
|
122
|
+
if (external.artifacts.kind === "refused") {
|
|
123
|
+
return { kind: "assembled", preparation: { kind: "refused", refusal: external.artifacts.refusal } };
|
|
124
|
+
}
|
|
125
|
+
if (!sameWorktreeWitness(external.artifacts.worktree, await recaptureReviewWorktree(input, state))) {
|
|
126
|
+
return { kind: "stale" };
|
|
102
127
|
}
|
|
103
128
|
return {
|
|
104
|
-
|
|
105
|
-
preparation: preparedReviewCapture(input, state,
|
|
106
|
-
...(
|
|
107
|
-
worktree: reviewWorktreeInput(prepared),
|
|
129
|
+
kind: "assembled",
|
|
130
|
+
preparation: preparedReviewCapture(input, state, external.artifacts.worktree.changeId),
|
|
131
|
+
...(external.artifacts.workspace === undefined ? {} : { workspace: external.artifacts.workspace }),
|
|
108
132
|
};
|
|
109
133
|
}
|
|
110
|
-
async function decideAndAdmitReview(input, attempt, seat, observation,
|
|
134
|
+
async function decideAndAdmitReview(input, attempt, seat, observation, assembled) {
|
|
111
135
|
const decision = decideAttestation({
|
|
112
136
|
input: {
|
|
113
137
|
contractId: input.contractId,
|
|
114
138
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
115
139
|
at: timestamp(),
|
|
116
|
-
...(
|
|
140
|
+
...(assembled.preparation === undefined ? {} : { preparation: assembled.preparation }),
|
|
117
141
|
},
|
|
118
142
|
attempt,
|
|
119
143
|
observation: observation.decision,
|
|
@@ -128,33 +152,30 @@ async function decideAndAdmitReview(input, attempt, seat, observation, speculate
|
|
|
128
152
|
attempt,
|
|
129
153
|
offer: decision.offer,
|
|
130
154
|
primaryContract: input.contractId,
|
|
131
|
-
...(input.progress === undefined ? {} :
|
|
155
|
+
...(input.progress === undefined ? {} : { progress: input.progress }),
|
|
132
156
|
});
|
|
133
157
|
if (admission.kind !== "accepted")
|
|
134
158
|
return admission;
|
|
135
159
|
return {
|
|
136
160
|
...admission,
|
|
137
161
|
value: {
|
|
138
|
-
...(
|
|
162
|
+
...(assembled.workspace === undefined ? {} : { workspace: assembled.workspace }),
|
|
139
163
|
},
|
|
140
164
|
};
|
|
141
165
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const state = contractState(fresh.decision, input.contractId);
|
|
147
|
-
if (state === null)
|
|
148
|
-
return false;
|
|
149
|
-
return sameSpeculativeWorktreeInput(speculated.worktree, await recaptureReviewWorktree(input, state));
|
|
150
|
-
});
|
|
151
|
-
return "kind" in observation
|
|
152
|
-
? observation
|
|
153
|
-
: await decideAndAdmitReview(input, attempt, seat, observation, speculated);
|
|
154
|
-
}
|
|
166
|
+
/**
|
|
167
|
+
* One review attempt: the repeatable worktree capture happens outside custody, then a single
|
|
168
|
+
* in-custody observation, witness validation, subject assembly, decision, and publication.
|
|
169
|
+
*/
|
|
155
170
|
async function reviewAttempt(input, attempt) {
|
|
156
|
-
const
|
|
157
|
-
return await privateStateSeatAttempt(input.scope, async (seat) =>
|
|
171
|
+
const external = await prepareExternalReview(input);
|
|
172
|
+
return await privateStateSeatAttempt(input.scope, async (seat) => {
|
|
173
|
+
const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
|
|
174
|
+
const assembled = await assembleReviewAttempt(input, observation, external);
|
|
175
|
+
if (assembled.kind === "stale")
|
|
176
|
+
return STALE_PRIVATE_STATE_PREPARATION;
|
|
177
|
+
return await decideAndAdmitReview(input, attempt, seat, observation, assembled);
|
|
178
|
+
}, attemptDecisionWithSeatClose);
|
|
158
179
|
}
|
|
159
180
|
export async function admitReviewOperation(input) {
|
|
160
181
|
const attempts = mintAttempts({ entryCount: 1 });
|
|
@@ -167,6 +188,8 @@ export async function admitReviewOperation(input) {
|
|
|
167
188
|
}
|
|
168
189
|
if (result.kind === "publication-failed")
|
|
169
190
|
return { kind: "retry", reason: result };
|
|
191
|
+
if (result.kind === "stale" || result.kind === "redecide")
|
|
192
|
+
continue;
|
|
170
193
|
if (result.kind === "collision" && index + 1 === attempts.length)
|
|
171
194
|
return { kind: "retry", reason: result };
|
|
172
195
|
}
|
|
@@ -3,15 +3,14 @@ import { type GitDecisionObservation } from "../git/observe.js";
|
|
|
3
3
|
import { type PrivateStatePublicationSeat, type PrivateStateSeatOutcome } from "../git/private-state-seat.js";
|
|
4
4
|
import type { GitDecodeChannel, GitTreeSelection } from "../git/read-observation.js";
|
|
5
5
|
import type { GitRepository } from "../git/process.js";
|
|
6
|
-
import { type
|
|
6
|
+
import { type GitRefAssertion } from "../git/repository.js";
|
|
7
7
|
import type { AttemptContext, DecideInput, OfferDecision } from "../core/decide.js";
|
|
8
8
|
import type { Offer, TreeUpdate } from "../core/facts/offer.js";
|
|
9
|
-
import {
|
|
10
|
-
import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
|
|
9
|
+
import type { ChangeId, ContractHead, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
11
10
|
import type { GitObjectId } from "../git/identity.js";
|
|
11
|
+
import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
|
|
12
12
|
export declare const STALE_PRIVATE_STATE_PREPARATION: {
|
|
13
|
-
readonly kind: "
|
|
14
|
-
readonly diagnostic: "stale private-state preparation";
|
|
13
|
+
readonly kind: "stale";
|
|
15
14
|
};
|
|
16
15
|
export type ProtocolTerminal = Readonly<{
|
|
17
16
|
kind: "exhausted";
|
|
@@ -26,11 +25,40 @@ export type CompanionDecorator = (input: Readonly<{
|
|
|
26
25
|
contractId: ContractId;
|
|
27
26
|
offer: Offer;
|
|
28
27
|
}>) => readonly TreeUpdate[] | Promise<readonly TreeUpdate[]>;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Seat-external preparation: immutable artifacts plus the currentness witnesses they carry.
|
|
30
|
+
* It never observes private state as authority and never forms an offer.
|
|
31
|
+
*/
|
|
32
|
+
export type ExternalProtocolPreparation<Prepared, Refusal> = Readonly<{
|
|
33
|
+
kind: "prepared";
|
|
34
|
+
prepared: Prepared;
|
|
35
|
+
assertions?: readonly GitRefAssertion[];
|
|
36
|
+
}> | Readonly<{
|
|
37
|
+
kind: "refused";
|
|
38
|
+
refusal: Refusal;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* In-custody assembly of one decision input from the fresh observation and an external artifact.
|
|
42
|
+
* `stale` means only that the outside preparation is spent: discard it and restart the cycle.
|
|
43
|
+
*/
|
|
44
|
+
export type InCustodyProtocolPreparation<Input, Refusal> = Readonly<{
|
|
45
|
+
kind: "prepared";
|
|
46
|
+
input: Input;
|
|
47
|
+
assertions?: readonly GitRefAssertion[];
|
|
48
|
+
}> | Readonly<{
|
|
49
|
+
kind: "stale";
|
|
50
|
+
}> | Readonly<{
|
|
51
|
+
kind: "refused";
|
|
52
|
+
refusal: Refusal;
|
|
53
|
+
}>;
|
|
54
|
+
/** The one split-preparation mechanism: repeatable work outside custody, assembly inside it. */
|
|
55
|
+
export type ProtocolPreparation<Input, Refusal, Seed, Prepared> = Readonly<{
|
|
56
|
+
external: (seed: Seed) => ExternalProtocolPreparation<Prepared, Refusal> | Promise<ExternalProtocolPreparation<Prepared, Refusal>>;
|
|
57
|
+
assemble: (observation: GitDecisionObservation, seed: Seed, prepared: Prepared) => InCustodyProtocolPreparation<Input, Refusal> | Promise<InCustodyProtocolPreparation<Input, Refusal>>;
|
|
58
|
+
}>;
|
|
59
|
+
type ProtocolAdmission<Input extends Readonly<{
|
|
32
60
|
contractId: ContractId;
|
|
33
|
-
}
|
|
61
|
+
}>, Refusal> = Readonly<{
|
|
34
62
|
channel: GitDecodeChannel;
|
|
35
63
|
repository: GitRepository;
|
|
36
64
|
progress?: ExecutionProgress;
|
|
@@ -45,28 +73,31 @@ export type RunProtocolInput<Input extends Readonly<{
|
|
|
45
73
|
decorateOffer?: CompanionDecorator;
|
|
46
74
|
validateAdmission?: (observation: GitDecisionObservation) => Refusal | undefined | Promise<Refusal | undefined>;
|
|
47
75
|
observationSelection?: GitTreeSelection;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
}>;
|
|
77
|
+
/** An intent whose invocation is already its decision input. */
|
|
78
|
+
export type DirectRunProtocolInput<Seed extends Readonly<{
|
|
79
|
+
contractId: ContractId;
|
|
80
|
+
}>, Refusal> = ProtocolAdmission<Seed, Refusal> & Readonly<{
|
|
81
|
+
input: Seed;
|
|
82
|
+
preparation?: undefined;
|
|
83
|
+
}>;
|
|
84
|
+
/** An intent whose repeatable preparation runs outside custody and is assembled inside it. */
|
|
85
|
+
export type SplitRunProtocolInput<Input extends Readonly<{
|
|
86
|
+
contractId: ContractId;
|
|
87
|
+
}>, Refusal, Seed extends Readonly<{
|
|
88
|
+
contractId: ContractId;
|
|
89
|
+
}>, Prepared> = ProtocolAdmission<Input, Refusal> & Readonly<{
|
|
52
90
|
input: Seed;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}> | Promise<Readonly<{
|
|
61
|
-
kind: "prepared";
|
|
62
|
-
input: Input;
|
|
63
|
-
assertions?: readonly GitRefAssertion[];
|
|
64
|
-
}> | Readonly<{
|
|
65
|
-
kind: "refused";
|
|
66
|
-
refusal: Refusal;
|
|
67
|
-
}>>;
|
|
68
|
-
}>);
|
|
91
|
+
preparation: ProtocolPreparation<Input, Refusal, Seed, Prepared>;
|
|
92
|
+
}>;
|
|
93
|
+
export type RunProtocolInput<Input extends Readonly<{
|
|
94
|
+
contractId: ContractId;
|
|
95
|
+
}>, Refusal, Seed extends Readonly<{
|
|
96
|
+
contractId: ContractId;
|
|
97
|
+
}> = Input, Prepared = Seed> = DirectRunProtocolInput<Seed, Refusal> | SplitRunProtocolInput<Input, Refusal, Seed, Prepared>;
|
|
69
98
|
export type PreparedProtocolAttempt<Refusal> = Readonly<{
|
|
99
|
+
kind: "stale";
|
|
100
|
+
}> | Readonly<{
|
|
70
101
|
kind: "refused";
|
|
71
102
|
refusal: Refusal;
|
|
72
103
|
}> | Readonly<{
|
|
@@ -76,29 +107,45 @@ export type PreparedProtocolAttempt<Refusal> = Readonly<{
|
|
|
76
107
|
offer: Offer;
|
|
77
108
|
assertions: readonly GitRefAssertion[];
|
|
78
109
|
}>;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
110
|
+
/**
|
|
111
|
+
* The private-state witness a seat-external preparation was computed against. Equal journal heads
|
|
112
|
+
* mean the same decoded contract state, so the same document, coordinates, and facts.
|
|
113
|
+
*/
|
|
114
|
+
export type ContractStateWitness = Readonly<{
|
|
115
|
+
head: ContractHead | null;
|
|
116
|
+
}>;
|
|
117
|
+
export declare function contractStateWitness(state: ContractStateWitness): ContractStateWitness;
|
|
118
|
+
/**
|
|
119
|
+
* A physical worktree capture carried across custody as a witness, never as authority.
|
|
120
|
+
*
|
|
121
|
+
* Unlike a ref or a journal head, uncommitted worktree bytes are covered by no atomic
|
|
122
|
+
* compare-and-swap, so the only sound proof that a seat-external capture still describes the
|
|
123
|
+
* bytes about to be published is a fresh capture compared against this witness under custody.
|
|
124
|
+
* A cheaper structural signature (head, merge head, dirty flag, changed paths) cannot judge it:
|
|
125
|
+
* a writer that rewrites an already-changed path - a conflict materialization, for instance -
|
|
126
|
+
* leaves every path-level signature identical while the bytes that would be published differ.
|
|
127
|
+
*/
|
|
128
|
+
export type WorktreeWitness = Readonly<{
|
|
82
129
|
tree: GitObjectId;
|
|
83
130
|
head: SnapshotId;
|
|
84
131
|
mergeHead?: SnapshotId;
|
|
85
132
|
dirty: boolean;
|
|
86
133
|
changeId: ChangeId;
|
|
87
134
|
}>;
|
|
88
|
-
export declare function
|
|
135
|
+
export declare function sameWorktreeWitness(prepared: WorktreeWitness | undefined, fresh: WorktreeWitness | undefined): boolean;
|
|
89
136
|
export declare function privateStateAssertionsMatch(repository: GitRepository, assertions: readonly GitRefAssertion[]): Promise<boolean>;
|
|
90
137
|
export declare function publicationFailedFromSeatError(error: unknown): AttemptTerminal;
|
|
91
|
-
export declare function matchingPrivateRootObservation(repository: GitRepository, channel: GitDecodeChannel, contractId: ContractId, prepared: GitDecisionObservation, revalidate?: (fresh: GitDecisionObservation) => boolean | Promise<boolean>): Promise<GitDecisionObservation | typeof STALE_PRIVATE_STATE_PREPARATION>;
|
|
92
138
|
export declare function privateStateSeatAttempt<T>(repository: GitRepository, action: (seat: PrivateStatePublicationSeat) => Promise<T>, wrap: (outcome: PrivateStateSeatOutcome<T>) => T): Promise<T | AttemptTerminal>;
|
|
93
139
|
/** Form one decided offer without admitting or reinterpreting it. */
|
|
94
140
|
export declare function prepareProtocolAttempt<Input extends Readonly<{
|
|
95
141
|
contractId: ContractId;
|
|
96
142
|
}>, Refusal, Seed extends Readonly<{
|
|
97
143
|
contractId: ContractId;
|
|
98
|
-
}> = Input>(input: RunProtocolInput<Input, Refusal, Seed>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
|
|
144
|
+
}> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
|
|
99
145
|
/** Run bounded, verb-neutral attempts and return one real admission on acceptance. */
|
|
100
146
|
export declare function runProtocol<Input extends Readonly<{
|
|
101
147
|
contractId: ContractId;
|
|
102
148
|
}>, Refusal, Seed extends Readonly<{
|
|
103
149
|
contractId: ContractId;
|
|
104
|
-
}> = Input>(input: RunProtocolInput<Input, Refusal, Seed>): Promise<ProtocolResult<Refusal>>;
|
|
150
|
+
}> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>): Promise<ProtocolResult<Refusal>>;
|
|
151
|
+
export {};
|
|
@@ -3,16 +3,12 @@ import { appendPrivateStateSeatClose, isPrivateStateSeatContention, mergePrivate
|
|
|
3
3
|
import { GIT_REF, readRefs } from "../git/repository.js";
|
|
4
4
|
import { admitDecidedOffer } from "./attempt.js";
|
|
5
5
|
export const STALE_PRIVATE_STATE_PREPARATION = {
|
|
6
|
-
kind: "
|
|
7
|
-
diagnostic: "stale private-state preparation",
|
|
6
|
+
kind: "stale",
|
|
8
7
|
};
|
|
9
|
-
export function
|
|
10
|
-
return
|
|
8
|
+
export function contractStateWitness(state) {
|
|
9
|
+
return { head: state.head };
|
|
11
10
|
}
|
|
12
|
-
export function
|
|
13
|
-
return privateRootCommit(prepared) === privateRootCommit(fresh);
|
|
14
|
-
}
|
|
15
|
-
export function sameSpeculativeWorktreeInput(prepared, fresh) {
|
|
11
|
+
export function sameWorktreeWitness(prepared, fresh) {
|
|
16
12
|
if (prepared === undefined || fresh === undefined)
|
|
17
13
|
return prepared === fresh;
|
|
18
14
|
return (prepared.tree === fresh.tree &&
|
|
@@ -32,14 +28,6 @@ export function publicationFailedFromSeatError(error) {
|
|
|
32
28
|
return { kind: "publication-failed", diagnostic: error.message };
|
|
33
29
|
throw error;
|
|
34
30
|
}
|
|
35
|
-
export async function matchingPrivateRootObservation(repository, channel, contractId, prepared, revalidate) {
|
|
36
|
-
const fresh = await observeContractsForAdmissionAt(repository, channel, [contractId]);
|
|
37
|
-
if (!samePrivateRootObservation(prepared, fresh))
|
|
38
|
-
return STALE_PRIVATE_STATE_PREPARATION;
|
|
39
|
-
if (revalidate !== undefined && !(await revalidate(fresh)))
|
|
40
|
-
return STALE_PRIVATE_STATE_PREPARATION;
|
|
41
|
-
return fresh;
|
|
42
|
-
}
|
|
43
31
|
export async function privateStateSeatAttempt(repository, action, wrap) {
|
|
44
32
|
try {
|
|
45
33
|
return wrap(await withPrivateStatePublicationSeat(repository, action));
|
|
@@ -53,30 +41,23 @@ async function observeProtocolDecision(input) {
|
|
|
53
41
|
? await observeContractsForAdmissionAt(input.repository, input.channel, input.contracts, input.observationSelection)
|
|
54
42
|
: await input.observe(input.repository, input.channel, input.contracts);
|
|
55
43
|
}
|
|
56
|
-
async function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
async function decideDecoratedProtocolOffer(input, baseAttempt, decisionObservation, preparedInput) {
|
|
64
|
-
const attempt = input.extendAttempt === undefined
|
|
65
|
-
? baseAttempt
|
|
66
|
-
: input.extendAttempt(baseAttempt, decisionObservation.decision.size);
|
|
67
|
-
const decision = input.decide({
|
|
68
|
-
input: preparedInput.input,
|
|
44
|
+
async function decideDecoratedProtocolOffer(admission, primaryContract, decisionObservation, prepared) {
|
|
45
|
+
const attempt = admission.extendAttempt === undefined
|
|
46
|
+
? prepared.attempt
|
|
47
|
+
: admission.extendAttempt(prepared.attempt, decisionObservation.decision.size);
|
|
48
|
+
const decision = admission.decide({
|
|
49
|
+
input: prepared.input,
|
|
69
50
|
attempt,
|
|
70
51
|
observation: decisionObservation.decision,
|
|
71
52
|
});
|
|
72
53
|
if (decision.kind === "refused")
|
|
73
54
|
return decision;
|
|
74
|
-
const companions =
|
|
55
|
+
const companions = admission.decorateOffer === undefined
|
|
75
56
|
? []
|
|
76
|
-
: await
|
|
77
|
-
repository:
|
|
57
|
+
: await admission.decorateOffer({
|
|
58
|
+
repository: admission.repository,
|
|
78
59
|
observation: decisionObservation,
|
|
79
|
-
contractId:
|
|
60
|
+
contractId: primaryContract,
|
|
80
61
|
offer: decision.offer,
|
|
81
62
|
});
|
|
82
63
|
const offer = companions.length === 0
|
|
@@ -84,22 +65,31 @@ async function decideDecoratedProtocolOffer(input, baseAttempt, decisionObservat
|
|
|
84
65
|
: { ...decision.offer, companions: [...(decision.offer.companions ?? []), ...companions] };
|
|
85
66
|
const admissionObservation = companions.length === 0
|
|
86
67
|
? decisionObservation
|
|
87
|
-
: await extendAdmissionPathsAt(
|
|
68
|
+
: await extendAdmissionPathsAt(admission.channel, decisionObservation, companions.map(({ path }) => path));
|
|
88
69
|
return {
|
|
89
70
|
kind: "offered",
|
|
90
71
|
observation: admissionObservation,
|
|
91
72
|
attempt,
|
|
92
73
|
offer,
|
|
93
|
-
assertions:
|
|
74
|
+
assertions: prepared.assertions,
|
|
94
75
|
};
|
|
95
76
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
77
|
+
async function admitDecidedProtocolOffer(admission, primaryContract, seat, decisionObservation, prepared) {
|
|
78
|
+
const decided = await decideDecoratedProtocolOffer(admission, primaryContract, decisionObservation, prepared);
|
|
79
|
+
if (decided.kind === "refused")
|
|
80
|
+
return decided;
|
|
81
|
+
return await admitDecidedOffer({
|
|
82
|
+
channel: admission.channel,
|
|
83
|
+
repository: admission.repository,
|
|
84
|
+
seat,
|
|
85
|
+
decisionObservation: decided.observation,
|
|
86
|
+
attempt: decided.attempt,
|
|
87
|
+
offer: decided.offer,
|
|
88
|
+
primaryContract,
|
|
89
|
+
...(admission.progress === undefined ? {} : { progress: admission.progress }),
|
|
90
|
+
assertions: decided.assertions,
|
|
91
|
+
...(admission.validateAdmission === undefined ? {} : { validateAdmission: admission.validateAdmission }),
|
|
92
|
+
});
|
|
103
93
|
}
|
|
104
94
|
function protocolAttemptWithSeatClose(outcome) {
|
|
105
95
|
return mergePrivateStateSeatClose(outcome, (value, closeLag) => {
|
|
@@ -108,47 +98,72 @@ function protocolAttemptWithSeatClose(outcome) {
|
|
|
108
98
|
return { ...value, seatClose: appendPrivateStateSeatClose(value.seatClose, closeLag) };
|
|
109
99
|
});
|
|
110
100
|
}
|
|
111
|
-
async function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
101
|
+
async function runDirectProtocolAttempt(input, attempt) {
|
|
102
|
+
return await privateStateSeatAttempt(input.repository, async (seat) => {
|
|
103
|
+
const observation = await observeProtocolDecision(input);
|
|
104
|
+
return await admitDecidedProtocolOffer(input, input.input.contractId, seat, observation, {
|
|
105
|
+
attempt,
|
|
106
|
+
input: input.input,
|
|
107
|
+
assertions: [],
|
|
108
|
+
});
|
|
109
|
+
}, protocolAttemptWithSeatClose);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One full attempt: repeatable preparation outside custody, then a single in-custody cycle of
|
|
113
|
+
* observation, witness validation, assembly, decision, companions, and atomic publication.
|
|
114
|
+
*/
|
|
115
|
+
async function runSplitProtocolAttempt(input, attempt) {
|
|
116
|
+
const external = await input.preparation.external(input.input);
|
|
117
|
+
if (external.kind === "refused")
|
|
118
|
+
return external;
|
|
119
|
+
return await privateStateSeatAttempt(input.repository, async (seat) => {
|
|
120
|
+
const observation = await observeProtocolDecision(input);
|
|
121
|
+
const assembled = await input.preparation.assemble(observation, input.input, external.prepared);
|
|
122
|
+
if (assembled.kind !== "prepared")
|
|
123
|
+
return assembled;
|
|
124
|
+
const assertions = [...(external.assertions ?? []), ...(assembled.assertions ?? [])];
|
|
125
|
+
if (!(await privateStateAssertionsMatch(input.repository, assertions)))
|
|
126
|
+
return STALE_PRIVATE_STATE_PREPARATION;
|
|
127
|
+
return await admitDecidedProtocolOffer(input, input.input.contractId, seat, observation, {
|
|
128
|
+
attempt,
|
|
129
|
+
input: assembled.input,
|
|
130
|
+
assertions,
|
|
131
|
+
});
|
|
132
|
+
}, protocolAttemptWithSeatClose);
|
|
115
133
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
repository: input.repository,
|
|
132
|
-
seat,
|
|
133
|
-
decisionObservation: decided.observation,
|
|
134
|
-
attempt: decided.attempt,
|
|
135
|
-
offer: decided.offer,
|
|
136
|
-
primaryContract: input.input.contractId,
|
|
137
|
-
...(input.progress === undefined ? {} : input.progress === undefined ? {} : { progress: input.progress }),
|
|
138
|
-
assertions: decided.assertions,
|
|
139
|
-
...(input.validateAdmission === undefined ? {} : { validateAdmission: input.validateAdmission }),
|
|
134
|
+
/** Form one decided offer without admitting or reinterpreting it. */
|
|
135
|
+
export async function prepareProtocolAttempt(input, baseAttempt) {
|
|
136
|
+
if (input.preparation === undefined)
|
|
137
|
+
return await prepareDirectProtocolAttempt(input, baseAttempt);
|
|
138
|
+
const external = await input.preparation.external(input.input);
|
|
139
|
+
if (external.kind === "refused")
|
|
140
|
+
return external;
|
|
141
|
+
const decisionObservation = await observeProtocolDecision(input);
|
|
142
|
+
const assembled = await input.preparation.assemble(decisionObservation, input.input, external.prepared);
|
|
143
|
+
if (assembled.kind !== "prepared")
|
|
144
|
+
return assembled;
|
|
145
|
+
return await decideDecoratedProtocolOffer(input, input.input.contractId, decisionObservation, {
|
|
146
|
+
attempt: baseAttempt,
|
|
147
|
+
input: assembled.input,
|
|
148
|
+
assertions: [...(external.assertions ?? []), ...(assembled.assertions ?? [])],
|
|
140
149
|
});
|
|
141
150
|
}
|
|
142
|
-
async function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
async function prepareDirectProtocolAttempt(input, baseAttempt) {
|
|
152
|
+
const decisionObservation = await observeProtocolDecision(input);
|
|
153
|
+
return await decideDecoratedProtocolOffer(input, input.input.contractId, decisionObservation, {
|
|
154
|
+
attempt: baseAttempt,
|
|
155
|
+
input: input.input,
|
|
156
|
+
assertions: [],
|
|
157
|
+
});
|
|
146
158
|
}
|
|
147
159
|
/** Run bounded, verb-neutral attempts and return one real admission on acceptance. */
|
|
148
160
|
export async function runProtocol(input) {
|
|
149
161
|
const attempts = input.attempts;
|
|
150
162
|
for (let index = 0; index < attempts.length; index += 1) {
|
|
151
|
-
const
|
|
163
|
+
const attempt = attempts[index];
|
|
164
|
+
const result = input.preparation === undefined
|
|
165
|
+
? await runDirectProtocolAttempt(input, attempt)
|
|
166
|
+
: await runSplitProtocolAttempt(input, attempt);
|
|
152
167
|
if (result.kind === "refused" || result.kind === "accepted" || result.kind === "publication-failed")
|
|
153
168
|
return result;
|
|
154
169
|
if (result.kind === "collision" && index + 1 === attempts.length)
|
|
@@ -8,6 +8,11 @@ type ProcessLaunch = Readonly<{
|
|
|
8
8
|
readonly cwd?: string;
|
|
9
9
|
readonly env?: NodeJS.ProcessEnv;
|
|
10
10
|
readonly signal?: AbortSignal;
|
|
11
|
+
/** @internal Mechanical output observation only; it does not affect process ownership or outcome. */
|
|
12
|
+
readonly onOutput?: (output: Readonly<{
|
|
13
|
+
stream: "stdout" | "stderr";
|
|
14
|
+
text: string;
|
|
15
|
+
}>) => void;
|
|
11
16
|
}>;
|
|
12
17
|
export type CancellableProcess = Readonly<{
|
|
13
18
|
child: ChildProcess;
|
|
@@ -27,23 +32,30 @@ type ProcessTerminal = Readonly<{
|
|
|
27
32
|
readonly stderr: string;
|
|
28
33
|
readonly truncated: boolean;
|
|
29
34
|
}>;
|
|
35
|
+
type ProcessCapture = Readonly<{
|
|
36
|
+
readonly stdout: string;
|
|
37
|
+
readonly stderr: string;
|
|
38
|
+
readonly truncated: boolean;
|
|
39
|
+
}>;
|
|
30
40
|
type ProcessTimeout = Readonly<{
|
|
31
41
|
readonly kind: "timeout";
|
|
32
|
-
}
|
|
42
|
+
}> & ProcessCapture;
|
|
33
43
|
type ProcessSpawnError = Readonly<{
|
|
34
44
|
readonly kind: "spawn-error";
|
|
35
45
|
readonly diagnostic: string;
|
|
36
|
-
}
|
|
46
|
+
}> & ProcessCapture;
|
|
37
47
|
type ProcessUnknownExit = Readonly<{
|
|
38
48
|
readonly kind: "unknown-exit";
|
|
39
|
-
}
|
|
49
|
+
}> & ProcessCapture;
|
|
40
50
|
type ProcessCancelled = Readonly<{
|
|
41
51
|
readonly kind: "cancelled";
|
|
42
|
-
}
|
|
52
|
+
}> & ProcessCapture;
|
|
43
53
|
export type ProcessOutcome = ProcessTerminal | ProcessTimeout | ProcessSpawnError | ProcessUnknownExit | ProcessCancelled;
|
|
44
54
|
type ProcessStreamError = Readonly<{
|
|
45
55
|
readonly kind: "stream-error";
|
|
46
56
|
readonly diagnostic: string;
|
|
57
|
+
readonly stderr: string;
|
|
58
|
+
readonly truncated: boolean;
|
|
47
59
|
}>;
|
|
48
60
|
export type ProcessConsumption = Readonly<{
|
|
49
61
|
readonly outcome: ProcessOutcome | ProcessStreamError;
|