@astrosheep/keiyaku 4.5.20 → 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 +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -8
- 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 -3
- 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 +22 -0
- package/build/src/akuma/akuma-product.d.ts +1 -0
- package/build/src/akuma/akuma-product.js +3 -0
- package/build/src/akuma/akuma.d.ts +1 -0
- package/build/src/akuma/body.d.ts +25 -5
- package/build/src/akuma/body.js +65 -8
- package/build/src/akuma/call-request.d.ts +2 -0
- package/build/src/akuma/call-request.js +2 -0
- package/build/src/akuma/fleet-execution.d.ts +28 -0
- package/build/src/akuma/fleet-execution.js +36 -4
- package/build/src/akuma/fleet-request.d.ts +10 -0
- package/build/src/akuma/fleet-request.js +11 -2
- 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.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/claude/index.js +9 -12
- package/build/src/akuma/providers/codex-app-server/index.js +4 -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/opencode-sdk/index.js +30 -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 +3 -2
- package/build/src/akuma/request-rendezvous.js +8 -3
- 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 +2 -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 +2 -0
- package/build/src/cli/commands/akuma-invoke.js +133 -13
- package/build/src/cli/commands/akuma.js +11 -4
- package/build/src/cli/commands/contract-help.js +17 -4
- package/build/src/cli/commands/contract.js +6 -4
- package/build/src/cli/commands/status-set.js +15 -24
- package/build/src/cli/main.js +5 -0
- package/build/src/cli/parse.d.ts +2 -0
- package/build/src/cli/parse.js +5 -0
- package/build/src/cli/render/akuma-activity.d.ts +99 -2
- package/build/src/cli/render/akuma-activity.js +484 -40
- package/build/src/cli/render/akuma.d.ts +10 -0
- package/build/src/cli/render/akuma.js +28 -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 +23 -1
- package/build/src/cli/render/execution-progress.js +151 -28
- 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 +5 -5
- 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 +13 -7
- 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/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 +33 -17
- package/build/src/library/bind.js +18 -16
- package/build/src/library/contract-bind.d.ts +3 -1
- package/build/src/library/contract-bind.js +5 -3
- package/build/src/library/contract-operations.d.ts +2 -2
- package/build/src/library/contract-operations.js +1 -1
- package/build/src/library/contract-types.d.ts +1 -0
- package/build/src/library/fleet.d.ts +4 -1
- package/build/src/library/fleet.js +32 -8
- package/build/src/plugin/akuma-signals.d.ts +6 -0
- package/build/src/plugin/akuma-signals.js +8 -0
- package/build/src/plugin/public.d.ts +4 -0
- package/build/src/plugin/runtime.js +4 -1
- 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 +7 -1
- package/build/src/protocol/deliver.js +165 -92
- package/build/src/protocol/execution-observation.d.ts +0 -4
- package/build/src/protocol/intent.d.ts +15 -20
- package/build/src/protocol/intent.js +29 -17
- package/build/src/protocol/operations.d.ts +2 -0
- package/build/src/protocol/placement.js +5 -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 +9 -7
- 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/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 +0 -1
- package/build/src/verification/execution.js +0 -24
- package/build/src/verification/observation.d.ts +0 -4
- package/build/src/verification/observation.js +1 -2
- package/package.json +3 -1
- package/build/src/git/verification-environment.d.ts +0 -10
- package/build/src/git/verification-environment.js +0 -281
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @architectureCompositionRoot */
|
|
2
2
|
import { type ActivityHistory, type OutcomeRow, type AkumaStatus, type InterruptReceipt } from "../akuma/akuma.js";
|
|
3
3
|
import { type ExecutionContext } from "../akuma/requests.js";
|
|
4
|
+
import type { WaitObserver } from "../akuma/fleet-execution.js";
|
|
4
5
|
import { type DispatchAssociation } from "../dispatch/index.js";
|
|
5
6
|
import { type AkumaAddressInput, type AkumaSetAddressInput } from "./address.js";
|
|
6
7
|
import { type AkumaKillResult, type AkumaObservation, type AkumaObservationStage, type AkumaTellResult, type AkumaWaitResult } from "../akuma/fleet-observation.js";
|
|
@@ -10,6 +11,7 @@ export type AkumaWaitInput = AkumaSetAddressInput & Readonly<{
|
|
|
10
11
|
}>;
|
|
11
12
|
export type AkumaTellInput = AkumaAddressInput & Readonly<{
|
|
12
13
|
body: string;
|
|
14
|
+
initiator?: string;
|
|
13
15
|
}>;
|
|
14
16
|
export type { TellResult, TellWake } from "../akuma/akuma.js";
|
|
15
17
|
export type { CreatedTaskObservation } from "../task/created-observation.js";
|
|
@@ -17,6 +19,7 @@ export type { DispatchAssociation } from "../dispatch/association.js";
|
|
|
17
19
|
export type { AkumaKillResult, AkumaObservation, AkumaObservationStage, AkumaTellResult, AkumaUnobserved, AkumaWaitResult, } from "../akuma/fleet-observation.js";
|
|
18
20
|
export type AkumaInterruptInput = AkumaAddressInput & Readonly<{
|
|
19
21
|
body: string;
|
|
22
|
+
initiator?: string;
|
|
20
23
|
}>;
|
|
21
24
|
export type AkumaInterruptResult = Readonly<{
|
|
22
25
|
id: AkumaStatus["id"];
|
|
@@ -56,7 +59,7 @@ export type AkumaHistoryResult = Readonly<{
|
|
|
56
59
|
contract: DispatchAssociation;
|
|
57
60
|
}>;
|
|
58
61
|
export declare function statusAkuma(input: AkumaAddressInput): Promise<AkumaObservation>;
|
|
59
|
-
export declare function waitAkuma(input: AkumaWaitInput, execution?: ExecutionContext): Promise<AkumaWaitResult>;
|
|
62
|
+
export declare function waitAkuma(input: AkumaWaitInput, execution?: ExecutionContext, observer?: WaitObserver): Promise<AkumaWaitResult>;
|
|
60
63
|
export declare function killAkuma(input: AkumaSetAddressInput, execution?: ExecutionContext): Promise<AkumaKillResult>;
|
|
61
64
|
export declare function tellAkuma(input: AkumaTellInput, execution?: ExecutionContext): Promise<AkumaTellResult>;
|
|
62
65
|
export declare function interruptAkuma(input: AkumaInterruptInput): Promise<AkumaInterruptResult>;
|
|
@@ -4,6 +4,7 @@ import { createAkumaProduct } from "../akuma/akuma-product.js";
|
|
|
4
4
|
import { executionChannel, localExecutionContext } from "../akuma/requests.js";
|
|
5
5
|
import { requestForwardedFleetKill, requestForwardedFleetTell, requestForwardedFleetWait, } from "../akuma/fleet-request.js";
|
|
6
6
|
import { executeKillAkuma, executeTellAkuma, executeWaitAkuma } from "../akuma/fleet-execution.js";
|
|
7
|
+
import { readAliases } from "../alias/index.js";
|
|
7
8
|
import { observeDispatchAssociation } from "../dispatch/index.js";
|
|
8
9
|
import { observeCreatedTaskObservations } from "../task/created-observation.js";
|
|
9
10
|
import { addressAkuma, addressAkumaSet } from "./address.js";
|
|
@@ -23,6 +24,26 @@ async function dispatchAssociation(repo, id) {
|
|
|
23
24
|
async function createdTasksFor(path, statuses) {
|
|
24
25
|
return await observeCreatedTaskObservations(path, statuses.map((status) => status.id));
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* One wait's identity-fact resolver: the alias bound to an Akuma in its world and
|
|
29
|
+
* its Dispatch association. The alias index is read once, on the first observed
|
|
30
|
+
* Akuma, and each Akuma's alias is the first binding the canonical order offers.
|
|
31
|
+
*/
|
|
32
|
+
function waitIdentityFacts(path, repo) {
|
|
33
|
+
let aliases;
|
|
34
|
+
return async (id) => {
|
|
35
|
+
if (aliases === undefined) {
|
|
36
|
+
const bound = new Map();
|
|
37
|
+
for (const binding of await readAliases(path)) {
|
|
38
|
+
if (!bound.has(binding.akuId))
|
|
39
|
+
bound.set(binding.akuId, binding.alias);
|
|
40
|
+
}
|
|
41
|
+
aliases = bound;
|
|
42
|
+
}
|
|
43
|
+
const alias = aliases.get(id);
|
|
44
|
+
return { ...(alias === undefined ? {} : { alias }), contract: await dispatchAssociation(repo, id) };
|
|
45
|
+
};
|
|
46
|
+
}
|
|
26
47
|
async function observeAkuma(status, path, repo) {
|
|
27
48
|
return (await observeAkumaSet([status], path, repo))[0];
|
|
28
49
|
}
|
|
@@ -85,7 +106,7 @@ export async function statusAkuma(input) {
|
|
|
85
106
|
const addressed = await addressAkuma(input);
|
|
86
107
|
return await observeAkuma(await source(addressed.path).selectHandle({ id: addressed.id }).status(), addressed.path, input.repo);
|
|
87
108
|
}
|
|
88
|
-
export async function waitAkuma(input, execution = localExecutionContext()) {
|
|
109
|
+
export async function waitAkuma(input, execution = localExecutionContext(), observer) {
|
|
89
110
|
const values = requireInput(input, "Keiyaku.wait input");
|
|
90
111
|
for (const key of Object.keys(values)) {
|
|
91
112
|
if (!["path", "akuma", "repo", "completion", "timeoutMs"].includes(key)) {
|
|
@@ -94,13 +115,11 @@ export async function waitAkuma(input, execution = localExecutionContext()) {
|
|
|
94
115
|
}
|
|
95
116
|
const addressed = await addressAkumaSet(setAddress(values));
|
|
96
117
|
const completion = values.completion;
|
|
97
|
-
if (addressed.ids.length > 1 && completion !== "any" && completion !== "all") {
|
|
98
|
-
throw new TypeError("completion must be any or all when waiting for multiple Akuma");
|
|
99
|
-
}
|
|
100
118
|
if (completion !== undefined && completion !== "any" && completion !== "all") {
|
|
101
119
|
throw new TypeError("completion must be any or all");
|
|
102
120
|
}
|
|
103
|
-
|
|
121
|
+
// An omitted mode is any: a plural wait returns when any selected Akuma is complete.
|
|
122
|
+
const selected = completion ?? "any";
|
|
104
123
|
const timeoutMs = timeout(values.timeoutMs);
|
|
105
124
|
const channel = executionChannel(execution);
|
|
106
125
|
const repo = values.repo;
|
|
@@ -117,6 +136,9 @@ export async function waitAkuma(input, execution = localExecutionContext()) {
|
|
|
117
136
|
ids: addressed.ids,
|
|
118
137
|
completion: selected,
|
|
119
138
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
139
|
+
identity: waitIdentityFacts(addressed.path, repo),
|
|
140
|
+
...(observer?.selected === undefined ? {} : { onSelected: observer.selected }),
|
|
141
|
+
...(observer?.observe === undefined ? {} : { observe: observer.observe }),
|
|
120
142
|
}));
|
|
121
143
|
}
|
|
122
144
|
export async function killAkuma(input, execution = localExecutionContext()) {
|
|
@@ -136,7 +158,7 @@ export async function killAkuma(input, execution = localExecutionContext()) {
|
|
|
136
158
|
export async function tellAkuma(input, execution = localExecutionContext()) {
|
|
137
159
|
const values = requireInput(input, "Keiyaku.tell input");
|
|
138
160
|
for (const key of Object.keys(values)) {
|
|
139
|
-
if (!["path", "akuma", "body", "repo"].includes(key)) {
|
|
161
|
+
if (!["path", "akuma", "body", "repo", "initiator"].includes(key)) {
|
|
140
162
|
throw new TypeError(`Keiyaku.tell input has unknown field: ${key}`);
|
|
141
163
|
}
|
|
142
164
|
}
|
|
@@ -149,18 +171,20 @@ export async function tellAkuma(input, execution = localExecutionContext()) {
|
|
|
149
171
|
directory: channel.directory,
|
|
150
172
|
target: addressed.id,
|
|
151
173
|
body: values.body,
|
|
174
|
+
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
152
175
|
});
|
|
153
176
|
}
|
|
154
177
|
return await executeTellAkuma({
|
|
155
178
|
path: addressed.path,
|
|
156
179
|
id: addressed.id,
|
|
157
180
|
body: values.body,
|
|
181
|
+
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
158
182
|
});
|
|
159
183
|
}
|
|
160
184
|
export async function interruptAkuma(input) {
|
|
161
185
|
const values = requireInput(input, "Keiyaku.interrupt input");
|
|
162
186
|
for (const key of Object.keys(values)) {
|
|
163
|
-
if (!["path", "akuma", "body", "repo"].includes(key)) {
|
|
187
|
+
if (!["path", "akuma", "body", "repo", "initiator"].includes(key)) {
|
|
164
188
|
throw new TypeError(`Keiyaku.interrupt input has unknown field: ${key}`);
|
|
165
189
|
}
|
|
166
190
|
}
|
|
@@ -168,7 +192,7 @@ export async function interruptAkuma(input) {
|
|
|
168
192
|
throw new TypeError("body must be a string");
|
|
169
193
|
const addressed = await addressAkuma(directAddress(values));
|
|
170
194
|
const handle = source(addressed.path).selectHandle({ id: addressed.id });
|
|
171
|
-
const receipt = await handle.interrupt(values.body);
|
|
195
|
+
const receipt = await handle.interrupt(values.body, input.initiator === undefined ? {} : { initiator: input.initiator });
|
|
172
196
|
const observation = await observeAkumaStage(addressed.path, addressed.id, values.repo);
|
|
173
197
|
return { id: addressed.id, receipt, observation };
|
|
174
198
|
}
|
|
@@ -8,5 +8,11 @@ type CalledPluginSignalInput = Readonly<{
|
|
|
8
8
|
callerAkumaId?: string;
|
|
9
9
|
contractId?: string;
|
|
10
10
|
}>;
|
|
11
|
+
export declare function emitInitiatingPluginSignal(input: Readonly<{
|
|
12
|
+
world: WorldRoot;
|
|
13
|
+
settings?: Settings;
|
|
14
|
+
initiator: string;
|
|
15
|
+
reportDiagnostic: (message: string) => void;
|
|
16
|
+
}>): Promise<void>;
|
|
11
17
|
export declare function emitCalledPluginSignal(input: CalledPluginSignalInput): Promise<void>;
|
|
12
18
|
export {};
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { pluginRuntime } from "./runtime.js";
|
|
2
|
+
export async function emitInitiatingPluginSignal(input) {
|
|
3
|
+
const runtime = await pluginRuntime({
|
|
4
|
+
world: input.world,
|
|
5
|
+
...(input.settings === undefined ? {} : { settings: input.settings }),
|
|
6
|
+
reportDiagnostic: input.reportDiagnostic,
|
|
7
|
+
});
|
|
8
|
+
await runtime.emit({ kind: "akuma.initiating", initiator: input.initiator }, input.reportDiagnostic);
|
|
9
|
+
}
|
|
2
10
|
export async function emitCalledPluginSignal(input) {
|
|
3
11
|
const runtime = await pluginRuntime({
|
|
4
12
|
world: input.world,
|
|
@@ -16,6 +16,9 @@ export type PluginOutcome = Readonly<{
|
|
|
16
16
|
}>;
|
|
17
17
|
export type PluginBodyEnd = "exited" | "broke-off" | "put-down" | "hung";
|
|
18
18
|
export type PluginSignalMap = Readonly<{
|
|
19
|
+
"akuma.initiating": Readonly<{
|
|
20
|
+
initiator: string;
|
|
21
|
+
}>;
|
|
19
22
|
"akuma.called": Readonly<{
|
|
20
23
|
akumaId: string;
|
|
21
24
|
callerAkumaId?: string;
|
|
@@ -24,6 +27,7 @@ export type PluginSignalMap = Readonly<{
|
|
|
24
27
|
"akuma.turn-outcome": Readonly<{
|
|
25
28
|
akumaId: string;
|
|
26
29
|
turnSequence: number;
|
|
30
|
+
initiator?: string;
|
|
27
31
|
outcome: PluginOutcome;
|
|
28
32
|
contractId?: string;
|
|
29
33
|
}>;
|
|
@@ -208,7 +208,10 @@ function handlers(pluginId, instance) {
|
|
|
208
208
|
throw new TypeError("plugin signals must be an object");
|
|
209
209
|
const registered = [];
|
|
210
210
|
for (const [kind, handler] of Object.entries(instance.signals)) {
|
|
211
|
-
if (kind !== "akuma.
|
|
211
|
+
if (kind !== "akuma.initiating" &&
|
|
212
|
+
kind !== "akuma.called" &&
|
|
213
|
+
kind !== "akuma.turn-outcome" &&
|
|
214
|
+
kind !== "akuma.body-ended") {
|
|
212
215
|
throw new TypeError(`plugin signal is unknown: ${kind}`);
|
|
213
216
|
}
|
|
214
217
|
if (typeof handler !== "function")
|
|
@@ -30,14 +30,15 @@ export async function amendOperation(input) {
|
|
|
30
30
|
const initial = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
|
|
31
31
|
source = contractState(initial.decision, input.contractId)?.terms;
|
|
32
32
|
}
|
|
33
|
+
// Deriving the caller-stamped document is pure: it never depends on the latest Contract.
|
|
34
|
+
const amendment = source === undefined || input.deriveAmendment === undefined
|
|
35
|
+
? undefined
|
|
36
|
+
: { source, ...input.deriveAmendment(source) };
|
|
33
37
|
return intentOutcomeWithSeatClose(await withPrivateStatePublicationSeat(input.scope, async (seat) => {
|
|
34
38
|
const attempts = mintAttempts({ entryCount: 1 });
|
|
35
39
|
for (let index = 0; index < attempts.length; index += 1) {
|
|
36
40
|
let observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
|
|
37
41
|
const state = contractState(observation.decision, input.contractId);
|
|
38
|
-
const amendment = source === undefined || input.deriveAmendment === undefined
|
|
39
|
-
? undefined
|
|
40
|
-
: { source, ...input.deriveAmendment(source) };
|
|
41
42
|
if (amendment !== undefined) {
|
|
42
43
|
observation = await extendPrerequisiteClosureAt(input.channel, observation, [
|
|
43
44
|
...new Set([...(state?.terms.after ?? []), ...amendment.terms.after]),
|
|
@@ -35,6 +35,8 @@ export type AttemptTerminal = Readonly<{
|
|
|
35
35
|
kind: "collision";
|
|
36
36
|
}> | PublicationFailed;
|
|
37
37
|
export type DecidedOfferResult<Refusal = never> = AcceptedAdmission | AttemptTerminal | Readonly<{
|
|
38
|
+
kind: "stale";
|
|
39
|
+
}> | Readonly<{
|
|
38
40
|
kind: "redecide";
|
|
39
41
|
}> | Readonly<{
|
|
40
42
|
kind: "refused";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { normalizeTargetBranch, observeBindCoordinates } from "../git/observe.js";
|
|
1
|
+
import { normalizeTargetBranch, observeBindCoordinates, } from "../git/observe.js";
|
|
2
2
|
import { gitObjectIdForSnapshot } from "../git/identity.js";
|
|
3
3
|
import { contractId } from "../core/facts/types.js";
|
|
4
4
|
import { decideBind } from "../core/verbs/bind.js";
|
|
5
|
-
import {
|
|
5
|
+
import { admitPreparedIntent } from "./intent.js";
|
|
6
6
|
import { complete } from "./outcome.js";
|
|
7
7
|
export function decodeTargetInputRefusal(value) {
|
|
8
8
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
@@ -29,19 +29,44 @@ export function decodeForkSourceMovedRefusal(value) {
|
|
|
29
29
|
throw new Error("malformed fork-source refusal");
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Acquire the repeatable Git coordinates outside the publication seat. The artifact carries the
|
|
34
|
+
* observed start/target plus the expected ref assertions that publication must still confirm.
|
|
35
|
+
*/
|
|
36
|
+
async function prepareBindCoordinates(input, selection) {
|
|
33
37
|
if (input.verification.kind === "refused") {
|
|
34
|
-
return { kind: "prepared",
|
|
38
|
+
return { kind: "prepared", prepared: { kind: "declaration-refused", preparation: input.verification } };
|
|
35
39
|
}
|
|
36
40
|
const observed = await observeBindCoordinates(input.scope, selection);
|
|
37
41
|
if (observed === null)
|
|
38
|
-
return { kind: "
|
|
39
|
-
if (
|
|
40
|
-
return { kind: "
|
|
42
|
+
return { kind: "prepared", prepared: { kind: "unresolved", reason: "target-missing" } };
|
|
43
|
+
if ("kind" in observed)
|
|
44
|
+
return { kind: "prepared", prepared: { kind: "unresolved", reason: "unborn-head" } };
|
|
45
|
+
return {
|
|
46
|
+
kind: "prepared",
|
|
47
|
+
prepared: { kind: "resolved", observed },
|
|
48
|
+
...(input.coordinates === undefined
|
|
49
|
+
? { assertions: [{ ref: observed.target ?? "HEAD", oid: gitObjectIdForSnapshot(observed.start) }] }
|
|
50
|
+
: {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Assemble the decision input from the fresh in-custody observation. Coordinates fixed by the
|
|
55
|
+
* invocation are never replaced; mutable coordinate disappearance is read again as current evidence.
|
|
56
|
+
*/
|
|
57
|
+
async function assembleBindInput(input, selection, seed, prepared) {
|
|
58
|
+
if (prepared.kind === "declaration-refused") {
|
|
59
|
+
return { kind: "prepared", input: { ...seed, preparation: prepared.preparation } };
|
|
60
|
+
}
|
|
61
|
+
if (prepared.kind === "unresolved") {
|
|
62
|
+
const current = await observeBindCoordinates(input.scope, selection);
|
|
63
|
+
if (current === null)
|
|
64
|
+
return { kind: "refused", refusal: { kind: "target-missing" } };
|
|
65
|
+
if ("kind" in current)
|
|
66
|
+
return { kind: "refused", refusal: { kind: "unborn-head" } };
|
|
67
|
+
return { kind: "stale" };
|
|
41
68
|
}
|
|
42
|
-
const
|
|
43
|
-
const oid = gitObjectIdForSnapshot(input.coordinates === undefined ? start : observed.start);
|
|
44
|
-
const assertions = input.coordinates === undefined ? [{ ref: observed.target ?? "HEAD", oid }] : [];
|
|
69
|
+
const observed = prepared.observed;
|
|
45
70
|
return {
|
|
46
71
|
kind: "prepared",
|
|
47
72
|
input: {
|
|
@@ -50,7 +75,7 @@ async function bindPreparation(input, selection, seed) {
|
|
|
50
75
|
kind: "prepared",
|
|
51
76
|
data: {
|
|
52
77
|
coordinates: {
|
|
53
|
-
start,
|
|
78
|
+
start: input.coordinates?.start ?? observed.start,
|
|
54
79
|
...(observed.target === undefined ? {} : { target: observed.target }),
|
|
55
80
|
workspace: input.workspace,
|
|
56
81
|
},
|
|
@@ -58,7 +83,6 @@ async function bindPreparation(input, selection, seed) {
|
|
|
58
83
|
},
|
|
59
84
|
},
|
|
60
85
|
},
|
|
61
|
-
assertions,
|
|
62
86
|
};
|
|
63
87
|
}
|
|
64
88
|
export async function bindOperation(input) {
|
|
@@ -71,13 +95,16 @@ export async function bindOperation(input) {
|
|
|
71
95
|
}
|
|
72
96
|
const at = new Date().toISOString();
|
|
73
97
|
const id = input.contractId;
|
|
74
|
-
return complete(await
|
|
98
|
+
return complete(await admitPreparedIntent(input.channel, input.scope, {
|
|
75
99
|
contractId: id,
|
|
76
100
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
77
101
|
at,
|
|
78
102
|
}, decideBind, {
|
|
79
103
|
observedContracts: [id, ...input.terms.after, ...(input.source === undefined ? [] : [input.source.contractId])],
|
|
80
|
-
|
|
104
|
+
preparation: {
|
|
105
|
+
external: async () => await prepareBindCoordinates(input, selection),
|
|
106
|
+
assemble: async (_observation, original, prepared) => await assembleBindInput(input, selection, original, prepared),
|
|
107
|
+
},
|
|
81
108
|
...(input.source === undefined
|
|
82
109
|
? {}
|
|
83
110
|
: {
|
|
@@ -6,6 +6,9 @@ import { type ContractCheckpoint, type ExecutionProgress, type ExecutionStop } f
|
|
|
6
6
|
import type { DocumentDerivation, PlacementStop, VerificationStop } from "./operations.js";
|
|
7
7
|
export type CandidateCompletion = Readonly<{
|
|
8
8
|
integration: SnapshotId;
|
|
9
|
+
/** The reference this placement advanced and the head it advanced from, both observed at completion time. */
|
|
10
|
+
predecessor?: SnapshotId;
|
|
11
|
+
target?: string;
|
|
9
12
|
verification?: Readonly<{
|
|
10
13
|
mode: "ran" | "reused";
|
|
11
14
|
verdict: "satisfied" | "unsatisfied";
|
|
@@ -82,6 +82,8 @@ function completedResult(cursor) {
|
|
|
82
82
|
const integration = state.currentIntegration?.snapshot;
|
|
83
83
|
if (integration === undefined)
|
|
84
84
|
throw new Error("accepted placement requires its integration snapshot");
|
|
85
|
+
const target = state.coordinates.target;
|
|
86
|
+
const predecessor = state.currentIntegration?.predecessor;
|
|
85
87
|
// Never attach a superseded run's verdict to the final integration.
|
|
86
88
|
const current = currentVerifiedAttestation(state);
|
|
87
89
|
const verification = current === undefined
|
|
@@ -96,7 +98,11 @@ function completedResult(cursor) {
|
|
|
96
98
|
checkpoint: cursor.checkpoint,
|
|
97
99
|
evidence: {
|
|
98
100
|
...evidence,
|
|
99
|
-
completion: {
|
|
101
|
+
completion: {
|
|
102
|
+
integration,
|
|
103
|
+
...(predecessor === undefined || target === undefined ? {} : { predecessor, target }),
|
|
104
|
+
...(verification === undefined ? {} : { verification }),
|
|
105
|
+
},
|
|
100
106
|
...(current === undefined || verification?.mode !== "reused" ? {} : { verificationReuse: current }),
|
|
101
107
|
...(current?.verdict !== "unsatisfied" || current.summary === undefined
|
|
102
108
|
? {}
|