@astrosheep/keiyaku 4.0.4 → 4.0.6
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/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +15 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +24 -4
- package/build/src/akuma/akuma.d.ts +2 -1
- package/build/src/akuma/akuma.js +27 -9
- package/build/src/akuma/allowed.d.ts +1 -1
- package/build/src/akuma/allowed.js +3 -10
- package/build/src/akuma/archetype.d.ts +3 -1
- package/build/src/akuma/archetype.js +11 -6
- package/build/src/akuma/body-turn.d.ts +64 -0
- package/build/src/akuma/body-turn.js +449 -0
- package/build/src/akuma/body.d.ts +4 -5
- package/build/src/akuma/body.js +9 -414
- package/build/src/akuma/heart/facts.d.ts +37 -9
- package/build/src/akuma/heart/facts.js +3 -1
- package/build/src/akuma/heart/index.d.ts +4 -13
- package/build/src/akuma/heart/index.js +14 -135
- package/build/src/akuma/heart/request-authority.d.ts +12 -0
- package/build/src/akuma/heart/request-authority.js +119 -0
- package/build/src/akuma/heart/request-rows.d.ts +16 -0
- package/build/src/akuma/heart/request-rows.js +88 -0
- package/build/src/akuma/heart/rows.d.ts +1 -28
- package/build/src/akuma/heart/rows.js +4 -138
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +11 -3
- package/build/src/akuma/heart/soul.js +0 -36
- package/build/src/akuma/heart/timeline.d.ts +1 -0
- package/build/src/akuma/heart/timeline.js +19 -0
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/nuke.d.ts +2 -0
- package/build/src/akuma/nuke.js +152 -0
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +4 -7
- package/build/src/akuma/providers/acp/config.d.ts +8 -0
- package/build/src/akuma/providers/acp/config.js +37 -0
- package/build/src/akuma/providers/acp/core.js +12 -2
- package/build/src/akuma/providers/acp/index.d.ts +1 -8
- package/build/src/akuma/providers/acp/index.js +2 -38
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +12 -3
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +5 -2
- package/build/src/akuma/providers/grok-build/index.js +1 -2
- package/build/src/akuma/providers/index.d.ts +3 -2
- package/build/src/akuma/providers/index.js +36 -36
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +14 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +2 -2
- package/build/src/akuma/providers/opencode-sdk/session.js +2 -2
- package/build/src/akuma/providers/pi/index.d.ts +1 -1
- package/build/src/akuma/providers/pi/index.js +29 -6
- package/build/src/akuma/request-serve.d.ts +88 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +125 -0
- package/build/src/akuma/request-wire.js +383 -0
- package/build/src/akuma/requests.d.ts +28 -94
- package/build/src/akuma/requests.js +52 -562
- package/build/src/akuma-body.d.ts +7 -0
- package/build/src/akuma-body.js +69 -2
- package/build/src/alias/index.d.ts +1 -0
- package/build/src/alias/index.js +34 -1
- package/build/src/body/arc.js +4 -29
- package/build/src/body/decode.js +5 -39
- package/build/src/body/envelope.d.ts +7 -0
- package/build/src/body/envelope.js +37 -0
- package/build/src/cli/accepted.js +21 -9
- package/build/src/cli/commands/akuma-invoke.js +1 -0
- package/build/src/cli/commands/akuma.d.ts +1 -0
- package/build/src/cli/commands/akuma.js +13 -10
- package/build/src/cli/commands/contract.d.ts +28 -17
- package/build/src/cli/commands/contract.js +23 -17
- package/build/src/cli/commands/install.d.ts +2 -2
- package/build/src/cli/commands/install.js +6 -6
- package/build/src/cli/commands/nuke.d.ts +10 -0
- package/build/src/cli/commands/nuke.js +20 -0
- package/build/src/cli/commands/task-invoke.d.ts +15 -0
- package/build/src/cli/commands/task-invoke.js +144 -24
- package/build/src/cli/commands/task-query.d.ts +2 -2
- package/build/src/cli/commands/task-query.js +1 -2
- package/build/src/cli/commands/task.js +22 -22
- package/build/src/cli/coordinates.d.ts +6 -2
- package/build/src/cli/coordinates.js +12 -56
- package/build/src/cli/invoke.d.ts +5 -4
- package/build/src/cli/invoke.js +112 -58
- package/build/src/cli/main.d.ts +0 -2
- package/build/src/cli/main.js +4 -140
- package/build/src/cli/parse.d.ts +17 -4
- package/build/src/cli/parse.js +93 -15
- package/build/src/cli/render/akuma.js +111 -53
- package/build/src/cli/render/audit.js +2 -6
- package/build/src/cli/render/contract.js +61 -20
- package/build/src/cli/render/kanshi.js +15 -5
- package/build/src/cli/render/nuke.d.ts +3 -0
- package/build/src/cli/render/nuke.js +8 -0
- package/build/src/cli/render/receipt.js +28 -1
- package/build/src/cli/render/refusal.d.ts +2 -1
- package/build/src/cli/render/refusal.js +31 -0
- package/build/src/cli/render/terminal.d.ts +0 -2
- package/build/src/cli/render/terminal.js +0 -29
- package/build/src/cli/render/text.js +6 -1
- package/build/src/cli/result.d.ts +36 -9
- package/build/src/cli/runtime.d.ts +2 -0
- package/build/src/cli/runtime.js +140 -0
- package/build/src/cli/usage.d.ts +0 -2
- package/build/src/cli/usage.js +0 -4
- package/build/src/contract-worktree.d.ts +29 -14
- package/build/src/contract-worktree.js +131 -70
- package/build/src/core/facts/codec.js +9 -0
- package/build/src/core/facts/fold.js +13 -1
- package/build/src/core/facts/types.d.ts +8 -1
- package/build/src/core/subject.js +4 -3
- package/build/src/core/verbs/placement.d.ts +9 -1
- package/build/src/core/verbs/placement.js +24 -6
- package/build/src/core/verbs/reintegrate.d.ts +13 -0
- package/build/src/core/verbs/reintegrate.js +24 -0
- package/build/src/dispatch/index.d.ts +1 -1
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/hooks.d.ts +2 -0
- package/build/src/git/hooks.js +34 -1
- package/build/src/git/integration.d.ts +9 -2
- package/build/src/git/integration.js +92 -1
- package/build/src/git/nuke.d.ts +2 -0
- package/build/src/git/nuke.js +97 -0
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +28 -0
- package/build/src/git/process.js +94 -0
- package/build/src/git/read-observation.d.ts +2 -1
- package/build/src/git/read-observation.js +3 -2
- package/build/src/git/reconcile.d.ts +4 -1
- package/build/src/git/reconcile.js +42 -8
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +7 -118
- package/build/src/git/scratch.d.ts +1 -1
- package/build/src/git/scratch.js +1 -1
- package/build/src/git/target-placement.d.ts +3 -2
- package/build/src/git/target-placement.js +15 -14
- package/build/src/git/tender.d.ts +2 -1
- package/build/src/git/tender.js +4 -3
- package/build/src/git/terminal-seal.d.ts +2 -2
- package/build/src/git/terminal-seal.js +3 -6
- package/build/src/git/workspace.d.ts +4 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/kanshi/read.js +5 -1
- package/build/src/library/address.js +7 -1
- package/build/src/library/akuma-creation.d.ts +1 -0
- package/build/src/library/akuma-creation.js +8 -1
- package/build/src/library/audit.d.ts +3 -1
- package/build/src/library/audit.js +2 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +1 -1
- package/build/src/library/configuration.d.ts +1 -1
- package/build/src/library/configuration.js +52 -18
- package/build/src/library/contract.d.ts +59 -4
- package/build/src/library/contract.js +195 -34
- package/build/src/library/delivery.d.ts +4 -2
- package/build/src/library/fleet.d.ts +27 -7
- package/build/src/library/fleet.js +79 -39
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +0 -5
- package/build/src/library/keiyaku.d.ts +6 -2
- package/build/src/library/keiyaku.js +2 -0
- package/build/src/library/mutation.d.ts +3 -1
- package/build/src/library/mutation.js +3 -0
- package/build/src/library/nuke.d.ts +14 -0
- package/build/src/library/nuke.js +54 -0
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +8 -3
- package/build/src/library/refusal.d.ts +10 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +4 -1
- package/build/src/library/repo.js +10 -4
- package/build/src/protocol/abandon.d.ts +9 -0
- package/build/src/protocol/abandon.js +15 -0
- package/build/src/protocol/amend.d.ts +16 -0
- package/build/src/protocol/amend.js +77 -0
- package/build/src/protocol/arc.d.ts +6 -0
- package/build/src/protocol/arc.js +12 -0
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/audit.d.ts +53 -0
- package/build/src/protocol/audit.js +147 -0
- package/build/src/protocol/bind.d.ts +1 -1
- package/build/src/protocol/completion.d.ts +42 -0
- package/build/src/protocol/completion.js +183 -0
- package/build/src/protocol/deliver.d.ts +49 -0
- package/build/src/protocol/deliver.js +261 -0
- package/build/src/protocol/intent.d.ts +2 -1
- package/build/src/protocol/intent.js +8 -2
- package/build/src/protocol/operations.d.ts +93 -202
- package/build/src/protocol/operations.js +23 -642
- package/build/src/protocol/placement.d.ts +12 -13
- package/build/src/protocol/placement.js +8 -19
- package/build/src/protocol/read/status.d.ts +17 -5
- package/build/src/protocol/read/status.js +57 -13
- package/build/src/protocol/reconcile.d.ts +35 -0
- package/build/src/protocol/reconcile.js +41 -0
- package/build/src/protocol/reintegrate.d.ts +31 -0
- package/build/src/protocol/reintegrate.js +74 -0
- package/build/src/protocol/review.d.ts +34 -0
- package/build/src/protocol/review.js +156 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/runtime/proc/run.js +9 -5
- package/build/src/settlement/fence.d.ts +3 -1
- package/build/src/settlement/fence.js +24 -0
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/settlement/settle.js +1 -2
- package/build/src/task/board.d.ts +0 -1
- package/build/src/task/board.js +0 -3
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +13 -11
- package/build/src/task/context.d.ts +14 -2
- package/build/src/task/context.js +30 -7
- package/build/src/task/index.d.ts +7 -6
- package/build/src/task/index.js +17 -145
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +44 -0
- package/build/src/task/mutation.js +93 -0
- package/build/src/task/operations.d.ts +14 -6
- package/build/src/task/operations.js +57 -57
- package/build/src/task/store.d.ts +1 -0
- package/build/src/task/store.js +91 -3
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/workspace-place.d.ts +3 -4
- package/build/src/workspace-place.js +26 -15
- package/package.json +3 -3
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { access, mkdir, readFile, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { abortableDelay } from "./abort.js";
|
|
4
|
+
import { HeldAkumaLeash, admitRequest, readNonterminalRequests, readRequest, readSoul, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./heart/index.js";
|
|
5
|
+
import { pathsForAkuId, worldRootForAkumaPaths, } from "./identity.js";
|
|
6
|
+
import { BIRTH_TIMEOUT_MS, publishAkuma } from "./publication.js";
|
|
7
|
+
import { atomicJson, canonicalAkuId, decodeClaim, decodeRecipe, receiptPath, } from "./request-wire.js";
|
|
8
|
+
import { isTaskMutationAction } from "../task/mutation.js";
|
|
9
|
+
const POLL_MS = 25;
|
|
10
|
+
function diagnostic(error) {
|
|
11
|
+
return error instanceof Error ? error.message : String(error);
|
|
12
|
+
}
|
|
13
|
+
function isTask(value) {
|
|
14
|
+
return isTaskMutationAction(value.action);
|
|
15
|
+
}
|
|
16
|
+
function acceptedReceipt(fact, service) {
|
|
17
|
+
return {
|
|
18
|
+
id: fact.id,
|
|
19
|
+
action: fact.action,
|
|
20
|
+
state: fact.state,
|
|
21
|
+
reference: service.action === "contract.deliver"
|
|
22
|
+
? {
|
|
23
|
+
kind: "accepted-reference",
|
|
24
|
+
repoRoot: service.repoRoot,
|
|
25
|
+
contractId: service.contractId,
|
|
26
|
+
deliveryFactId: service.deliveryFactId,
|
|
27
|
+
}
|
|
28
|
+
: {
|
|
29
|
+
kind: "accepted-reference",
|
|
30
|
+
repoRoot: service.repoRoot,
|
|
31
|
+
contractId: service.contractId,
|
|
32
|
+
reviewFactId: service.reviewFactId,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function receiptFor(fact) {
|
|
37
|
+
if (fact.state === "refused") {
|
|
38
|
+
return { id: fact.id, action: fact.action, state: fact.state, diagnostic: fact.diagnostic };
|
|
39
|
+
}
|
|
40
|
+
if (fact.state === "voided") {
|
|
41
|
+
return { id: fact.id, action: fact.action, state: fact.state, evidence: fact.evidence };
|
|
42
|
+
}
|
|
43
|
+
if (fact.action === "akuma.call" && fact.state === "served") {
|
|
44
|
+
return { id: fact.id, action: fact.action, state: fact.state, child: fact.child };
|
|
45
|
+
}
|
|
46
|
+
if (fact.state !== "served")
|
|
47
|
+
return null;
|
|
48
|
+
if (fact.action === "contract.deliver") {
|
|
49
|
+
return acceptedReceipt(fact, fact.service);
|
|
50
|
+
}
|
|
51
|
+
if (fact.action === "contract.review") {
|
|
52
|
+
return acceptedReceipt(fact, fact.service);
|
|
53
|
+
}
|
|
54
|
+
if (isTask(fact)) {
|
|
55
|
+
return {
|
|
56
|
+
id: fact.id,
|
|
57
|
+
action: fact.action,
|
|
58
|
+
state: fact.state,
|
|
59
|
+
reference: { kind: "served-reference", action: fact.action },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
async function projectReceipt(directory, fact, outcome) {
|
|
65
|
+
const receipt = outcome === undefined
|
|
66
|
+
? receiptFor(fact)
|
|
67
|
+
: fact.action === "akuma.call"
|
|
68
|
+
? null
|
|
69
|
+
: { id: fact.id, action: fact.action, state: "served", outcome };
|
|
70
|
+
if (receipt === null)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
await atomicJson(receiptPath(directory, fact.id), receipt);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code !== "ENOENT")
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function serveCall(input) {
|
|
81
|
+
input.signal.throwIfAborted();
|
|
82
|
+
if (!input.admissionOpen())
|
|
83
|
+
return;
|
|
84
|
+
const recipe = decodeRecipe(input.claim.recipe);
|
|
85
|
+
if (recipe === null)
|
|
86
|
+
return;
|
|
87
|
+
let fact = await admitRequest(input.paths, { ...input.claim, recipe, admittedAt: input.now() });
|
|
88
|
+
const existing = receiptFor(fact);
|
|
89
|
+
if (existing !== null) {
|
|
90
|
+
await projectReceipt(input.directory, fact);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!input.admissionOpen()) {
|
|
94
|
+
fact = await voidRequest(input.paths, input.claim.id, "body closed request admission");
|
|
95
|
+
await projectReceipt(input.directory, fact);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (fact.action !== "akuma.call" || fact.state !== "admitted") {
|
|
99
|
+
throw new Error(`Akuma request ${fact.id} cannot be replayed from ${fact.state}`);
|
|
100
|
+
}
|
|
101
|
+
const request = fact;
|
|
102
|
+
const world = worldRootForAkumaPaths(input.paths);
|
|
103
|
+
if (request.world !== world) {
|
|
104
|
+
fact = await refuseRequest(input.paths, request.id, `request world ${request.world} does not match ${world}`);
|
|
105
|
+
await projectReceipt(input.directory, fact);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const published = await publishAkuma({
|
|
110
|
+
worldPath: world,
|
|
111
|
+
archetype: request.archetype,
|
|
112
|
+
signal: input.signal,
|
|
113
|
+
reserve: async (allocated) => {
|
|
114
|
+
if (!input.admissionOpen())
|
|
115
|
+
throw new Error("body closed request admission");
|
|
116
|
+
fact = await reserveRequest(input.paths, request.id, allocated.id);
|
|
117
|
+
},
|
|
118
|
+
launch: async (allocated) => {
|
|
119
|
+
if (!input.admissionOpen())
|
|
120
|
+
throw new Error("body closed request admission");
|
|
121
|
+
await input.spawn({
|
|
122
|
+
paths: allocated.paths,
|
|
123
|
+
seed: {
|
|
124
|
+
id: allocated.id,
|
|
125
|
+
archetype: allocated.archetype,
|
|
126
|
+
...(request.recipe.description === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: { description: request.recipe.description }),
|
|
129
|
+
provider: request.recipe.provider,
|
|
130
|
+
options: request.recipe.options,
|
|
131
|
+
...(request.recipe.readonly === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: { readonly: request.recipe.readonly }),
|
|
134
|
+
allowed: request.recipe.allowed,
|
|
135
|
+
cwd: request.cwd ?? input.parent.cwd,
|
|
136
|
+
origin: {
|
|
137
|
+
kind: "request",
|
|
138
|
+
parent: input.parent.id,
|
|
139
|
+
requestId: request.id,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
initialBody: request.body,
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
fact = await serveRequest(input.paths, request.id, published.id);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
const current = await readRequest(input.paths, request.id);
|
|
150
|
+
if (current === null)
|
|
151
|
+
throw error;
|
|
152
|
+
fact = current.state === "admitted" || current.state === "reserved"
|
|
153
|
+
? await voidRequest(input.paths, request.id, diagnostic(error))
|
|
154
|
+
: current;
|
|
155
|
+
}
|
|
156
|
+
await projectReceipt(input.directory, fact);
|
|
157
|
+
}
|
|
158
|
+
function failure(error) {
|
|
159
|
+
const value = error !== null && typeof error === "object" ? error : null;
|
|
160
|
+
if (value?.kind === "akuma-not-born") {
|
|
161
|
+
const id = canonicalAkuId(value.id);
|
|
162
|
+
if (id !== null)
|
|
163
|
+
return { kind: "akuma-not-born", id };
|
|
164
|
+
}
|
|
165
|
+
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
166
|
+
}
|
|
167
|
+
async function execute(input, request) {
|
|
168
|
+
if (input.upstream === undefined)
|
|
169
|
+
throw new Error("upstream execution port is unavailable");
|
|
170
|
+
if (request.action === "akuma.wait") {
|
|
171
|
+
const result = await input.upstream.wait({
|
|
172
|
+
targets: request.targets,
|
|
173
|
+
completion: request.completion,
|
|
174
|
+
...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
|
|
175
|
+
signal: input.signal,
|
|
176
|
+
});
|
|
177
|
+
return { result, service: { action: request.action } };
|
|
178
|
+
}
|
|
179
|
+
if (request.action === "akuma.tell") {
|
|
180
|
+
const result = await input.upstream.tell({
|
|
181
|
+
target: request.target,
|
|
182
|
+
body: request.body,
|
|
183
|
+
tellId: request.id,
|
|
184
|
+
recordedAt: request.admittedAt,
|
|
185
|
+
signal: input.signal,
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
result,
|
|
189
|
+
service: { action: request.action, target: request.target, tellId: request.id },
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (request.action === "akuma.kill") {
|
|
193
|
+
const served = await input.upstream.kill({
|
|
194
|
+
targets: request.targets,
|
|
195
|
+
signal: input.signal,
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
result: served.result,
|
|
199
|
+
service: { action: request.action, results: served.service },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (request.action === "contract.deliver") {
|
|
203
|
+
return await executeDeliver(input, request);
|
|
204
|
+
}
|
|
205
|
+
if (request.action === "contract.review") {
|
|
206
|
+
return await executeReview(input, request);
|
|
207
|
+
}
|
|
208
|
+
const task = request;
|
|
209
|
+
const result = await input.upstream.task({
|
|
210
|
+
world: task.world,
|
|
211
|
+
request: task.request,
|
|
212
|
+
requester: task.requester,
|
|
213
|
+
signal: input.signal,
|
|
214
|
+
});
|
|
215
|
+
return { result, service: { action: task.action } };
|
|
216
|
+
}
|
|
217
|
+
async function executeDeliver(input, request) {
|
|
218
|
+
if (input.upstream === undefined)
|
|
219
|
+
throw new Error("upstream execution port is unavailable");
|
|
220
|
+
const served = await input.upstream.deliver({
|
|
221
|
+
repoRoot: request.repoRoot,
|
|
222
|
+
contractId: request.contractId,
|
|
223
|
+
...(request.message === undefined ? {} : { message: request.message }),
|
|
224
|
+
includeDirty: request.includeDirty,
|
|
225
|
+
materializeConflict: request.materializeConflict,
|
|
226
|
+
requester: request.requester,
|
|
227
|
+
signal: input.signal,
|
|
228
|
+
});
|
|
229
|
+
return {
|
|
230
|
+
result: served.result,
|
|
231
|
+
...(served.deliveryFactId === undefined ? {} : {
|
|
232
|
+
service: {
|
|
233
|
+
action: request.action,
|
|
234
|
+
repoRoot: request.repoRoot,
|
|
235
|
+
contractId: request.contractId,
|
|
236
|
+
deliveryFactId: served.deliveryFactId,
|
|
237
|
+
},
|
|
238
|
+
}),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function executeReview(input, request) {
|
|
242
|
+
if (input.upstream === undefined)
|
|
243
|
+
throw new Error("upstream execution port is unavailable");
|
|
244
|
+
const served = await input.upstream.review({
|
|
245
|
+
repoRoot: request.repoRoot,
|
|
246
|
+
contractId: request.contractId,
|
|
247
|
+
verdict: request.verdict,
|
|
248
|
+
...(request.summary === undefined ? {} : { summary: request.summary }),
|
|
249
|
+
requester: request.requester,
|
|
250
|
+
signal: input.signal,
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
result: served.result,
|
|
254
|
+
...(served.reviewFactId === undefined ? {} : {
|
|
255
|
+
service: {
|
|
256
|
+
action: request.action,
|
|
257
|
+
repoRoot: request.repoRoot,
|
|
258
|
+
contractId: request.contractId,
|
|
259
|
+
reviewFactId: served.reviewFactId,
|
|
260
|
+
},
|
|
261
|
+
}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function pendingService(request) {
|
|
265
|
+
if (request.action === "akuma.wait")
|
|
266
|
+
return { action: request.action };
|
|
267
|
+
if (request.action === "akuma.tell") {
|
|
268
|
+
return { action: request.action, target: request.target, tellId: request.id };
|
|
269
|
+
}
|
|
270
|
+
return request.action === "akuma.kill" ? { action: request.action, results: [] } : undefined;
|
|
271
|
+
}
|
|
272
|
+
async function serveUpstream(input) {
|
|
273
|
+
input.signal.throwIfAborted();
|
|
274
|
+
let fact = await admitRequest(input.paths, {
|
|
275
|
+
...input.claim,
|
|
276
|
+
admittedAt: input.now(),
|
|
277
|
+
});
|
|
278
|
+
const existing = receiptFor(fact);
|
|
279
|
+
if (existing !== null) {
|
|
280
|
+
await projectReceipt(input.directory, fact);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (fact.state === "served") {
|
|
284
|
+
await projectReceipt(input.directory, fact, {
|
|
285
|
+
kind: "failed",
|
|
286
|
+
failure: {
|
|
287
|
+
kind: "failed",
|
|
288
|
+
diagnostic: "served request receipt is no longer available",
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!input.admissionOpen()) {
|
|
294
|
+
fact = await voidRequest(input.paths, input.claim.id, "body closed request admission");
|
|
295
|
+
await projectReceipt(input.directory, fact);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (fact.state !== "admitted" || fact.action === "akuma.call") {
|
|
299
|
+
throw new Error(`Akuma request ${fact.id} cannot be served from ${fact.state}`);
|
|
300
|
+
}
|
|
301
|
+
const request = fact;
|
|
302
|
+
let outcome;
|
|
303
|
+
let service = pendingService(request);
|
|
304
|
+
try {
|
|
305
|
+
const served = await execute(input, request);
|
|
306
|
+
outcome = { kind: "returned", result: served.result };
|
|
307
|
+
if (served.service !== undefined)
|
|
308
|
+
service = served.service;
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
outcome = { kind: "failed", failure: failure(error) };
|
|
312
|
+
fact = await voidRequest(input.paths, request.id, "body failed before serving the request");
|
|
313
|
+
await projectReceipt(input.directory, fact);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
fact = service === undefined
|
|
317
|
+
? await voidRequest(input.paths, request.id, "body completed without a durable Contract fact reference")
|
|
318
|
+
: await serveUpstreamRequest(input.paths, request.id, service);
|
|
319
|
+
await projectReceipt(input.directory, fact, outcome);
|
|
320
|
+
}
|
|
321
|
+
async function serve(input) {
|
|
322
|
+
return input.claim.action === "akuma.call"
|
|
323
|
+
? await serveCall({ ...input, claim: input.claim })
|
|
324
|
+
: await serveUpstream({ ...input, claim: input.claim });
|
|
325
|
+
}
|
|
326
|
+
async function requestFiles(directory) {
|
|
327
|
+
try {
|
|
328
|
+
return (await readdir(directory))
|
|
329
|
+
.filter((name) => name.endsWith(".request.json"))
|
|
330
|
+
.sort();
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
if (error.code === "ENOENT")
|
|
334
|
+
return [];
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
export class BodyRequestPump {
|
|
339
|
+
input;
|
|
340
|
+
directory;
|
|
341
|
+
failure;
|
|
342
|
+
closeSignal = new AbortController();
|
|
343
|
+
executionSignal = new AbortController();
|
|
344
|
+
handled = new Set();
|
|
345
|
+
running;
|
|
346
|
+
cancelBody;
|
|
347
|
+
admissionClosed = false;
|
|
348
|
+
constructor(input) {
|
|
349
|
+
this.input = input;
|
|
350
|
+
this.directory = join(input.paths.directory, "requests", String(input.bodySequence));
|
|
351
|
+
this.cancelBody = () => {
|
|
352
|
+
this.stopAdmission();
|
|
353
|
+
if (!this.executionSignal.signal.aborted) {
|
|
354
|
+
this.executionSignal.abort(input.signal.reason);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
input.signal.addEventListener("abort", this.cancelBody, { once: true });
|
|
358
|
+
if (input.signal.aborted)
|
|
359
|
+
this.cancelBody();
|
|
360
|
+
this.running = this.run();
|
|
361
|
+
this.failure = this.running.then(() => new Promise(() => { }), (error) => Promise.reject(error));
|
|
362
|
+
}
|
|
363
|
+
static async open(input) {
|
|
364
|
+
await mkdir(join(input.paths.directory, "requests", String(input.bodySequence)), { recursive: true });
|
|
365
|
+
return new BodyRequestPump(input);
|
|
366
|
+
}
|
|
367
|
+
async run() {
|
|
368
|
+
while (!this.closeSignal.signal.aborted) {
|
|
369
|
+
for (const name of await requestFiles(this.directory)) {
|
|
370
|
+
if (this.closeSignal.signal.aborted)
|
|
371
|
+
return;
|
|
372
|
+
const id = name.slice(0, -".request.json".length);
|
|
373
|
+
if (this.handled.has(id))
|
|
374
|
+
continue;
|
|
375
|
+
const path = join(this.directory, name);
|
|
376
|
+
const claim = decodeClaim(await readFile(path, "utf8"), id);
|
|
377
|
+
if (claim === null) {
|
|
378
|
+
await rm(path, { force: true });
|
|
379
|
+
this.handled.add(id);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (!this.admissionClosed) {
|
|
383
|
+
await serve({
|
|
384
|
+
directory: this.directory,
|
|
385
|
+
claim,
|
|
386
|
+
...this.input,
|
|
387
|
+
signal: this.executionSignal.signal,
|
|
388
|
+
admissionOpen: () => !this.admissionClosed,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
this.handled.add(id);
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
await abortableDelay(POLL_MS, this.closeSignal.signal);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (this.closeSignal.signal.aborted)
|
|
398
|
+
return;
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async close() {
|
|
404
|
+
this.stopAdmission();
|
|
405
|
+
try {
|
|
406
|
+
await this.running;
|
|
407
|
+
}
|
|
408
|
+
finally {
|
|
409
|
+
this.input.signal.removeEventListener("abort", this.cancelBody);
|
|
410
|
+
await rm(this.directory, { recursive: true, force: true });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
stopAdmission() {
|
|
414
|
+
this.admissionClosed = true;
|
|
415
|
+
if (!this.closeSignal.signal.aborted) {
|
|
416
|
+
this.closeSignal.abort(new Error("Body request pump closed"));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
export async function clearBodyRequestTransport(paths) {
|
|
421
|
+
await rm(join(paths.directory, "requests"), { recursive: true, force: true });
|
|
422
|
+
}
|
|
423
|
+
function matchingRequestOrigin(soul, parent, id) {
|
|
424
|
+
return soul.origin.kind === "request"
|
|
425
|
+
&& soul.origin.parent === parent
|
|
426
|
+
&& soul.origin.requestId === id;
|
|
427
|
+
}
|
|
428
|
+
async function settleReservedSoul(paths, parent, request, soul) {
|
|
429
|
+
await (matchingRequestOrigin(soul, parent.id, request.id)
|
|
430
|
+
? serveRequest(paths, request.id, request.child)
|
|
431
|
+
: voidRequest(paths, request.id, "reserved child origin does not match the request"));
|
|
432
|
+
}
|
|
433
|
+
async function settleReserved(paths, parent, request, now, signal) {
|
|
434
|
+
const childPaths = pathsForAkuId(request.world, request.child);
|
|
435
|
+
const deadline = performance.now() + BIRTH_TIMEOUT_MS;
|
|
436
|
+
for (;;) {
|
|
437
|
+
signal?.throwIfAborted();
|
|
438
|
+
if (!await access(childPaths.directory).then(() => true, () => false)) {
|
|
439
|
+
await voidRequest(paths, request.id, "reserved child directory is absent");
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
const soul = await readSoul(childPaths);
|
|
443
|
+
if (soul !== null) {
|
|
444
|
+
await settleReservedSoul(paths, parent, request, soul);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
const leash = await HeldAkumaLeash.try(childPaths);
|
|
448
|
+
if (leash !== null) {
|
|
449
|
+
try {
|
|
450
|
+
const settled = await readSoul(childPaths);
|
|
451
|
+
if (settled !== null) {
|
|
452
|
+
await settleReservedSoul(paths, parent, request, settled);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
await leash.sealIfUnborn(childPaths, {
|
|
456
|
+
evidence: "request settlement",
|
|
457
|
+
at: now(),
|
|
458
|
+
});
|
|
459
|
+
await voidRequest(paths, request.id, "reserved child was sealed unborn");
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
finally {
|
|
463
|
+
leash.release();
|
|
464
|
+
}
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
if (performance.now() >= deadline)
|
|
468
|
+
return false;
|
|
469
|
+
await abortableDelay(POLL_MS, signal);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
export async function settleBodyRequests(paths, parent, now, signal) {
|
|
473
|
+
let pending = false;
|
|
474
|
+
for (const request of await readNonterminalRequests(paths)) {
|
|
475
|
+
signal?.throwIfAborted();
|
|
476
|
+
if (request.state === "admitted") {
|
|
477
|
+
await voidRequest(paths, request.id, "body died before serving the request");
|
|
478
|
+
}
|
|
479
|
+
else if (request.state === "reserved"
|
|
480
|
+
&& !await settleReserved(paths, parent, request, now, signal))
|
|
481
|
+
pending = true;
|
|
482
|
+
}
|
|
483
|
+
return pending ? "pending" : "settled";
|
|
484
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type AkuId } from "./identity.js";
|
|
2
|
+
import { decodeProviderOptions, type ReadonlyRestraint } from "./provider-recipe.js";
|
|
3
|
+
import { decodeProviderExecution } from "./providers/index.js";
|
|
4
|
+
import { type TaskMutationAction, type TaskMutationRequest } from "../task/mutation.js";
|
|
5
|
+
export type RequestRecipe = Readonly<{
|
|
6
|
+
description?: string;
|
|
7
|
+
allowed: readonly string[];
|
|
8
|
+
provider: ReturnType<typeof decodeProviderExecution>;
|
|
9
|
+
options: ReturnType<typeof decodeProviderOptions>;
|
|
10
|
+
readonly?: ReadonlyRestraint;
|
|
11
|
+
}>;
|
|
12
|
+
export type CallRequestClaim = Readonly<{
|
|
13
|
+
id: string;
|
|
14
|
+
action: "akuma.call";
|
|
15
|
+
world: string;
|
|
16
|
+
archetype: string;
|
|
17
|
+
body: string;
|
|
18
|
+
cwd?: string;
|
|
19
|
+
recipe: RequestRecipe;
|
|
20
|
+
}>;
|
|
21
|
+
export type WaitRequestClaim = Readonly<{
|
|
22
|
+
id: string;
|
|
23
|
+
action: "akuma.wait";
|
|
24
|
+
targets: readonly AkuId[];
|
|
25
|
+
completion: "any" | "all";
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
}>;
|
|
28
|
+
export type TellRequestClaim = Readonly<{
|
|
29
|
+
id: string;
|
|
30
|
+
action: "akuma.tell";
|
|
31
|
+
target: AkuId;
|
|
32
|
+
body: string;
|
|
33
|
+
}>;
|
|
34
|
+
export type KillRequestClaim = Readonly<{
|
|
35
|
+
id: string;
|
|
36
|
+
action: "akuma.kill";
|
|
37
|
+
targets: readonly AkuId[];
|
|
38
|
+
}>;
|
|
39
|
+
export type DeliverRequestClaim = Readonly<{
|
|
40
|
+
id: string;
|
|
41
|
+
action: "contract.deliver";
|
|
42
|
+
repoRoot: string;
|
|
43
|
+
contractId: string;
|
|
44
|
+
message?: string;
|
|
45
|
+
includeDirty: boolean;
|
|
46
|
+
materializeConflict: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
export type ReviewRequestClaim = Readonly<{
|
|
49
|
+
id: string;
|
|
50
|
+
action: "contract.review";
|
|
51
|
+
repoRoot: string;
|
|
52
|
+
contractId: string;
|
|
53
|
+
verdict: "satisfied" | "unsatisfied";
|
|
54
|
+
summary?: string;
|
|
55
|
+
}>;
|
|
56
|
+
export type TaskRequestClaim = Readonly<{
|
|
57
|
+
id: string;
|
|
58
|
+
action: TaskMutationAction;
|
|
59
|
+
world: string;
|
|
60
|
+
request: TaskMutationRequest;
|
|
61
|
+
}>;
|
|
62
|
+
export type RequestClaim = CallRequestClaim | WaitRequestClaim | TellRequestClaim | KillRequestClaim | DeliverRequestClaim | ReviewRequestClaim | TaskRequestClaim;
|
|
63
|
+
export type StructuralRequestClaim = Omit<CallRequestClaim, "recipe"> & Readonly<{
|
|
64
|
+
recipe: unknown;
|
|
65
|
+
}> | Exclude<RequestClaim, CallRequestClaim>;
|
|
66
|
+
export type UpstreamRequestFailure = Readonly<{
|
|
67
|
+
kind: "akuma-not-born";
|
|
68
|
+
id: AkuId;
|
|
69
|
+
}> | Readonly<{
|
|
70
|
+
kind: "failed";
|
|
71
|
+
diagnostic: string;
|
|
72
|
+
}>;
|
|
73
|
+
export type UpstreamRequestOutcome = Readonly<{
|
|
74
|
+
kind: "returned";
|
|
75
|
+
result: unknown;
|
|
76
|
+
}> | Readonly<{
|
|
77
|
+
kind: "failed";
|
|
78
|
+
failure: UpstreamRequestFailure;
|
|
79
|
+
}>;
|
|
80
|
+
export type ForwardedDeliveryReference = Readonly<{
|
|
81
|
+
kind: "accepted-reference";
|
|
82
|
+
repoRoot: string;
|
|
83
|
+
contractId: string;
|
|
84
|
+
deliveryFactId: string;
|
|
85
|
+
}>;
|
|
86
|
+
export type ForwardedReviewReference = Readonly<{
|
|
87
|
+
kind: "accepted-reference";
|
|
88
|
+
repoRoot: string;
|
|
89
|
+
contractId: string;
|
|
90
|
+
reviewFactId: string;
|
|
91
|
+
}>;
|
|
92
|
+
export type ForwardedTaskReference = Readonly<{
|
|
93
|
+
kind: "served-reference";
|
|
94
|
+
action: TaskMutationAction;
|
|
95
|
+
}>;
|
|
96
|
+
type ReceiptBase<A extends RequestClaim["action"], S extends string> = Readonly<{
|
|
97
|
+
id: string;
|
|
98
|
+
action: A;
|
|
99
|
+
state: S;
|
|
100
|
+
}>;
|
|
101
|
+
export type RequestReceipt = (ReceiptBase<"akuma.call", "served"> & Readonly<{
|
|
102
|
+
child: AkuId;
|
|
103
|
+
}>) | (ReceiptBase<"contract.deliver", "served"> & Readonly<{
|
|
104
|
+
reference: ForwardedDeliveryReference;
|
|
105
|
+
}>) | (ReceiptBase<"contract.review", "served"> & Readonly<{
|
|
106
|
+
reference: ForwardedReviewReference;
|
|
107
|
+
}>) | (ReceiptBase<TaskMutationAction, "served"> & Readonly<{
|
|
108
|
+
reference: ForwardedTaskReference;
|
|
109
|
+
}>) | (ReceiptBase<Exclude<RequestClaim["action"], "akuma.call">, "served"> & Readonly<{
|
|
110
|
+
outcome: UpstreamRequestOutcome;
|
|
111
|
+
}>) | (ReceiptBase<RequestClaim["action"], "refused"> & Readonly<{
|
|
112
|
+
diagnostic: string;
|
|
113
|
+
}>) | (ReceiptBase<RequestClaim["action"], "voided"> & Readonly<{
|
|
114
|
+
evidence: string;
|
|
115
|
+
}>);
|
|
116
|
+
export declare function absolute(value: unknown): value is string;
|
|
117
|
+
export declare function canonicalAkuId(value: unknown): AkuId | null;
|
|
118
|
+
export declare function decodeRecipe(value: unknown): RequestRecipe | null;
|
|
119
|
+
export declare function decodeClaim(bytes: string, fileId: string): StructuralRequestClaim | null;
|
|
120
|
+
export declare function decodeReceipt(bytes: string, id: string, action: RequestClaim["action"]): RequestReceipt | null;
|
|
121
|
+
export declare function requestPayload(claim: RequestClaim): unknown;
|
|
122
|
+
export declare function requestPath(directory: string, id: string): string;
|
|
123
|
+
export declare function receiptPath(directory: string, id: string): string;
|
|
124
|
+
export declare function atomicJson(path: string, value: unknown): Promise<void>;
|
|
125
|
+
export {};
|