@astrosheep/keiyaku 4.0.6 → 4.1.1
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 +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
- package/build/src/akuma/akuma.d.ts +17 -14
- package/build/src/akuma/akuma.js +122 -114
- package/build/src/akuma/allowed.d.ts +1 -0
- package/build/src/akuma/allowed.js +6 -0
- package/build/src/akuma/archetype.js +12 -2
- package/build/src/akuma/body-turn.js +27 -15
- package/build/src/akuma/body.d.ts +44 -3
- package/build/src/akuma/body.js +142 -33
- package/build/src/akuma/coordinate.js +17 -7
- package/build/src/akuma/heart/facts.js +1 -1
- package/build/src/akuma/heart/index.d.ts +1 -1
- package/build/src/akuma/heart/index.js +2 -4
- package/build/src/akuma/heart/request-authority.js +4 -4
- package/build/src/akuma/heart/request-rows.js +27 -16
- package/build/src/akuma/heart/rows.js +79 -41
- package/build/src/akuma/heart/soul.js +1 -1
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +76 -11
- package/build/src/akuma/heart/tells.js +35 -18
- package/build/src/akuma/heart/timeline.js +19 -11
- package/build/src/akuma/identity.d.ts +1 -0
- package/build/src/akuma/identity.js +1 -0
- package/build/src/akuma/index.d.ts +3 -3
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/nuke.js +106 -14
- package/build/src/akuma/projection.d.ts +11 -9
- package/build/src/akuma/projection.js +110 -53
- package/build/src/akuma/provider-recipe.d.ts +2 -0
- package/build/src/akuma/provider-recipe.js +21 -5
- package/build/src/akuma/provider.js +105 -62
- package/build/src/akuma/providers/acp/config.d.ts +2 -0
- package/build/src/akuma/providers/acp/config.js +13 -6
- package/build/src/akuma/providers/acp/core.d.ts +5 -2
- package/build/src/akuma/providers/acp/core.js +45 -25
- package/build/src/akuma/providers/acp/events.js +38 -29
- package/build/src/akuma/providers/acp/index.js +18 -8
- package/build/src/akuma/providers/claude/events.js +8 -12
- package/build/src/akuma/providers/claude/index.d.ts +2 -1
- package/build/src/akuma/providers/claude/index.js +59 -26
- package/build/src/akuma/providers/claude/input.js +6 -2
- package/build/src/akuma/providers/codex-app-server/events.js +61 -36
- package/build/src/akuma/providers/codex-app-server/index.js +29 -14
- package/build/src/akuma/providers/grok-build/index.js +28 -17
- package/build/src/akuma/providers/index.js +3 -19
- package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
- package/build/src/akuma/providers/opencode-sdk/index.js +29 -17
- package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
- package/build/src/akuma/providers/pi/events.js +26 -19
- package/build/src/akuma/providers/pi/index.js +58 -38
- package/build/src/akuma/publication.js +9 -9
- package/build/src/akuma/request-serve.js +37 -40
- package/build/src/akuma/request-wire.d.ts +2 -2
- package/build/src/akuma/request-wire.js +75 -102
- package/build/src/akuma/requests.js +2 -2
- package/build/src/akuma-body.js +6 -5
- package/build/src/body/amend.d.ts +4 -1
- package/build/src/body/amend.js +14 -15
- package/build/src/body/decode.js +2 -4
- package/build/src/body/envelope.js +2 -4
- package/build/src/body/region.d.ts +1 -2
- package/build/src/body/region.js +4 -72
- package/build/src/body/render.js +17 -12
- package/build/src/body/shape.js +1 -7
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +10 -1
- package/build/src/cli/commands/akuma-invoke.js +18 -6
- package/build/src/cli/commands/akuma.js +10 -13
- package/build/src/cli/commands/amend.d.ts +2 -2
- package/build/src/cli/commands/amend.js +2 -4
- package/build/src/cli/commands/bind.d.ts +4 -4
- package/build/src/cli/commands/bind.js +16 -6
- package/build/src/cli/commands/contract.d.ts +99 -10
- package/build/src/cli/commands/contract.js +128 -24
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +23 -15
- package/build/src/cli/commands/task-invoke.d.ts +4 -1
- package/build/src/cli/commands/task-invoke.js +191 -68
- package/build/src/cli/commands/task-query.js +48 -24
- package/build/src/cli/commands/task.js +150 -30
- package/build/src/cli/coordinates.js +2 -4
- package/build/src/cli/invoke.js +88 -44
- package/build/src/cli/main.js +4 -2
- package/build/src/cli/parse.d.ts +2 -89
- package/build/src/cli/parse.js +87 -79
- package/build/src/cli/render/akuma-tool-command.js +6 -7
- package/build/src/cli/render/akuma-tool.js +10 -8
- package/build/src/cli/render/akuma.js +149 -53
- package/build/src/cli/render/audit.js +5 -15
- package/build/src/cli/render/board.d.ts +1 -0
- package/build/src/cli/render/board.js +17 -0
- package/build/src/cli/render/catalog.js +84 -7
- package/build/src/cli/render/contract-observation.d.ts +10 -0
- package/build/src/cli/render/contract-observation.js +58 -0
- package/build/src/cli/render/contract.js +57 -54
- package/build/src/cli/render/kanshi.js +281 -209
- package/build/src/cli/render/receipt.d.ts +1 -0
- package/build/src/cli/render/receipt.js +20 -23
- package/build/src/cli/render/refusal.js +16 -15
- package/build/src/cli/render/region.js +22 -6
- package/build/src/cli/render/settings.js +4 -4
- package/build/src/cli/render/task.js +93 -21
- package/build/src/cli/render/terminal.js +16 -5
- package/build/src/cli/result.d.ts +11 -4
- package/build/src/cli/runtime.d.ts +6 -1
- package/build/src/cli/runtime.js +84 -63
- package/build/src/cli/selectors.js +2 -4
- package/build/src/cli/usage.js +6 -5
- package/build/src/contract-worktree.d.ts +1 -25
- package/build/src/contract-worktree.js +87 -174
- package/build/src/coordination/durable-file.js +7 -3
- package/build/src/coordination/sqlite-transaction-lock.js +29 -12
- package/build/src/core/facts/codec.js +7 -3
- package/build/src/core/facts/types.d.ts +1 -1
- package/build/src/core/subject.js +16 -9
- package/build/src/core/verbs/abandon.d.ts +1 -1
- package/build/src/core/verbs/abandon.js +1 -1
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +7 -7
- package/build/src/core/verbs/attestation.js +4 -1
- package/build/src/core/verbs/bind.d.ts +1 -1
- package/build/src/core/verbs/bind.js +1 -1
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +10 -8
- package/build/src/dispatch/index.js +8 -6
- package/build/src/duration.js +5 -1
- package/build/src/git/admission.js +5 -3
- package/build/src/git/hooks.d.ts +8 -0
- package/build/src/git/hooks.js +63 -14
- package/build/src/git/identity.js +1 -4
- package/build/src/git/integration.d.ts +2 -3
- package/build/src/git/integration.js +32 -39
- package/build/src/git/nuke.d.ts +1 -1
- package/build/src/git/nuke.js +110 -62
- package/build/src/git/observe.d.ts +13 -1
- package/build/src/git/observe.js +51 -44
- package/build/src/git/process.js +4 -2
- package/build/src/git/read-observation.js +10 -8
- package/build/src/git/reconcile.d.ts +14 -3
- package/build/src/git/reconcile.js +123 -102
- package/build/src/git/repository.d.ts +2 -0
- package/build/src/git/repository.js +13 -22
- package/build/src/git/target-placement.d.ts +9 -10
- package/build/src/git/target-placement.js +59 -62
- package/build/src/git/tender.d.ts +13 -2
- package/build/src/git/tender.js +28 -22
- package/build/src/git/terminal-seal.js +15 -12
- package/build/src/git/tree.js +5 -2
- package/build/src/git/workspace.d.ts +24 -2
- package/build/src/git/workspace.js +120 -16
- package/build/src/identity/coordinates.js +3 -1
- package/build/src/identity/selector.js +6 -3
- package/build/src/kanshi/fleet.d.ts +6 -0
- package/build/src/kanshi/fleet.js +27 -0
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +77 -45
- package/build/src/kanshi/report.d.ts +13 -24
- package/build/src/kanshi/select.js +25 -24
- package/build/src/library/address.d.ts +2 -1
- package/build/src/library/address.js +7 -8
- package/build/src/library/akuma-creation.js +20 -12
- package/build/src/library/audit.d.ts +0 -1
- package/build/src/library/audit.js +1 -2
- package/build/src/library/bind.d.ts +53 -5
- package/build/src/library/bind.js +98 -22
- package/build/src/library/catalog.d.ts +1 -0
- package/build/src/library/catalog.js +6 -3
- package/build/src/library/configuration.js +3 -1
- package/build/src/library/continuation.d.ts +37 -0
- package/build/src/library/continuation.js +86 -0
- package/build/src/library/contract.d.ts +39 -45
- package/build/src/library/contract.js +141 -118
- package/build/src/library/delivery.d.ts +18 -13
- package/build/src/library/fleet.d.ts +1 -1
- package/build/src/library/fleet.js +19 -11
- package/build/src/library/input.d.ts +2 -0
- package/build/src/library/input.js +14 -0
- package/build/src/library/keiyaku.d.ts +8 -7
- package/build/src/library/keiyaku.js +3 -3
- package/build/src/library/mutation.js +11 -4
- package/build/src/library/nuke.js +14 -5
- package/build/src/library/reconcile.d.ts +9 -4
- package/build/src/library/reconcile.js +30 -24
- package/build/src/library/refusal.d.ts +13 -5
- package/build/src/library/refusal.js +7 -0
- package/build/src/library/region.d.ts +8 -7
- package/build/src/library/region.js +31 -11
- package/build/src/library/repo.js +2 -2
- package/build/src/markdown/lex.js +9 -2
- package/build/src/markdown/parse.js +11 -7
- package/build/src/markdown/query.js +4 -2
- package/build/src/protocol/amend.js +3 -1
- package/build/src/protocol/attempt.d.ts +7 -3
- package/build/src/protocol/attempt.js +9 -5
- package/build/src/protocol/audit.d.ts +1 -1
- package/build/src/protocol/audit.js +4 -14
- package/build/src/protocol/bind.d.ts +19 -6
- package/build/src/protocol/bind.js +28 -13
- package/build/src/protocol/completion.d.ts +0 -1
- package/build/src/protocol/completion.js +1 -3
- package/build/src/protocol/deliver.d.ts +12 -4
- package/build/src/protocol/deliver.js +60 -51
- package/build/src/protocol/intent.d.ts +1 -0
- package/build/src/protocol/intent.js +9 -7
- package/build/src/protocol/operations.d.ts +10 -9
- package/build/src/protocol/operations.js +5 -3
- package/build/src/protocol/placement.d.ts +0 -1
- package/build/src/protocol/placement.js +6 -6
- package/build/src/protocol/read/observation.d.ts +11 -0
- package/build/src/protocol/read/observation.js +37 -0
- package/build/src/protocol/read/status.d.ts +23 -12
- package/build/src/protocol/read/status.js +90 -72
- package/build/src/protocol/reconcile.d.ts +1 -0
- package/build/src/protocol/reconcile.js +2 -3
- package/build/src/protocol/reintegrate.js +1 -1
- package/build/src/protocol/review.d.ts +0 -1
- package/build/src/protocol/review.js +15 -22
- package/build/src/protocol/run.d.ts +1 -0
- package/build/src/protocol/run.js +4 -5
- package/build/src/runtime/proc/launch.d.ts +33 -0
- package/build/src/runtime/proc/launch.js +98 -0
- package/build/src/runtime/proc/line-rpc.js +9 -3
- package/build/src/runtime/proc/run.d.ts +12 -6
- package/build/src/runtime/proc/run.js +78 -21
- package/build/src/runtime/proc/stdio.js +7 -10
- package/build/src/settings.js +6 -2
- package/build/src/settlement/fence.d.ts +0 -2
- package/build/src/settlement/fence.js +0 -24
- package/build/src/settlement/holder.js +13 -10
- package/build/src/settlement/settle.js +39 -6
- package/build/src/task/board.d.ts +21 -2
- package/build/src/task/board.js +35 -16
- package/build/src/task/compose.js +85 -22
- package/build/src/task/context.js +21 -8
- package/build/src/task/document.js +46 -10
- package/build/src/task/identity.js +19 -6
- package/build/src/task/index.js +118 -27
- package/build/src/task/input.js +29 -7
- package/build/src/task/mutation.js +61 -19
- package/build/src/task/operations.d.ts +8 -2
- package/build/src/task/operations.js +97 -32
- package/build/src/task/query.js +57 -28
- package/build/src/task/store.d.ts +5 -1
- package/build/src/task/store.js +88 -95
- package/build/src/verification/execution.js +14 -11
- package/build/src/workspace-place.js +19 -7
- package/build/src/world.js +16 -6
- package/package.json +8 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { abortableDelay } from "./abort.js";
|
|
2
2
|
import { appendActivity, beginTurn, breakBody, heartExists, readHeart, recordSession, recordTellDeliveries, recordTellReceipt, } from "./heart/index.js";
|
|
3
|
-
import { encodeAgentEvent
|
|
4
|
-
import { BodyRequestPump
|
|
3
|
+
import { encodeAgentEvent } from "./provider.js";
|
|
4
|
+
import { BodyRequestPump } from "./request-serve.js";
|
|
5
5
|
const BODY_CONTROL_OBSERVATION_MS = 100;
|
|
6
6
|
export const CONTROL_RESPONSE_MS = 1_000;
|
|
7
7
|
export class BodySupervisor {
|
|
@@ -26,8 +26,12 @@ export class BodySupervisor {
|
|
|
26
26
|
static async open(paths, bodySequence, leash) {
|
|
27
27
|
return new BodySupervisor(paths, bodySequence, leash, await readHeart(paths));
|
|
28
28
|
}
|
|
29
|
-
get reason() {
|
|
30
|
-
|
|
29
|
+
get reason() {
|
|
30
|
+
return this.stopping;
|
|
31
|
+
}
|
|
32
|
+
current() {
|
|
33
|
+
return this.observation;
|
|
34
|
+
}
|
|
31
35
|
async recordHung(diagnostic, at) {
|
|
32
36
|
await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
|
|
33
37
|
}
|
|
@@ -38,15 +42,15 @@ export class BodySupervisor {
|
|
|
38
42
|
next(after) {
|
|
39
43
|
if (this.observation !== after)
|
|
40
44
|
return Promise.resolve(this.observation);
|
|
41
|
-
return new Promise((resolve) => {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
this.waiter = resolve;
|
|
47
|
+
});
|
|
42
48
|
}
|
|
43
49
|
cancel(reason) {
|
|
44
50
|
if (this.stopping !== undefined)
|
|
45
51
|
return;
|
|
46
52
|
this.stopping = reason;
|
|
47
|
-
this.controller.abort(new Error(reason === "control"
|
|
48
|
-
? "Akuma Body interrupted by durable control"
|
|
49
|
-
: "Akuma Heart disappeared"));
|
|
53
|
+
this.controller.abort(new Error(reason === "control" ? "Akuma Body interrupted by durable control" : "Akuma Heart disappeared"));
|
|
50
54
|
this.waiter?.(this.observation);
|
|
51
55
|
this.waiter = undefined;
|
|
52
56
|
}
|
|
@@ -63,7 +67,7 @@ export class BodySupervisor {
|
|
|
63
67
|
async observe() {
|
|
64
68
|
for (;;) {
|
|
65
69
|
const snapshot = this.observation;
|
|
66
|
-
if (!await heartExists(this.paths))
|
|
70
|
+
if (!(await heartExists(this.paths)))
|
|
67
71
|
return this.cancel("heart-gone");
|
|
68
72
|
if (snapshot.stop?.bodySequence === this.bodySequence || snapshot.pause?.bodySequence === this.bodySequence) {
|
|
69
73
|
return this.cancel("control");
|
|
@@ -119,7 +123,9 @@ async function failedTurnSetup(input, turnSequence, requests, cancelDrive, error
|
|
|
119
123
|
try {
|
|
120
124
|
await requests.close();
|
|
121
125
|
}
|
|
122
|
-
catch {
|
|
126
|
+
catch {
|
|
127
|
+
/* Preserve the setup or pump failure. */
|
|
128
|
+
}
|
|
123
129
|
input.supervisor.signal.removeEventListener("abort", cancelDrive);
|
|
124
130
|
if (input.supervisor.signal.aborted) {
|
|
125
131
|
if (input.supervisor.reason === "heart-gone")
|
|
@@ -255,7 +261,9 @@ async function retireProviderCustody(input, turnSequence, drive) {
|
|
|
255
261
|
at,
|
|
256
262
|
});
|
|
257
263
|
}
|
|
258
|
-
catch {
|
|
264
|
+
catch {
|
|
265
|
+
/* Undisposed provider custody still owns the leash. */
|
|
266
|
+
}
|
|
259
267
|
}
|
|
260
268
|
await breakBody(input.paths, { sequence: input.bodySequence, end: "broke-off", at: input.now() });
|
|
261
269
|
return { kind: "hung" };
|
|
@@ -291,14 +299,16 @@ async function submitPendingLiveTells(writers, pending, attempted, tellLive) {
|
|
|
291
299
|
const submission = await tellLive({ id: tell.id, text: tell.body });
|
|
292
300
|
if (!mayWrite() || submission.kind === "turn-ended")
|
|
293
301
|
return "turn-ended";
|
|
294
|
-
await recordTellDeliveries(input.paths, [
|
|
302
|
+
await recordTellDeliveries(input.paths, [
|
|
303
|
+
{
|
|
295
304
|
tellId: tell.id,
|
|
296
305
|
route: "live",
|
|
297
306
|
turnSequence,
|
|
298
307
|
fence: submission.fence,
|
|
299
308
|
receipt: drive.receipts === undefined ? "unavailable" : "required",
|
|
300
309
|
deliveredAt: input.now(),
|
|
301
|
-
}
|
|
310
|
+
},
|
|
311
|
+
]);
|
|
302
312
|
return "accepted";
|
|
303
313
|
});
|
|
304
314
|
if (outcome === "turn-ended")
|
|
@@ -400,7 +410,9 @@ async function consumeTurnDrive(input, active) {
|
|
|
400
410
|
try {
|
|
401
411
|
await requests.close();
|
|
402
412
|
}
|
|
403
|
-
catch {
|
|
413
|
+
catch {
|
|
414
|
+
/* Preserve the first pump failure. */
|
|
415
|
+
}
|
|
404
416
|
active.releaseDriveSignal();
|
|
405
417
|
if (retirement.kind === "hung")
|
|
406
418
|
return retirement;
|
|
@@ -416,7 +428,7 @@ export async function driveTurn(input) {
|
|
|
416
428
|
active = started;
|
|
417
429
|
}
|
|
418
430
|
catch (error) {
|
|
419
|
-
if (!await heartExists(input.paths)) {
|
|
431
|
+
if (!(await heartExists(input.paths))) {
|
|
420
432
|
input.supervisor.cancel("heart-gone");
|
|
421
433
|
return { kind: "stopped" };
|
|
422
434
|
}
|
|
@@ -2,18 +2,59 @@ import { type SessionFact, type Soul } from "./heart/index.js";
|
|
|
2
2
|
import type { AkumaPaths } from "./identity.js";
|
|
3
3
|
import type { ProviderAdapter } from "./provider.js";
|
|
4
4
|
import { type UpstreamExecutionPort, type RequestChildLaunch } from "./request-serve.js";
|
|
5
|
+
import { type OwnedProcess, type RunLogReference } from "../runtime/proc/run.js";
|
|
6
|
+
export declare const LEASH_HELD_EXIT = 75;
|
|
5
7
|
export { CONTROL_RESPONSE_MS } from "./body-turn.js";
|
|
6
8
|
export type BodyLaunch = Readonly<{
|
|
7
9
|
paths: AkumaPaths;
|
|
8
10
|
seed?: Omit<Soul, "createdAt">;
|
|
9
11
|
birthSession?: Omit<SessionFact, "sequence">;
|
|
10
12
|
initialBody?: string;
|
|
13
|
+
refuseIfHeld?: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
export type TellWake = Readonly<{
|
|
16
|
+
kind: "told";
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
kind: "pursuing";
|
|
19
|
+
bodySequence: number;
|
|
20
|
+
}> | Readonly<{
|
|
21
|
+
kind: "held";
|
|
22
|
+
}> | Readonly<{
|
|
23
|
+
kind: "failed";
|
|
24
|
+
diagnostic: string;
|
|
25
|
+
child?: Readonly<{
|
|
26
|
+
code: number | null;
|
|
27
|
+
signal: string | null;
|
|
28
|
+
log: RunLogReference;
|
|
29
|
+
}>;
|
|
30
|
+
}>;
|
|
31
|
+
export type TellResult = Readonly<{
|
|
32
|
+
admission: Readonly<{
|
|
33
|
+
tellId: string;
|
|
34
|
+
fact: "recorded";
|
|
35
|
+
}>;
|
|
36
|
+
wake: TellWake;
|
|
37
|
+
}>;
|
|
38
|
+
export type TellWakeRuntime = Readonly<{
|
|
39
|
+
observeHeart(paths: AkumaPaths, signal: AbortSignal): Promise<AsyncGenerator<void>>;
|
|
40
|
+
spawn(paths: AkumaPaths): Promise<OwnedProcess>;
|
|
11
41
|
}>;
|
|
12
42
|
type BodyRuntime = Readonly<{
|
|
13
43
|
now(): string;
|
|
14
44
|
spawnChild?(launch: RequestChildLaunch): Promise<void>;
|
|
45
|
+
spawnBody?(launch: BodyLaunch): Promise<OwnedProcess>;
|
|
15
46
|
upstream?: UpstreamExecutionPort;
|
|
16
47
|
}>;
|
|
17
|
-
export declare function
|
|
18
|
-
|
|
19
|
-
|
|
48
|
+
export declare function bodyProcessInput(launch: BodyLaunch, bodyModuleUrl?: string): Promise<{
|
|
49
|
+
argv: string[];
|
|
50
|
+
cwd: string;
|
|
51
|
+
env: {
|
|
52
|
+
KEIYAKU_ACTOR_ID: import("./identity.js").AkuId;
|
|
53
|
+
};
|
|
54
|
+
log: string;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function handoffPendingTells(paths: AkumaPaths, spawn?: (launch: BodyLaunch) => Promise<OwnedProcess>): Promise<void>;
|
|
57
|
+
export declare function driveAkumaBody(launch: BodyLaunch, adapter?: ProviderAdapter, runtime?: BodyRuntime): Promise<"held" | void>;
|
|
58
|
+
export declare function spawnAkumaBody(launch: BodyLaunch): Promise<OwnedProcess>;
|
|
59
|
+
export declare function runAkumaBody(launch: BodyLaunch, upstream: UpstreamExecutionPort): Promise<"held" | void>;
|
|
60
|
+
export declare function wakeRecordedTell(paths: AkumaPaths, tellId: string, runtime?: TellWakeRuntime): Promise<TellResult>;
|
package/build/src/akuma/body.js
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { abortableDelay } from "./abort.js";
|
|
3
3
|
import { BodySupervisor, driveTurn, turnRecipe } from "./body-turn.js";
|
|
4
|
-
import { HeldAkumaLeash, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, } from "./heart/index.js";
|
|
4
|
+
import { HeldAkumaLeash, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, readNonterminalRequests, watchHeart, } from "./heart/index.js";
|
|
5
5
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
6
6
|
import { clearBodyRequestTransport, settleBodyRequests, } from "./request-serve.js";
|
|
7
|
-
import { spawnDetachedProcess, } from "../runtime/proc/run.js";
|
|
8
|
-
const LEASH_RETRY_MS =
|
|
7
|
+
import { handoffProcess, spawnDetachedProcess, } from "../runtime/proc/run.js";
|
|
8
|
+
const LEASH_RETRY_MS = 100;
|
|
9
|
+
export const LEASH_HELD_EXIT = 75;
|
|
9
10
|
export { CONTROL_RESPONSE_MS } from "./body-turn.js";
|
|
10
11
|
function missing(error) {
|
|
11
12
|
return isHeartAbsent(error) || error.code === "ENOENT";
|
|
12
13
|
}
|
|
13
14
|
async function putDownIfControlled(paths, supervisor, bodySequence, now) {
|
|
14
|
-
if (supervisor.reason === "control" && await heartExists(paths)) {
|
|
15
|
+
if (supervisor.reason === "control" && (await heartExists(paths))) {
|
|
15
16
|
await breakBody(paths, { sequence: bodySequence, end: "put-down", at: now() });
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
|
-
async function takeLeash(paths) {
|
|
19
|
+
async function takeLeash(paths, refuseIfHeld = false) {
|
|
19
20
|
for (;;) {
|
|
20
21
|
try {
|
|
21
22
|
const leash = await HeldAkumaLeash.try(paths);
|
|
22
23
|
if (leash !== null)
|
|
23
24
|
return leash;
|
|
25
|
+
if (refuseIfHeld)
|
|
26
|
+
return "held";
|
|
24
27
|
}
|
|
25
28
|
catch (error) {
|
|
26
29
|
if (missing(error))
|
|
27
|
-
return
|
|
30
|
+
return "absent";
|
|
28
31
|
throw error;
|
|
29
32
|
}
|
|
30
33
|
await abortableDelay(LEASH_RETRY_MS);
|
|
@@ -37,7 +40,7 @@ async function bornSoul(launch, leash, now) {
|
|
|
37
40
|
if (launch.seed === undefined)
|
|
38
41
|
throw new Error("Akuma wake has no born soul");
|
|
39
42
|
const candidate = { ...launch.seed, createdAt: now };
|
|
40
|
-
if (await leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
|
|
43
|
+
if ((await leash.birth(launch.paths, candidate, launch.birthSession)) === "sealed")
|
|
41
44
|
return null;
|
|
42
45
|
const born = (await readHeart(launch.paths)).soul;
|
|
43
46
|
if (born === null || born.id !== candidate.id) {
|
|
@@ -71,11 +74,25 @@ async function persistTurn(paths, turnSequence, result, completedAt) {
|
|
|
71
74
|
return result.kind === "answered" && result.session === undefined ? "failed" : result.kind;
|
|
72
75
|
}
|
|
73
76
|
function defaultRuntime() {
|
|
77
|
+
return { now: () => new Date().toISOString(), spawnChild: handoffAkumaBody, spawnBody: spawnAkumaBody };
|
|
78
|
+
}
|
|
79
|
+
export async function bodyProcessInput(launch, bodyModuleUrl = import.meta.url) {
|
|
80
|
+
const encoded = Buffer.from(JSON.stringify(launch), "utf8").toString("base64url");
|
|
81
|
+
const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
|
|
82
|
+
if (actorId === undefined)
|
|
83
|
+
throw new Error("Akuma wake has no born soul");
|
|
84
|
+
const source = bodyModuleUrl.endsWith(".ts");
|
|
85
|
+
const entry = fileURLToPath(new URL(source ? "../akuma-body.ts" : "../akuma-body.js", bodyModuleUrl));
|
|
74
86
|
return {
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
argv: source ? [process.execPath, "--import", "tsx", entry, encoded] : [process.execPath, entry, encoded],
|
|
88
|
+
cwd: await launchCwd(launch),
|
|
89
|
+
env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
|
|
90
|
+
log: launch.paths.log,
|
|
77
91
|
};
|
|
78
92
|
}
|
|
93
|
+
async function handoffAkumaBody(launch) {
|
|
94
|
+
await handoffProcess(await bodyProcessInput(launch));
|
|
95
|
+
}
|
|
79
96
|
async function prepareBodyStart(paths, leash) {
|
|
80
97
|
const before = await readHeart(paths);
|
|
81
98
|
if (before.pause !== null)
|
|
@@ -132,7 +149,7 @@ async function runBodyTurns(input) {
|
|
|
132
149
|
adapter,
|
|
133
150
|
bodySequence,
|
|
134
151
|
supervisor,
|
|
135
|
-
runtimeSpawn: runtime.spawnChild ??
|
|
152
|
+
runtimeSpawn: runtime.spawnChild ?? handoffAkumaBody,
|
|
136
153
|
body: initial ?? "",
|
|
137
154
|
...(initial === undefined ? {} : { call: initial }),
|
|
138
155
|
launchTells,
|
|
@@ -149,8 +166,8 @@ async function runBodyTurns(input) {
|
|
|
149
166
|
await breakBody(launch.paths, { sequence: bodySequence, end: "put-down", at: runtime.now() });
|
|
150
167
|
return;
|
|
151
168
|
}
|
|
152
|
-
if (result.kind === "resume-unsupported"
|
|
153
|
-
|
|
169
|
+
if (result.kind === "resume-unsupported" ||
|
|
170
|
+
(await persistTurn(launch.paths, result.turnSequence, result, runtime.now())) === "failed") {
|
|
154
171
|
await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
|
|
155
172
|
return;
|
|
156
173
|
}
|
|
@@ -158,18 +175,38 @@ async function runBodyTurns(input) {
|
|
|
158
175
|
initial = undefined;
|
|
159
176
|
}
|
|
160
177
|
}
|
|
178
|
+
export async function handoffPendingTells(paths, spawn = spawnAkumaBody) {
|
|
179
|
+
try {
|
|
180
|
+
if ((await readHeart(paths)).pending.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
(await spawn({ paths, refuseIfHeld: true })).release();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
/* a later Heart interaction retries the unchanged pending Tell */
|
|
186
|
+
}
|
|
187
|
+
}
|
|
161
188
|
export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()) {
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
189
|
+
const acquired = await takeLeash(launch.paths, launch.refuseIfHeld);
|
|
190
|
+
if (acquired === "held")
|
|
191
|
+
return "held";
|
|
192
|
+
if (acquired === "absent")
|
|
164
193
|
return;
|
|
194
|
+
const leash = acquired;
|
|
195
|
+
let bodyStarted = false;
|
|
165
196
|
try {
|
|
166
197
|
const soul = await bornSoul(launch, leash, runtime.now());
|
|
167
198
|
if (soul === null)
|
|
168
199
|
return;
|
|
200
|
+
if (launch.seed === undefined && launch.initialBody === undefined) {
|
|
201
|
+
const [heart, requests] = await Promise.all([readHeart(launch.paths), readNonterminalRequests(launch.paths)]);
|
|
202
|
+
if (heart.pending.length === 0 && requests.length === 0)
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
169
205
|
const selected = adapter ?? (await resolveProviderExecution(soul.provider)).adapter;
|
|
170
|
-
if (!await prepareBodyStart(launch.paths, leash))
|
|
206
|
+
if (!(await prepareBodyStart(launch.paths, leash)))
|
|
171
207
|
return;
|
|
172
208
|
const body = await leash.recordBody(launch.paths, { leashTakenAt: runtime.now() });
|
|
209
|
+
bodyStarted = true;
|
|
173
210
|
const supervisor = await BodySupervisor.open(launch.paths, body.sequence, leash);
|
|
174
211
|
const execution = { launch, soul, adapter: selected, bodySequence: body.sequence, supervisor, runtime };
|
|
175
212
|
try {
|
|
@@ -181,7 +218,7 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
181
218
|
}
|
|
182
219
|
}
|
|
183
220
|
catch (error) {
|
|
184
|
-
if (!await heartExists(launch.paths))
|
|
221
|
+
if (!(await heartExists(launch.paths)))
|
|
185
222
|
return;
|
|
186
223
|
try {
|
|
187
224
|
await leash.sealIfUnborn(launch.paths, {
|
|
@@ -189,30 +226,102 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
189
226
|
at: runtime.now(),
|
|
190
227
|
});
|
|
191
228
|
}
|
|
192
|
-
catch {
|
|
229
|
+
catch {
|
|
230
|
+
/* the original Body failure remains authoritative */
|
|
231
|
+
}
|
|
193
232
|
throw error;
|
|
194
233
|
}
|
|
195
234
|
finally {
|
|
196
235
|
leash.release();
|
|
236
|
+
if (bodyStarted)
|
|
237
|
+
await handoffPendingTells(launch.paths, runtime.spawnBody ?? spawnAkumaBody);
|
|
197
238
|
}
|
|
198
239
|
}
|
|
199
240
|
export async function spawnAkumaBody(launch) {
|
|
200
|
-
|
|
201
|
-
const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
|
|
202
|
-
if (actorId === undefined)
|
|
203
|
-
throw new Error("Akuma wake has no born soul");
|
|
204
|
-
const owned = await spawnDetachedProcess({
|
|
205
|
-
argv: [process.execPath, ...process.execArgv, fileURLToPath(new URL("../akuma-body.js", import.meta.url)), encoded],
|
|
206
|
-
cwd: await launchCwd(launch),
|
|
207
|
-
env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
|
|
208
|
-
log: launch.paths.log,
|
|
209
|
-
});
|
|
210
|
-
owned.release();
|
|
241
|
+
return await spawnDetachedProcess(await bodyProcessInput(launch));
|
|
211
242
|
}
|
|
212
243
|
export async function runAkumaBody(launch, upstream) {
|
|
213
|
-
await driveAkumaBody(launch, undefined, {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
})
|
|
244
|
+
return await driveAkumaBody(launch, undefined, { ...defaultRuntime(), upstream });
|
|
245
|
+
}
|
|
246
|
+
const DIRECT_TELL_WAKE = {
|
|
247
|
+
observeHeart: watchHeart,
|
|
248
|
+
spawn: async (paths) => await spawnAkumaBody({ paths, refuseIfHeld: true }),
|
|
249
|
+
};
|
|
250
|
+
function diagnostic(error) {
|
|
251
|
+
return error instanceof Error ? error.message : String(error);
|
|
252
|
+
}
|
|
253
|
+
function preAdmissionDiagnostic(exit) {
|
|
254
|
+
return exit.code === null ? `pre-admission signal ${exit.signal ?? "unknown"}` : `pre-admission exit ${exit.code}`;
|
|
255
|
+
}
|
|
256
|
+
async function settledWake(paths, tellId, after) {
|
|
257
|
+
const heart = await readHeart(paths);
|
|
258
|
+
if (tellId !== undefined && !heart.pending.some((tell) => tell.id === tellId))
|
|
259
|
+
return { kind: "told" };
|
|
260
|
+
if (heart.latestBody !== null && heart.latestBody.sequence > after) {
|
|
261
|
+
return { kind: "pursuing", bodySequence: heart.latestBody.sequence };
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
function failedChild(exit) {
|
|
266
|
+
return {
|
|
267
|
+
kind: "failed",
|
|
268
|
+
diagnostic: preAdmissionDiagnostic(exit),
|
|
269
|
+
child: { code: exit.code, signal: exit.signal, log: exit.log },
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
async function awaitWake(paths, tellId, after, child, changed) {
|
|
273
|
+
let nextChange = changed.next();
|
|
274
|
+
for (;;) {
|
|
275
|
+
const settled = await settledWake(paths, tellId, after);
|
|
276
|
+
if (settled !== null) {
|
|
277
|
+
child.release();
|
|
278
|
+
return settled;
|
|
279
|
+
}
|
|
280
|
+
const winner = await Promise.race([
|
|
281
|
+
nextChange.then(({ done }) => ({ kind: done ? "closed" : "changed" }), (error) => ({ kind: "observer-failed", error })),
|
|
282
|
+
child.exited.then((exit) => ({ kind: "exited", exit })),
|
|
283
|
+
]);
|
|
284
|
+
if (winner.kind === "changed") {
|
|
285
|
+
nextChange = changed.next();
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const final = await settledWake(paths, tellId, after);
|
|
289
|
+
if (final !== null) {
|
|
290
|
+
child.release();
|
|
291
|
+
return final;
|
|
292
|
+
}
|
|
293
|
+
if (winner.kind === "exited") {
|
|
294
|
+
return winner.exit.code === LEASH_HELD_EXIT ? { kind: "held" } : failedChild(winner.exit);
|
|
295
|
+
}
|
|
296
|
+
await child.terminate();
|
|
297
|
+
await child.exited;
|
|
298
|
+
return winner.kind === "observer-failed"
|
|
299
|
+
? { kind: "failed", diagnostic: diagnostic(winner.error) }
|
|
300
|
+
: { kind: "failed", diagnostic: "Heart observer closed" };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async function wakePendingTells(paths, tellId, runtime) {
|
|
304
|
+
const watching = new AbortController();
|
|
305
|
+
let changed;
|
|
306
|
+
try {
|
|
307
|
+
const beforeHeart = await readHeart(paths);
|
|
308
|
+
if (tellId === undefined && beforeHeart.pending.length === 0)
|
|
309
|
+
return null;
|
|
310
|
+
const before = beforeHeart.latestBody?.sequence ?? 0;
|
|
311
|
+
changed = await runtime.observeHeart(paths, watching.signal);
|
|
312
|
+
return await awaitWake(paths, tellId, before, await runtime.spawn(paths), changed);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
watching.abort();
|
|
319
|
+
await changed?.return(undefined);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
export async function wakeRecordedTell(paths, tellId, runtime = DIRECT_TELL_WAKE) {
|
|
323
|
+
return {
|
|
324
|
+
admission: { tellId, fact: "recorded" },
|
|
325
|
+
wake: (await wakePendingTells(paths, tellId, runtime)),
|
|
326
|
+
};
|
|
218
327
|
}
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
function record(value) {
|
|
2
2
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
3
|
-
? value
|
|
3
|
+
? value
|
|
4
|
+
: null;
|
|
4
5
|
}
|
|
5
6
|
export function decodeResumeCoordinate(value) {
|
|
6
7
|
const coordinate = record(value);
|
|
7
8
|
if (coordinate === null)
|
|
8
9
|
return null;
|
|
9
10
|
const keys = Object.keys(coordinate);
|
|
10
|
-
if (typeof coordinate.sessionFile === "string" &&
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if (typeof coordinate.sessionFile === "string" &&
|
|
12
|
+
coordinate.sessionFile.trim().length > 0 &&
|
|
13
|
+
(coordinate.sessionId === undefined ||
|
|
14
|
+
(typeof coordinate.sessionId === "string" && coordinate.sessionId.trim().length > 0)) &&
|
|
15
|
+
keys.every((key) => key === "sessionFile" || key === "sessionId")) {
|
|
16
|
+
return {
|
|
17
|
+
sessionFile: coordinate.sessionFile,
|
|
18
|
+
...(coordinate.sessionId === undefined ? {} : { sessionId: coordinate.sessionId }),
|
|
19
|
+
};
|
|
14
20
|
}
|
|
15
21
|
return typeof coordinate.sessionId === "string" && coordinate.sessionId.trim().length > 0 && keys.length === 1
|
|
16
|
-
? { sessionId: coordinate.sessionId }
|
|
22
|
+
? { sessionId: coordinate.sessionId }
|
|
23
|
+
: null;
|
|
17
24
|
}
|
|
18
25
|
export function encodeResumeCoordinate(coordinate) {
|
|
19
26
|
return "sessionFile" in coordinate
|
|
20
|
-
? {
|
|
27
|
+
? {
|
|
28
|
+
sessionFile: coordinate.sessionFile,
|
|
29
|
+
...(coordinate.sessionId === undefined ? {} : { sessionId: coordinate.sessionId }),
|
|
30
|
+
}
|
|
21
31
|
: { sessionId: coordinate.sessionId };
|
|
22
32
|
}
|
|
@@ -18,5 +18,5 @@ export function lifeAt(current, body, kill, createdAt) {
|
|
|
18
18
|
return kill?.at ?? null;
|
|
19
19
|
if (current === "asleep")
|
|
20
20
|
return body?.endedAt ?? createdAt;
|
|
21
|
-
return current === "stranded" ? body?.endedAt ?? null : null;
|
|
21
|
+
return current === "stranded" ? (body?.endedAt ?? null) : null;
|
|
22
22
|
}
|
|
@@ -3,7 +3,7 @@ import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, SealFact, S
|
|
|
3
3
|
export type { CallFact } from "./facts.js";
|
|
4
4
|
import type { ActivityFact } from "./rows.js";
|
|
5
5
|
import { type ActivityFactSlice } from "./timeline.js";
|
|
6
|
-
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
|
|
6
|
+
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash, watchHeart } from "./storage.js";
|
|
7
7
|
export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
8
8
|
export { life, lifeAt } from "./facts.js";
|
|
9
9
|
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, UpstreamRequestService, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
|
|
@@ -3,7 +3,7 @@ import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingT
|
|
|
3
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
|
-
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
|
|
6
|
+
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash, watchHeart } from "./storage.js";
|
|
7
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;
|
|
@@ -80,9 +80,7 @@ export async function recordTellDeliveries(paths, inputs) {
|
|
|
80
80
|
}
|
|
81
81
|
export async function recordTellReceipt(paths, input) {
|
|
82
82
|
await withHeart(paths, (heart) => transaction(heart, () => {
|
|
83
|
-
const tellIds = input.evidence === "exact"
|
|
84
|
-
? [input.tellId]
|
|
85
|
-
: tellIdsForFence(heart, input.turnSequence, input.fence);
|
|
83
|
+
const tellIds = input.evidence === "exact" ? [input.tellId] : tellIdsForFence(heart, input.turnSequence, input.fence);
|
|
86
84
|
if (tellIds.length === 0)
|
|
87
85
|
throw new Error("tell receipt has no delivery mapping");
|
|
88
86
|
insertTellReceiptFact(heart, input);
|
|
@@ -29,10 +29,10 @@ export async function admitRequest(paths, input) {
|
|
|
29
29
|
const fact = requestFact(heart, input.id);
|
|
30
30
|
if (fact === null)
|
|
31
31
|
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
32
|
-
if (fact.requester !== soul.id
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
if (fact.requester !== soul.id ||
|
|
33
|
+
fact.id !== normalized.id ||
|
|
34
|
+
fact.action !== normalized.action ||
|
|
35
|
+
requestPayloadJson(fact) !== requestPayloadJson(normalized)) {
|
|
36
36
|
throw new Error(`Akuma request ${input.id} reused different input`);
|
|
37
37
|
}
|
|
38
38
|
return fact;
|
|
@@ -12,9 +12,7 @@ export function requestPayloadJson(input) {
|
|
|
12
12
|
return json(payload);
|
|
13
13
|
}
|
|
14
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);
|
|
15
|
+
const knownAction = ["akuma.call", "akuma.wait", "akuma.tell", "akuma.kill", "contract.deliver", "contract.review"].includes(row.action) || isTaskMutationAction(row.action);
|
|
18
16
|
if (!knownAction)
|
|
19
17
|
throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
|
|
20
18
|
const payload = parsed(row.payload_json);
|
|
@@ -53,36 +51,49 @@ const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted
|
|
|
53
51
|
state, child, service_json, diagnostic, evidence`;
|
|
54
52
|
export function insertRequestFact(database, input) {
|
|
55
53
|
const { id, action, requester, admittedAt, refusal } = input;
|
|
56
|
-
database
|
|
54
|
+
database
|
|
55
|
+
.prepare(`INSERT OR IGNORE INTO requests(
|
|
57
56
|
id, requester, action, payload_json, admitted_at, state, diagnostic
|
|
58
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
57
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
58
|
+
.run(id, requester, action, requestPayloadJson(input), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
|
|
59
59
|
}
|
|
60
60
|
export function requestFact(database, id) {
|
|
61
61
|
const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
|
|
62
62
|
return row === undefined ? null : decodeRequestRow(row);
|
|
63
63
|
}
|
|
64
64
|
export function nonterminalRequestFacts(database) {
|
|
65
|
-
const rows = database
|
|
66
|
-
|
|
65
|
+
const rows = database
|
|
66
|
+
.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
|
|
67
|
+
WHERE state IN ('admitted', 'reserved') ORDER BY sequence`)
|
|
68
|
+
.all();
|
|
67
69
|
return rows.map(decodeRequestRow);
|
|
68
70
|
}
|
|
69
71
|
export function updateRequestReserved(database, id, child) {
|
|
70
|
-
database
|
|
72
|
+
database
|
|
73
|
+
.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
|
|
71
74
|
.run(child, id);
|
|
72
75
|
}
|
|
73
76
|
export function updateRequestServed(database, id, child) {
|
|
74
|
-
database
|
|
75
|
-
|
|
77
|
+
database
|
|
78
|
+
.prepare(`UPDATE requests SET state = 'served', child = ?
|
|
79
|
+
WHERE id = ? AND state = 'reserved'`)
|
|
80
|
+
.run(child, id);
|
|
76
81
|
}
|
|
77
82
|
export function updateUpstreamRequestServed(database, id, service) {
|
|
78
|
-
database
|
|
79
|
-
|
|
83
|
+
database
|
|
84
|
+
.prepare(`UPDATE requests SET state = 'served', service_json = ?
|
|
85
|
+
WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`)
|
|
86
|
+
.run(json(service), id);
|
|
80
87
|
}
|
|
81
88
|
export function updateRequestRefused(database, id, diagnostic) {
|
|
82
|
-
database
|
|
83
|
-
|
|
89
|
+
database
|
|
90
|
+
.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
|
|
91
|
+
WHERE id = ? AND state = 'admitted'`)
|
|
92
|
+
.run(diagnostic, id);
|
|
84
93
|
}
|
|
85
94
|
export function updateRequestVoided(database, id, evidence) {
|
|
86
|
-
database
|
|
87
|
-
|
|
95
|
+
database
|
|
96
|
+
.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
|
|
97
|
+
WHERE id = ? AND state IN ('admitted', 'reserved')`)
|
|
98
|
+
.run(evidence, id);
|
|
88
99
|
}
|