@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
|
@@ -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,11 +1,19 @@
|
|
|
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,
|
|
5
13
|
...(input.settings === undefined ? {} : { settings: input.settings }),
|
|
6
14
|
reportDiagnostic: input.reportDiagnostic,
|
|
7
15
|
});
|
|
8
|
-
runtime.emit({
|
|
16
|
+
await runtime.emit({
|
|
9
17
|
kind: "akuma.called",
|
|
10
18
|
akumaId: input.akumaId,
|
|
11
19
|
...(input.callerAkumaId === undefined ? {} : { callerAkumaId: input.callerAkumaId }),
|
|
@@ -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
|
}>;
|
|
@@ -43,7 +47,7 @@ export type PluginSignal = {
|
|
|
43
47
|
export type PluginHooks = Readonly<{
|
|
44
48
|
[K in keyof PluginSignalMap]?: (signal: Readonly<{
|
|
45
49
|
kind: K;
|
|
46
|
-
}> & PluginSignalMap[K]) => Promise<void> | void;
|
|
50
|
+
}> & PluginSignalMap[K], cancellation?: AbortSignal) => Promise<void> | void;
|
|
47
51
|
}>;
|
|
48
52
|
export type PluginContext = Readonly<{
|
|
49
53
|
world: WorldRoot;
|
|
@@ -52,7 +56,7 @@ export type PluginContext = Readonly<{
|
|
|
52
56
|
}>;
|
|
53
57
|
export type KeiyakuPlugin = Readonly<{
|
|
54
58
|
manifest: PluginManifest;
|
|
55
|
-
activate(context: PluginContext): Promise<PluginInstance> | PluginInstance;
|
|
59
|
+
activate(context: PluginContext, cancellation?: AbortSignal): Promise<PluginInstance> | PluginInstance;
|
|
56
60
|
}>;
|
|
57
61
|
export type PluginInstance = Readonly<{
|
|
58
62
|
signals?: PluginHooks;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Settings } from "../settings.js";
|
|
2
2
|
import type { WorldRoot } from "../world.js";
|
|
3
|
-
import type { PluginSignal
|
|
3
|
+
import type { PluginSignal } from "./public.js";
|
|
4
4
|
type PluginDiagnostic = (diagnostic: string) => void;
|
|
5
5
|
type PluginRuntimeInput = Readonly<{
|
|
6
6
|
world: WorldRoot;
|
|
@@ -9,8 +9,6 @@ type PluginRuntimeInput = Readonly<{
|
|
|
9
9
|
}>;
|
|
10
10
|
export type PluginRuntime = Readonly<{
|
|
11
11
|
emit(signal: PluginSignal, reportDiagnostic?: PluginDiagnostic): Promise<void>;
|
|
12
|
-
drain(kind?: keyof PluginSignalMap): Promise<void>;
|
|
13
12
|
}>;
|
|
14
13
|
export declare function pluginRuntime(input: PluginRuntimeInput): Promise<PluginRuntime>;
|
|
15
|
-
export declare function drainPluginRuntime(world: WorldRoot): Promise<void>;
|
|
16
14
|
export {};
|
|
@@ -4,13 +4,14 @@ import { isAbsolute, join, posix, relative, sep } from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { settings } from "../settings.js";
|
|
6
6
|
const DIAGNOSTIC_LIMIT = 500;
|
|
7
|
-
const
|
|
7
|
+
const PLUGIN_ATTEMPT_TIMEOUT_MS = 5_000;
|
|
8
8
|
const PLUGIN_NAMESPACE = "plugins";
|
|
9
9
|
const SQUARE_PLUGIN_PACKAGE = "@astrosheep/keiyaku-plugin-square";
|
|
10
10
|
const BUILTIN_PLUGINS = Object.freeze([
|
|
11
11
|
Object.freeze({ id: "square", package: SQUARE_PLUGIN_PACKAGE, config: undefined }),
|
|
12
12
|
]);
|
|
13
13
|
const PROCESS_RUNTIMES = new Map();
|
|
14
|
+
const ABORTED = Symbol("plugin attempt aborted");
|
|
14
15
|
function object(value) {
|
|
15
16
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16
17
|
}
|
|
@@ -27,6 +28,43 @@ function diagnostic(report, subject, stage, error) {
|
|
|
27
28
|
// Diagnostics are optional side effects too.
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
function timeoutError() {
|
|
32
|
+
return new Error(`timed out after ${PLUGIN_ATTEMPT_TIMEOUT_MS}ms`);
|
|
33
|
+
}
|
|
34
|
+
async function boundedAttempt(attempt, timeoutMs = PLUGIN_ATTEMPT_TIMEOUT_MS) {
|
|
35
|
+
if (timeoutMs <= 0)
|
|
36
|
+
return { kind: "timed-out" };
|
|
37
|
+
const controller = new AbortController();
|
|
38
|
+
let timeout;
|
|
39
|
+
const completed = Promise.resolve()
|
|
40
|
+
.then(() => attempt(controller.signal))
|
|
41
|
+
.then((value) => ({ kind: "settled", value }), (error) => ({ kind: "failed", error }));
|
|
42
|
+
const expired = new Promise((resolve) => {
|
|
43
|
+
timeout = setTimeout(() => {
|
|
44
|
+
controller.abort(timeoutError());
|
|
45
|
+
resolve({ kind: "timed-out" });
|
|
46
|
+
}, timeoutMs);
|
|
47
|
+
});
|
|
48
|
+
const result = await Promise.race([completed, expired]);
|
|
49
|
+
if (timeout !== undefined)
|
|
50
|
+
clearTimeout(timeout);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
async function settleBefore(promise, cancellation) {
|
|
54
|
+
if (cancellation.aborted)
|
|
55
|
+
return ABORTED;
|
|
56
|
+
let releaseAbort;
|
|
57
|
+
const aborted = new Promise((resolve) => {
|
|
58
|
+
releaseAbort = () => resolve(ABORTED);
|
|
59
|
+
cancellation.addEventListener("abort", releaseAbort, { once: true });
|
|
60
|
+
});
|
|
61
|
+
try {
|
|
62
|
+
return await Promise.race([promise, aborted]);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
cancellation.removeEventListener("abort", releaseAbort);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
30
68
|
function selectedEntry(entry, report) {
|
|
31
69
|
if (entry.name.trim().length === 0) {
|
|
32
70
|
diagnostic(report, "<unnamed>", "selection", new TypeError("plugin setting name must be nonblank"));
|
|
@@ -170,7 +208,10 @@ function handlers(pluginId, instance) {
|
|
|
170
208
|
throw new TypeError("plugin signals must be an object");
|
|
171
209
|
const registered = [];
|
|
172
210
|
for (const [kind, handler] of Object.entries(instance.signals)) {
|
|
173
|
-
if (kind !== "akuma.
|
|
211
|
+
if (kind !== "akuma.initiating" &&
|
|
212
|
+
kind !== "akuma.called" &&
|
|
213
|
+
kind !== "akuma.turn-outcome" &&
|
|
214
|
+
kind !== "akuma.body-ended") {
|
|
174
215
|
throw new TypeError(`plugin signal is unknown: ${kind}`);
|
|
175
216
|
}
|
|
176
217
|
if (typeof handler !== "function")
|
|
@@ -214,7 +255,8 @@ async function importFromWorld(world, packageName) {
|
|
|
214
255
|
hooks.deregister();
|
|
215
256
|
}
|
|
216
257
|
}
|
|
217
|
-
async function activate(
|
|
258
|
+
async function activate(input) {
|
|
259
|
+
const { selected, world, activated, report, cancellation, onStarted } = input;
|
|
218
260
|
let started = false;
|
|
219
261
|
const markStarted = () => {
|
|
220
262
|
if (started)
|
|
@@ -231,6 +273,10 @@ async function activate(selected, world, activated, report, onStarted) {
|
|
|
231
273
|
diagnostic(report, selected.id, "import", error);
|
|
232
274
|
return [];
|
|
233
275
|
}
|
|
276
|
+
if (cancellation.aborted) {
|
|
277
|
+
markStarted();
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
234
280
|
let candidate;
|
|
235
281
|
try {
|
|
236
282
|
candidate = manifest(module.default);
|
|
@@ -264,9 +310,15 @@ async function activate(selected, world, activated, report, onStarted) {
|
|
|
264
310
|
diagnostic(report, selected.id, "validation", error);
|
|
265
311
|
return [];
|
|
266
312
|
}
|
|
313
|
+
if (cancellation.aborted) {
|
|
314
|
+
markStarted();
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
267
317
|
markStarted();
|
|
268
318
|
try {
|
|
269
|
-
const instance = await candidate.activate(context);
|
|
319
|
+
const instance = await candidate.activate(context, cancellation);
|
|
320
|
+
if (cancellation.aborted)
|
|
321
|
+
return [];
|
|
270
322
|
const registered = handlers(candidate.manifest.id, instance);
|
|
271
323
|
activated.add(candidate.manifest.id);
|
|
272
324
|
return registered;
|
|
@@ -276,23 +328,12 @@ async function activate(selected, world, activated, report, onStarted) {
|
|
|
276
328
|
return [];
|
|
277
329
|
}
|
|
278
330
|
}
|
|
279
|
-
function deliver(entry, signal, reportDiagnostic,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
void delivery.finally(() => deliveries.delete(delivery));
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
diagnostic(reportDiagnostic, entry.pluginId, "signal", error);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
function deliverTo(handlers, signal, reportDiagnostic, inFlight) {
|
|
292
|
-
for (const entry of handlers) {
|
|
293
|
-
if (entry.kind === signal.kind)
|
|
294
|
-
deliver(entry, signal, reportDiagnostic, inFlight);
|
|
295
|
-
}
|
|
331
|
+
async function deliver(entry, signal, reportDiagnostic, timeoutMs) {
|
|
332
|
+
const result = await boundedAttempt((cancellation) => entry.handler(signal, cancellation), timeoutMs);
|
|
333
|
+
if (result.kind === "timed-out")
|
|
334
|
+
diagnostic(reportDiagnostic, entry.pluginId, "signal", timeoutError());
|
|
335
|
+
if (result.kind === "failed")
|
|
336
|
+
diagnostic(reportDiagnostic, entry.pluginId, "signal", result.error);
|
|
296
337
|
}
|
|
297
338
|
export async function pluginRuntime(input) {
|
|
298
339
|
let runtime = PROCESS_RUNTIMES.get(input.world);
|
|
@@ -302,101 +343,52 @@ export async function pluginRuntime(input) {
|
|
|
302
343
|
}
|
|
303
344
|
return await runtime;
|
|
304
345
|
}
|
|
305
|
-
export async function drainPluginRuntime(world) {
|
|
306
|
-
const runtime = PROCESS_RUNTIMES.get(world);
|
|
307
|
-
if (runtime !== undefined)
|
|
308
|
-
await (await runtime).drain();
|
|
309
|
-
}
|
|
310
|
-
async function drainWithDeadline(pending) {
|
|
311
|
-
let timeout;
|
|
312
|
-
try {
|
|
313
|
-
await Promise.race([
|
|
314
|
-
pending,
|
|
315
|
-
new Promise((resolve) => {
|
|
316
|
-
timeout = setTimeout(resolve, PLUGIN_DRAIN_TIMEOUT_MS);
|
|
317
|
-
}),
|
|
318
|
-
]);
|
|
319
|
-
}
|
|
320
|
-
finally {
|
|
321
|
-
clearTimeout(timeout);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
346
|
async function createPluginRuntime(input) {
|
|
325
347
|
const report = input.reportDiagnostic;
|
|
326
348
|
const selected = selectedPlugins(input.settings ?? (await settings({ root: input.world })), report);
|
|
327
349
|
const activated = new Set();
|
|
328
|
-
const registered = new Map();
|
|
329
|
-
const settled = new Set();
|
|
330
|
-
const pending = new Map();
|
|
331
|
-
const settledPromises = [];
|
|
332
|
-
const inFlight = new Map();
|
|
333
350
|
let previousStarted = Promise.resolve();
|
|
334
|
-
|
|
351
|
+
const activations = selected.map((entry) => {
|
|
335
352
|
let releaseStarted;
|
|
336
|
-
let releaseSettled;
|
|
337
353
|
const started = new Promise((resolve) => {
|
|
338
354
|
releaseStarted = resolve;
|
|
339
355
|
});
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
registered.set(index, handlers);
|
|
348
|
-
settled.add(index);
|
|
349
|
-
const queued = pending.get(index);
|
|
350
|
-
if (queued !== undefined) {
|
|
351
|
-
pending.delete(index);
|
|
352
|
-
for (const signal of queued)
|
|
353
|
-
deliverTo(handlers, signal, report, inFlight);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
diagnostic(report, entry.id, "activation", error);
|
|
358
|
-
settled.add(index);
|
|
359
|
-
pending.delete(index);
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
releaseStarted();
|
|
363
|
-
releaseSettled();
|
|
364
|
-
}
|
|
356
|
+
const activation = previousStarted.then(async () => {
|
|
357
|
+
const result = await boundedAttempt((cancellation) => activate({ selected: entry, world: input.world, activated, report, cancellation, onStarted: releaseStarted }));
|
|
358
|
+
releaseStarted();
|
|
359
|
+
if (result.kind === "settled")
|
|
360
|
+
return { handlers: result.value, timedOut: false };
|
|
361
|
+
diagnostic(report, entry.id, "activation", result.kind === "failed" ? result.error : timeoutError());
|
|
362
|
+
return { handlers: [], timedOut: result.kind === "timed-out" };
|
|
365
363
|
});
|
|
366
364
|
previousStarted = started;
|
|
367
|
-
|
|
365
|
+
return activation;
|
|
366
|
+
});
|
|
368
367
|
return Object.freeze({
|
|
369
|
-
emit(signal, reportDiagnostic = report) {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
368
|
+
async emit(signal, reportDiagnostic = report) {
|
|
369
|
+
const deadline = performance.now() + PLUGIN_ATTEMPT_TIMEOUT_MS;
|
|
370
|
+
const activated = await boundedAttempt(async (cancellation) => {
|
|
371
|
+
const handlers = await Promise.all(activations.map(async (activation, index) => {
|
|
372
|
+
const activatedHandlers = await settleBefore(activation, cancellation);
|
|
373
|
+
if (activatedHandlers !== ABORTED) {
|
|
374
|
+
if (activatedHandlers.timedOut)
|
|
375
|
+
diagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
|
|
376
|
+
return activatedHandlers.handlers;
|
|
377
|
+
}
|
|
378
|
+
diagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
|
|
379
|
+
return [];
|
|
380
|
+
}));
|
|
381
|
+
return handlers.flat();
|
|
382
|
+
});
|
|
383
|
+
if (activated.kind === "failed") {
|
|
384
|
+
diagnostic(reportDiagnostic, "runtime", "signal", activated.error);
|
|
385
|
+
return;
|
|
382
386
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const settle = async () => {
|
|
389
|
-
await Promise.all(settledPromises);
|
|
390
|
-
for (;;) {
|
|
391
|
-
const pending = kind === undefined
|
|
392
|
-
? [...inFlight.values()].flatMap((deliveries) => [...deliveries])
|
|
393
|
-
: [...(inFlight.get(kind) ?? [])];
|
|
394
|
-
if (pending.length === 0)
|
|
395
|
-
return;
|
|
396
|
-
await Promise.all(pending);
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
await drainWithDeadline(settle());
|
|
387
|
+
if (activated.kind === "timed-out")
|
|
388
|
+
return;
|
|
389
|
+
await Promise.all(activated.value
|
|
390
|
+
.filter((entry) => entry.kind === signal.kind)
|
|
391
|
+
.map((entry) => deliver(entry, signal, reportDiagnostic, Math.max(0, deadline - performance.now()))));
|
|
400
392
|
},
|
|
401
393
|
});
|
|
402
394
|
}
|
|
@@ -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
|
? {}
|
|
@@ -113,17 +119,35 @@ function stoppedResult(cursor, stop) {
|
|
|
113
119
|
async function placeCurrentCandidate(input, cursor) {
|
|
114
120
|
cursor.stage = "placement";
|
|
115
121
|
input.signal?.throwIfAborted();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
placement: {
|
|
122
|
-
contractId: cursor.checkpoint.state.id,
|
|
123
|
-
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
124
|
-
at: timestamp(),
|
|
125
|
-
},
|
|
122
|
+
input.progress.observe({
|
|
123
|
+
kind: "stage",
|
|
124
|
+
contractId: cursor.checkpoint.state.id,
|
|
125
|
+
stage: "placement",
|
|
126
|
+
state: "started",
|
|
126
127
|
});
|
|
128
|
+
const result = await (async () => {
|
|
129
|
+
try {
|
|
130
|
+
return await admitPlacement({
|
|
131
|
+
channel: input.channel,
|
|
132
|
+
repository: input.repository,
|
|
133
|
+
progress: input.progress,
|
|
134
|
+
target: cursor.checkpoint.state.coordinates.target,
|
|
135
|
+
placement: {
|
|
136
|
+
contractId: cursor.checkpoint.state.id,
|
|
137
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
138
|
+
at: timestamp(),
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
input.progress.observe({
|
|
144
|
+
kind: "stage",
|
|
145
|
+
contractId: cursor.checkpoint.state.id,
|
|
146
|
+
stage: "placement",
|
|
147
|
+
state: "finished",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
127
151
|
if (result.kind === "accepted") {
|
|
128
152
|
cursor.checkpoint = contractCheckpoint(result);
|
|
129
153
|
input.progress.recordResidue(result.state.id, result);
|