@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
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl,
|
|
1
|
+
import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, } from "./rows.js";
|
|
2
2
|
import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingTellFacts, tellFact, tellIdsForFence, } from "./tells.js";
|
|
3
|
-
import { activityFactSlice, pruneActivityFacts, } from "./timeline.js";
|
|
3
|
+
import { activityFactSlice, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
|
|
4
4
|
import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart } from "./storage.js";
|
|
5
5
|
import { soulFact } from "./soul.js";
|
|
6
|
-
import { clipAllowedActions } from "../allowed.js";
|
|
7
6
|
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
|
|
7
|
+
export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
8
8
|
export { life, lifeAt } from "./facts.js";
|
|
9
9
|
const ACTIVITY_LIMIT = 5_000;
|
|
10
10
|
export async function readSoul(paths) {
|
|
@@ -89,137 +89,6 @@ export async function recordTellReceipt(paths, input) {
|
|
|
89
89
|
pruneActivityFacts(heart, ACTIVITY_LIMIT);
|
|
90
90
|
}));
|
|
91
91
|
}
|
|
92
|
-
function sameRequestInput(fact, input) {
|
|
93
|
-
if (fact.id !== input.id || fact.action !== input.action)
|
|
94
|
-
return false;
|
|
95
|
-
if (fact.action === "akuma.call" && input.action === "akuma.call") {
|
|
96
|
-
return fact.archetype === input.archetype
|
|
97
|
-
&& fact.body === input.body
|
|
98
|
-
&& fact.cwd === input.cwd
|
|
99
|
-
&& fact.world === input.world
|
|
100
|
-
&& JSON.stringify(fact.recipe) === JSON.stringify(input.recipe);
|
|
101
|
-
}
|
|
102
|
-
if (fact.action === "akuma.wait" && input.action === "akuma.wait") {
|
|
103
|
-
return fact.completion === input.completion
|
|
104
|
-
&& fact.timeoutMs === input.timeoutMs
|
|
105
|
-
&& JSON.stringify(fact.targets) === JSON.stringify(input.targets);
|
|
106
|
-
}
|
|
107
|
-
if (fact.action === "akuma.tell" && input.action === "akuma.tell") {
|
|
108
|
-
return fact.target === input.target && fact.body === input.body;
|
|
109
|
-
}
|
|
110
|
-
return fact.action === "akuma.kill" && input.action === "akuma.kill"
|
|
111
|
-
&& JSON.stringify(fact.targets) === JSON.stringify(input.targets);
|
|
112
|
-
}
|
|
113
|
-
export async function admitRequest(paths, input) {
|
|
114
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
115
|
-
const soul = soulFact(heart);
|
|
116
|
-
if (soul === null)
|
|
117
|
-
throw new Error("Akuma request admission requires a born Soul");
|
|
118
|
-
const action = input.action;
|
|
119
|
-
const parentAllowed = soul.allowed;
|
|
120
|
-
const permitted = action === "akuma.wait" || parentAllowed.includes(action);
|
|
121
|
-
const normalized = input.action === "akuma.call"
|
|
122
|
-
? {
|
|
123
|
-
...input,
|
|
124
|
-
recipe: {
|
|
125
|
-
...input.recipe,
|
|
126
|
-
allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
|
|
127
|
-
},
|
|
128
|
-
}
|
|
129
|
-
: input;
|
|
130
|
-
insertRequestFact(heart, {
|
|
131
|
-
...normalized,
|
|
132
|
-
requester: soul.id,
|
|
133
|
-
admittedAt: input.admittedAt,
|
|
134
|
-
...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
|
|
135
|
-
});
|
|
136
|
-
const fact = requestFact(heart, input.id);
|
|
137
|
-
if (fact === null)
|
|
138
|
-
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
139
|
-
if (fact.requester !== soul.id || !sameRequestInput(fact, normalized)) {
|
|
140
|
-
throw new Error(`Akuma request ${input.id} reused different input`);
|
|
141
|
-
}
|
|
142
|
-
return fact;
|
|
143
|
-
}));
|
|
144
|
-
}
|
|
145
|
-
export async function reserveRequest(paths, id, child) {
|
|
146
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
147
|
-
const before = requestFact(heart, id);
|
|
148
|
-
if (before === null)
|
|
149
|
-
throw new Error(`unknown Akuma request ${id}`);
|
|
150
|
-
if (before.action !== "akuma.call")
|
|
151
|
-
throw new Error(`Akuma request ${id} cannot reserve a child`);
|
|
152
|
-
if (before.state === "admitted")
|
|
153
|
-
updateRequestReserved(heart, id, child);
|
|
154
|
-
else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
|
|
155
|
-
throw new Error(`Akuma request ${id} cannot reserve ${child}`);
|
|
156
|
-
}
|
|
157
|
-
return requestFact(heart, id);
|
|
158
|
-
}));
|
|
159
|
-
}
|
|
160
|
-
export async function serveRequest(paths, id, child) {
|
|
161
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
162
|
-
const before = requestFact(heart, id);
|
|
163
|
-
if (before === null)
|
|
164
|
-
throw new Error(`unknown Akuma request ${id}`);
|
|
165
|
-
if (before.action !== "akuma.call")
|
|
166
|
-
throw new Error(`Akuma request ${id} cannot serve a child`);
|
|
167
|
-
if (before.state === "reserved" && before.child === child)
|
|
168
|
-
updateRequestServed(heart, id, child);
|
|
169
|
-
else if (before.state !== "served" || before.child !== child) {
|
|
170
|
-
throw new Error(`Akuma request ${id} cannot serve ${child}`);
|
|
171
|
-
}
|
|
172
|
-
return requestFact(heart, id);
|
|
173
|
-
}));
|
|
174
|
-
}
|
|
175
|
-
export async function serveUpstreamRequest(paths, id, service) {
|
|
176
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
177
|
-
const before = requestFact(heart, id);
|
|
178
|
-
if (before === null)
|
|
179
|
-
throw new Error(`unknown Akuma request ${id}`);
|
|
180
|
-
if (before.action === "akuma.call" || service.action !== before.action) {
|
|
181
|
-
throw new Error(`Akuma request ${id} has a mismatched service reference`);
|
|
182
|
-
}
|
|
183
|
-
if (before.state === "admitted")
|
|
184
|
-
updateUpstreamRequestServed(heart, id, service);
|
|
185
|
-
else if (before.state !== "served" || JSON.stringify(before.service) !== JSON.stringify(service)) {
|
|
186
|
-
throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
|
|
187
|
-
}
|
|
188
|
-
return requestFact(heart, id);
|
|
189
|
-
}));
|
|
190
|
-
}
|
|
191
|
-
export async function refuseRequest(paths, id, diagnostic) {
|
|
192
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
193
|
-
const before = requestFact(heart, id);
|
|
194
|
-
if (before === null)
|
|
195
|
-
throw new Error(`unknown Akuma request ${id}`);
|
|
196
|
-
if (before.state === "admitted")
|
|
197
|
-
updateRequestRefused(heart, id, diagnostic);
|
|
198
|
-
else if (before.state !== "refused" || before.diagnostic !== diagnostic) {
|
|
199
|
-
throw new Error(`Akuma request ${id} cannot be refused`);
|
|
200
|
-
}
|
|
201
|
-
return requestFact(heart, id);
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
export async function voidRequest(paths, id, evidence) {
|
|
205
|
-
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
206
|
-
const before = requestFact(heart, id);
|
|
207
|
-
if (before === null)
|
|
208
|
-
throw new Error(`unknown Akuma request ${id}`);
|
|
209
|
-
if (before.state === "admitted" || before.state === "reserved")
|
|
210
|
-
updateRequestVoided(heart, id, evidence);
|
|
211
|
-
else if (before.state !== "voided" || before.evidence !== evidence) {
|
|
212
|
-
throw new Error(`Akuma request ${id} cannot be voided`);
|
|
213
|
-
}
|
|
214
|
-
return requestFact(heart, id);
|
|
215
|
-
}));
|
|
216
|
-
}
|
|
217
|
-
export async function readRequest(paths, id) {
|
|
218
|
-
return await withHeart(paths, (heart) => requestFact(heart, id));
|
|
219
|
-
}
|
|
220
|
-
export async function readNonterminalRequests(paths) {
|
|
221
|
-
return await withHeart(paths, nonterminalRequestFacts);
|
|
222
|
-
}
|
|
223
92
|
export async function readKill(paths, bodySequence) {
|
|
224
93
|
return await withHeart(paths, (heart) => killFactForBody(heart, bodySequence));
|
|
225
94
|
}
|
|
@@ -303,11 +172,21 @@ export async function readHeart(paths) {
|
|
|
303
172
|
latestKill: latestKillFact(heart),
|
|
304
173
|
stop: stopFact(heart),
|
|
305
174
|
pause: pauseFact(heart),
|
|
175
|
+
lastActivityAt: readLastActivityAt(heart),
|
|
306
176
|
})));
|
|
307
177
|
}
|
|
308
178
|
catch (error) {
|
|
309
179
|
if (isHeartAbsent(error)) {
|
|
310
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
soul: null,
|
|
182
|
+
latestBody: null,
|
|
183
|
+
latestSession: null,
|
|
184
|
+
pending: [],
|
|
185
|
+
latestKill: null,
|
|
186
|
+
stop: null,
|
|
187
|
+
pause: null,
|
|
188
|
+
lastActivityAt: null,
|
|
189
|
+
};
|
|
311
190
|
}
|
|
312
191
|
throw error;
|
|
313
192
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AkuId, AkumaPaths } from "../identity.js";
|
|
2
|
+
import type { RequestFact, RequestInput, UpstreamRequestService } from "./facts.js";
|
|
3
|
+
export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
|
|
4
|
+
admittedAt: string;
|
|
5
|
+
}>): Promise<RequestFact>;
|
|
6
|
+
export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
7
|
+
export declare function serveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
8
|
+
export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, service: UpstreamRequestService): Promise<RequestFact>;
|
|
9
|
+
export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
|
|
10
|
+
export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
|
|
11
|
+
export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
|
|
12
|
+
export declare function readNonterminalRequests(paths: AkumaPaths): Promise<readonly RequestFact[]>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { insertRequestFact, nonterminalRequestFacts, requestFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./request-rows.js";
|
|
2
|
+
import { transaction, withHeart } from "./storage.js";
|
|
3
|
+
import { soulFact } from "./soul.js";
|
|
4
|
+
import { clipAllowedActions } from "../allowed.js";
|
|
5
|
+
import { requestPayloadJson } from "./request-rows.js";
|
|
6
|
+
export async function admitRequest(paths, input) {
|
|
7
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
8
|
+
const soul = soulFact(heart);
|
|
9
|
+
if (soul === null)
|
|
10
|
+
throw new Error("Akuma request admission requires a born Soul");
|
|
11
|
+
const action = input.action;
|
|
12
|
+
const parentAllowed = soul.allowed;
|
|
13
|
+
const permitted = action === "akuma.wait" || parentAllowed.includes(action);
|
|
14
|
+
const normalized = input.action === "akuma.call"
|
|
15
|
+
? {
|
|
16
|
+
...input,
|
|
17
|
+
recipe: {
|
|
18
|
+
...input.recipe,
|
|
19
|
+
allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
: input;
|
|
23
|
+
insertRequestFact(heart, {
|
|
24
|
+
...normalized,
|
|
25
|
+
requester: soul.id,
|
|
26
|
+
admittedAt: input.admittedAt,
|
|
27
|
+
...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
|
|
28
|
+
});
|
|
29
|
+
const fact = requestFact(heart, input.id);
|
|
30
|
+
if (fact === null)
|
|
31
|
+
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
32
|
+
if (fact.requester !== soul.id
|
|
33
|
+
|| fact.id !== normalized.id
|
|
34
|
+
|| fact.action !== normalized.action
|
|
35
|
+
|| requestPayloadJson(fact) !== requestPayloadJson(normalized)) {
|
|
36
|
+
throw new Error(`Akuma request ${input.id} reused different input`);
|
|
37
|
+
}
|
|
38
|
+
return fact;
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
export async function reserveRequest(paths, id, child) {
|
|
42
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
43
|
+
const before = requestFact(heart, id);
|
|
44
|
+
if (before === null)
|
|
45
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
46
|
+
if (before.action !== "akuma.call")
|
|
47
|
+
throw new Error(`Akuma request ${id} cannot reserve a child`);
|
|
48
|
+
if (before.state === "admitted")
|
|
49
|
+
updateRequestReserved(heart, id, child);
|
|
50
|
+
else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
|
|
51
|
+
throw new Error(`Akuma request ${id} cannot reserve ${child}`);
|
|
52
|
+
}
|
|
53
|
+
return requestFact(heart, id);
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
export async function serveRequest(paths, id, child) {
|
|
57
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
58
|
+
const before = requestFact(heart, id);
|
|
59
|
+
if (before === null)
|
|
60
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
61
|
+
if (before.action !== "akuma.call")
|
|
62
|
+
throw new Error(`Akuma request ${id} cannot serve a child`);
|
|
63
|
+
if (before.state === "reserved" && before.child === child)
|
|
64
|
+
updateRequestServed(heart, id, child);
|
|
65
|
+
else if (before.state !== "served" || before.child !== child) {
|
|
66
|
+
throw new Error(`Akuma request ${id} cannot serve ${child}`);
|
|
67
|
+
}
|
|
68
|
+
return requestFact(heart, id);
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
export async function serveUpstreamRequest(paths, id, service) {
|
|
72
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
73
|
+
const before = requestFact(heart, id);
|
|
74
|
+
if (before === null)
|
|
75
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
76
|
+
if (before.action === "akuma.call" || service.action !== before.action) {
|
|
77
|
+
throw new Error(`Akuma request ${id} has a mismatched service reference`);
|
|
78
|
+
}
|
|
79
|
+
if (before.state === "admitted")
|
|
80
|
+
updateUpstreamRequestServed(heart, id, service);
|
|
81
|
+
else if (before.state !== "served") {
|
|
82
|
+
throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
|
|
83
|
+
}
|
|
84
|
+
return requestFact(heart, id);
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
export async function refuseRequest(paths, id, diagnostic) {
|
|
88
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
89
|
+
const before = requestFact(heart, id);
|
|
90
|
+
if (before === null)
|
|
91
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
92
|
+
if (before.state === "admitted")
|
|
93
|
+
updateRequestRefused(heart, id, diagnostic);
|
|
94
|
+
else if (before.state !== "refused" || before.diagnostic !== diagnostic) {
|
|
95
|
+
throw new Error(`Akuma request ${id} cannot be refused`);
|
|
96
|
+
}
|
|
97
|
+
return requestFact(heart, id);
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
export async function voidRequest(paths, id, evidence) {
|
|
101
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
102
|
+
const before = requestFact(heart, id);
|
|
103
|
+
if (before === null)
|
|
104
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
105
|
+
if (before.state === "admitted" || before.state === "reserved") {
|
|
106
|
+
updateRequestVoided(heart, id, evidence);
|
|
107
|
+
}
|
|
108
|
+
else if (before.state !== "voided" || before.evidence !== evidence) {
|
|
109
|
+
throw new Error(`Akuma request ${id} cannot be voided`);
|
|
110
|
+
}
|
|
111
|
+
return requestFact(heart, id);
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
export async function readRequest(paths, id) {
|
|
115
|
+
return await withHeart(paths, (heart) => requestFact(heart, id));
|
|
116
|
+
}
|
|
117
|
+
export async function readNonterminalRequests(paths) {
|
|
118
|
+
return await withHeart(paths, nonterminalRequestFacts);
|
|
119
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { AkuId } from "../identity.js";
|
|
3
|
+
import type { RequestFact, RequestInput } from "./facts.js";
|
|
4
|
+
export declare function requestPayloadJson(input: RequestInput): string;
|
|
5
|
+
export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
|
|
6
|
+
requester: AkuId;
|
|
7
|
+
admittedAt: string;
|
|
8
|
+
refusal?: string;
|
|
9
|
+
}>): void;
|
|
10
|
+
export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
|
|
11
|
+
export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
|
|
12
|
+
export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
|
|
13
|
+
export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
|
|
14
|
+
export declare function updateUpstreamRequestServed(database: DatabaseSync, id: string, service: unknown): void;
|
|
15
|
+
export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
|
|
16
|
+
export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isTaskMutationAction } from "../../task/mutation.js";
|
|
2
|
+
function json(value) {
|
|
3
|
+
return JSON.stringify(value);
|
|
4
|
+
}
|
|
5
|
+
function parsed(value) {
|
|
6
|
+
if (typeof value !== "string")
|
|
7
|
+
throw new Error("Akuma authority contains non-text JSON");
|
|
8
|
+
return JSON.parse(value);
|
|
9
|
+
}
|
|
10
|
+
export function requestPayloadJson(input) {
|
|
11
|
+
const { id: _id, action: _action, requester: _requester, admittedAt: _admittedAt, state: _state, child: _child, service: _service, diagnostic: _diagnostic, evidence: _evidence, refusal: _refusal, ...payload } = input;
|
|
12
|
+
return json(payload);
|
|
13
|
+
}
|
|
14
|
+
function decodeRequestRow(row) {
|
|
15
|
+
const knownAction = [
|
|
16
|
+
"akuma.call", "akuma.wait", "akuma.tell", "akuma.kill", "contract.deliver", "contract.review",
|
|
17
|
+
].includes(row.action) || isTaskMutationAction(row.action);
|
|
18
|
+
if (!knownAction)
|
|
19
|
+
throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
|
|
20
|
+
const payload = parsed(row.payload_json);
|
|
21
|
+
const input = {
|
|
22
|
+
id: row.id,
|
|
23
|
+
requester: row.requester,
|
|
24
|
+
action: row.action,
|
|
25
|
+
...payload,
|
|
26
|
+
admittedAt: row.admitted_at,
|
|
27
|
+
};
|
|
28
|
+
if (row.state === "reserved") {
|
|
29
|
+
if (input.action !== "akuma.call" || row.child === null) {
|
|
30
|
+
throw new Error("Akuma authority contains an invalid reserved request");
|
|
31
|
+
}
|
|
32
|
+
return { ...input, state: row.state, child: row.child };
|
|
33
|
+
}
|
|
34
|
+
if (row.state === "served") {
|
|
35
|
+
if (input.action === "akuma.call") {
|
|
36
|
+
if (row.child === null)
|
|
37
|
+
throw new Error("Akuma authority contains a served call without a child");
|
|
38
|
+
return { ...input, state: row.state, child: row.child };
|
|
39
|
+
}
|
|
40
|
+
if (row.service_json === null) {
|
|
41
|
+
throw new Error("Akuma authority contains a served request without a service reference");
|
|
42
|
+
}
|
|
43
|
+
const service = parsed(row.service_json);
|
|
44
|
+
return { ...input, state: row.state, service };
|
|
45
|
+
}
|
|
46
|
+
if (row.state === "refused")
|
|
47
|
+
return { ...input, state: row.state, diagnostic: row.diagnostic };
|
|
48
|
+
if (row.state === "voided")
|
|
49
|
+
return { ...input, state: row.state, evidence: row.evidence };
|
|
50
|
+
return { ...input, state: "admitted" };
|
|
51
|
+
}
|
|
52
|
+
const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted_at,
|
|
53
|
+
state, child, service_json, diagnostic, evidence`;
|
|
54
|
+
export function insertRequestFact(database, input) {
|
|
55
|
+
const { id, action, requester, admittedAt, refusal } = input;
|
|
56
|
+
database.prepare(`INSERT OR IGNORE INTO requests(
|
|
57
|
+
id, requester, action, payload_json, admitted_at, state, diagnostic
|
|
58
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(id, requester, action, requestPayloadJson(input), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
|
|
59
|
+
}
|
|
60
|
+
export function requestFact(database, id) {
|
|
61
|
+
const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
|
|
62
|
+
return row === undefined ? null : decodeRequestRow(row);
|
|
63
|
+
}
|
|
64
|
+
export function nonterminalRequestFacts(database) {
|
|
65
|
+
const rows = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
|
|
66
|
+
WHERE state IN ('admitted', 'reserved') ORDER BY sequence`).all();
|
|
67
|
+
return rows.map(decodeRequestRow);
|
|
68
|
+
}
|
|
69
|
+
export function updateRequestReserved(database, id, child) {
|
|
70
|
+
database.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
|
|
71
|
+
.run(child, id);
|
|
72
|
+
}
|
|
73
|
+
export function updateRequestServed(database, id, child) {
|
|
74
|
+
database.prepare(`UPDATE requests SET state = 'served', child = ?
|
|
75
|
+
WHERE id = ? AND state = 'reserved'`).run(child, id);
|
|
76
|
+
}
|
|
77
|
+
export function updateUpstreamRequestServed(database, id, service) {
|
|
78
|
+
database.prepare(`UPDATE requests SET state = 'served', service_json = ?
|
|
79
|
+
WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`).run(json(service), id);
|
|
80
|
+
}
|
|
81
|
+
export function updateRequestRefused(database, id, diagnostic) {
|
|
82
|
+
database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
|
|
83
|
+
WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
|
|
84
|
+
}
|
|
85
|
+
export function updateRequestVoided(database, id, evidence) {
|
|
86
|
+
database.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
|
|
87
|
+
WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
|
|
88
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
-
import type {
|
|
3
|
-
import type { BodyEnd, BodyFact, CallFact, KillFact, PauseFact, RequestFact, RequestInput, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
|
|
2
|
+
import type { BodyEnd, BodyFact, CallFact, KillFact, PauseFact, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
|
|
4
3
|
export type SealRow = Readonly<{
|
|
5
4
|
evidence: string;
|
|
6
5
|
at: string;
|
|
@@ -39,19 +38,6 @@ export type CallRow = Readonly<{
|
|
|
39
38
|
body: string;
|
|
40
39
|
at: string;
|
|
41
40
|
}>;
|
|
42
|
-
export type RequestRow = Readonly<{
|
|
43
|
-
sequence: number;
|
|
44
|
-
id: string;
|
|
45
|
-
requester: string;
|
|
46
|
-
action: string;
|
|
47
|
-
payload_json: string;
|
|
48
|
-
admitted_at: string;
|
|
49
|
-
state: RequestFact["state"];
|
|
50
|
-
child: string | null;
|
|
51
|
-
service_json: string | null;
|
|
52
|
-
diagnostic: string | null;
|
|
53
|
-
evidence: string | null;
|
|
54
|
-
}>;
|
|
55
41
|
export type ActivityRow = Readonly<{
|
|
56
42
|
sequence: number;
|
|
57
43
|
turn_sequence: number;
|
|
@@ -77,7 +63,6 @@ export declare function decodeSessionRow(row: SessionRow): SessionFact;
|
|
|
77
63
|
export declare function decodeBodyRow(row: BodyRow): BodyFact;
|
|
78
64
|
export declare function decodeTurnRow(row: TurnRow): TurnFact;
|
|
79
65
|
export declare function decodeCallRow(row: CallRow): CallFact;
|
|
80
|
-
export declare function decodeRequestRow(row: RequestRow): RequestFact;
|
|
81
66
|
export declare function encodeActivityEvent(event: unknown): string;
|
|
82
67
|
export declare function decodeActivityRow(row: ActivityRow): ActivityFact;
|
|
83
68
|
export declare function sealExists(database: DatabaseSync): boolean;
|
|
@@ -100,18 +85,6 @@ export declare function insertActivityFact(database: DatabaseSync, input: Readon
|
|
|
100
85
|
event: unknown;
|
|
101
86
|
at: string;
|
|
102
87
|
}>): number;
|
|
103
|
-
export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
|
|
104
|
-
requester: AkuId;
|
|
105
|
-
admittedAt: string;
|
|
106
|
-
refusal?: string;
|
|
107
|
-
}>): void;
|
|
108
|
-
export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
|
|
109
|
-
export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
|
|
110
|
-
export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
|
|
111
|
-
export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
|
|
112
|
-
export declare function updateUpstreamRequestServed(database: DatabaseSync, id: string, service: unknown): void;
|
|
113
|
-
export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
|
|
114
|
-
export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
|
|
115
88
|
export declare function insertStopControl(database: DatabaseSync, bodySequence: number, at: string): void;
|
|
116
89
|
export declare function insertPauseControl(database: DatabaseSync, at: string): void;
|
|
117
90
|
export declare function stopFact(database: DatabaseSync): StopFact | null;
|
|
@@ -7,66 +7,6 @@ function parsed(value) {
|
|
|
7
7
|
throw new Error("Akuma authority contains non-text JSON");
|
|
8
8
|
return JSON.parse(value);
|
|
9
9
|
}
|
|
10
|
-
function object(value) {
|
|
11
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
12
|
-
? value
|
|
13
|
-
: null;
|
|
14
|
-
}
|
|
15
|
-
function exactKeys(value, expected) {
|
|
16
|
-
const actual = Object.keys(value).sort();
|
|
17
|
-
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
18
|
-
}
|
|
19
|
-
const KILL_EVIDENCE = [
|
|
20
|
-
"already-killed",
|
|
21
|
-
"already-stopped",
|
|
22
|
-
"hung",
|
|
23
|
-
"killed",
|
|
24
|
-
"untidy",
|
|
25
|
-
"unavailable",
|
|
26
|
-
];
|
|
27
|
-
function decodeRequestService(value, input) {
|
|
28
|
-
const service = object(value);
|
|
29
|
-
if (service === null || service.action !== input.action) {
|
|
30
|
-
throw new Error("Akuma authority contains a mismatched request service reference");
|
|
31
|
-
}
|
|
32
|
-
if (input.action === "akuma.wait") {
|
|
33
|
-
if (!exactKeys(service, ["action"])) {
|
|
34
|
-
throw new Error("Akuma authority contains an invalid wait service reference");
|
|
35
|
-
}
|
|
36
|
-
return { action: input.action };
|
|
37
|
-
}
|
|
38
|
-
if (input.action === "akuma.tell") {
|
|
39
|
-
if (!exactKeys(service, ["action", "target", "tellId"])
|
|
40
|
-
|| service.target !== input.target
|
|
41
|
-
|| service.tellId !== input.id) {
|
|
42
|
-
throw new Error("Akuma authority contains an invalid tell service reference");
|
|
43
|
-
}
|
|
44
|
-
return { action: input.action, target: input.target, tellId: input.id };
|
|
45
|
-
}
|
|
46
|
-
if (!exactKeys(service, ["action", "results"]) || !Array.isArray(service.results)) {
|
|
47
|
-
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
48
|
-
}
|
|
49
|
-
let targetIndex = -1;
|
|
50
|
-
const results = service.results.map((value) => {
|
|
51
|
-
const result = object(value);
|
|
52
|
-
if (result === null
|
|
53
|
-
|| !exactKeys(result, ["evidence", "id"])
|
|
54
|
-
|| typeof result.id !== "string"
|
|
55
|
-
|| !KILL_EVIDENCE.includes(result.evidence)) {
|
|
56
|
-
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
57
|
-
}
|
|
58
|
-
const index = input.targets.indexOf(result.id);
|
|
59
|
-
if (index <= targetIndex) {
|
|
60
|
-
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
61
|
-
}
|
|
62
|
-
targetIndex = index;
|
|
63
|
-
return {
|
|
64
|
-
id: result.id,
|
|
65
|
-
evidence: result.evidence,
|
|
66
|
-
};
|
|
67
|
-
});
|
|
68
|
-
return { action: input.action, results };
|
|
69
|
-
}
|
|
70
10
|
function resumeCoordinate(value) {
|
|
71
11
|
const coordinate = decodeResumeCoordinate(value);
|
|
72
12
|
if (coordinate === null)
|
|
@@ -126,46 +66,6 @@ export function decodeTurnRow(row) {
|
|
|
126
66
|
export function decodeCallRow(row) {
|
|
127
67
|
return { kind: "call", sequence: row.sequence, turnSequence: row.turn_sequence, body: row.body, at: row.at };
|
|
128
68
|
}
|
|
129
|
-
export function decodeRequestRow(row) {
|
|
130
|
-
if (!["akuma.call", "akuma.wait", "akuma.tell", "akuma.kill"].includes(row.action)) {
|
|
131
|
-
throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
|
|
132
|
-
}
|
|
133
|
-
const payload = parsed(row.payload_json);
|
|
134
|
-
const input = {
|
|
135
|
-
id: row.id,
|
|
136
|
-
requester: row.requester,
|
|
137
|
-
action: row.action,
|
|
138
|
-
...payload,
|
|
139
|
-
admittedAt: row.admitted_at,
|
|
140
|
-
};
|
|
141
|
-
if (row.state === "reserved") {
|
|
142
|
-
if (input.action !== "akuma.call" || row.child === null) {
|
|
143
|
-
throw new Error("Akuma authority contains an invalid reserved request");
|
|
144
|
-
}
|
|
145
|
-
return { ...input, state: row.state, child: row.child };
|
|
146
|
-
}
|
|
147
|
-
if (row.state === "served") {
|
|
148
|
-
if (input.action === "akuma.call") {
|
|
149
|
-
if (row.child === null)
|
|
150
|
-
throw new Error("Akuma authority contains a served call without a child");
|
|
151
|
-
return { ...input, state: row.state, child: row.child };
|
|
152
|
-
}
|
|
153
|
-
if (row.service_json === null) {
|
|
154
|
-
throw new Error("Akuma authority contains a served request without a service reference");
|
|
155
|
-
}
|
|
156
|
-
const service = decodeRequestService(parsed(row.service_json), input);
|
|
157
|
-
return {
|
|
158
|
-
...input,
|
|
159
|
-
state: row.state,
|
|
160
|
-
service,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if (row.state === "refused")
|
|
164
|
-
return { ...input, state: row.state, diagnostic: row.diagnostic };
|
|
165
|
-
if (row.state === "voided")
|
|
166
|
-
return { ...input, state: row.state, evidence: row.evidence };
|
|
167
|
-
return { ...input, state: "admitted" };
|
|
168
|
-
}
|
|
169
69
|
export function encodeActivityEvent(event) {
|
|
170
70
|
return json(event);
|
|
171
71
|
}
|
|
@@ -196,6 +96,9 @@ export function sealFact(database) {
|
|
|
196
96
|
return row === undefined ? null : { evidence: row.evidence, at: row.at };
|
|
197
97
|
}
|
|
198
98
|
export function insertBodyFact(database, input) {
|
|
99
|
+
if (latestBodyFact(database)?.hung !== undefined) {
|
|
100
|
+
throw new Error("Akuma successor is permanently gated by hung custody");
|
|
101
|
+
}
|
|
199
102
|
const result = database.prepare("INSERT INTO bodies(leash_taken_at) VALUES (?)").run(input.leashTakenAt);
|
|
200
103
|
return Number(result.lastInsertRowid);
|
|
201
104
|
}
|
|
@@ -210,43 +113,6 @@ export function insertActivityFact(database, input) {
|
|
|
210
113
|
.run(sequence, input.turnSequence, encodeActivityEvent(input.event), input.at);
|
|
211
114
|
return sequence;
|
|
212
115
|
}
|
|
213
|
-
const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted_at,
|
|
214
|
-
state, child, service_json, diagnostic, evidence`;
|
|
215
|
-
export function insertRequestFact(database, input) {
|
|
216
|
-
const { id, action, requester, admittedAt, refusal, ...payload } = input;
|
|
217
|
-
database.prepare(`INSERT OR IGNORE INTO requests(
|
|
218
|
-
id, requester, action, payload_json, admitted_at, state, diagnostic
|
|
219
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(id, requester, action, json(payload), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
|
|
220
|
-
}
|
|
221
|
-
export function requestFact(database, id) {
|
|
222
|
-
const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
|
|
223
|
-
return row === undefined ? null : decodeRequestRow(row);
|
|
224
|
-
}
|
|
225
|
-
export function nonterminalRequestFacts(database) {
|
|
226
|
-
const rows = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
|
|
227
|
-
WHERE state IN ('admitted', 'reserved') ORDER BY sequence`).all();
|
|
228
|
-
return rows.map(decodeRequestRow);
|
|
229
|
-
}
|
|
230
|
-
export function updateRequestReserved(database, id, child) {
|
|
231
|
-
database.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
|
|
232
|
-
.run(child, id);
|
|
233
|
-
}
|
|
234
|
-
export function updateRequestServed(database, id, child) {
|
|
235
|
-
database.prepare(`UPDATE requests SET state = 'served', child = ?
|
|
236
|
-
WHERE id = ? AND state = 'reserved'`).run(child, id);
|
|
237
|
-
}
|
|
238
|
-
export function updateUpstreamRequestServed(database, id, service) {
|
|
239
|
-
database.prepare(`UPDATE requests SET state = 'served', service_json = ?
|
|
240
|
-
WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`).run(json(service), id);
|
|
241
|
-
}
|
|
242
|
-
export function updateRequestRefused(database, id, diagnostic) {
|
|
243
|
-
database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
|
|
244
|
-
WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
|
|
245
|
-
}
|
|
246
|
-
export function updateRequestVoided(database, id, evidence) {
|
|
247
|
-
database.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
|
|
248
|
-
WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
|
|
249
|
-
}
|
|
250
116
|
export function insertStopControl(database, bodySequence, at) {
|
|
251
117
|
database.prepare("INSERT OR IGNORE INTO control(kind, value_json, at) VALUES ('stop', ?, ?)")
|
|
252
118
|
.run(json({ bodySequence }), at);
|
|
@@ -288,7 +154,7 @@ export function endBodyFact(database, input) {
|
|
|
288
154
|
}
|
|
289
155
|
export function markBodyHung(database, input) {
|
|
290
156
|
const result = database.prepare(`UPDATE bodies SET hung_diagnostic = ?, hung_at = ?
|
|
291
|
-
WHERE sequence = ? AND
|
|
157
|
+
WHERE sequence = ? AND hung_diagnostic IS NULL`)
|
|
292
158
|
.run(input.diagnostic, input.at, input.sequence);
|
|
293
159
|
if (result.changes !== 1)
|
|
294
160
|
throw new Error(`Akuma Body ${input.sequence} cannot record hung custody`);
|