@astrosheep/keiyaku 4.1.0 → 4.1.2
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-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +9 -0
- package/build/src/akuma/akuma-handle.d.ts +39 -0
- package/build/src/akuma/akuma-handle.js +263 -0
- package/build/src/akuma/akuma-observe.d.ts +19 -0
- package/build/src/akuma/akuma-observe.js +74 -0
- package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
- package/build/src/akuma/akuma-product-symbols.js +1 -0
- package/build/src/akuma/akuma-product.d.ts +18 -0
- package/build/src/akuma/akuma-product.js +154 -0
- package/build/src/akuma/akuma.d.ts +37 -81
- package/build/src/akuma/akuma.js +39 -447
- package/build/src/akuma/allowed.d.ts +1 -0
- package/build/src/akuma/allowed.js +6 -0
- package/build/src/akuma/archetype.js +8 -5
- package/build/src/akuma/body-supervisor.d.ts +26 -0
- package/build/src/akuma/body-supervisor.js +89 -0
- package/build/src/akuma/body.d.ts +45 -4
- package/build/src/akuma/body.js +145 -35
- 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-read.d.ts +17 -0
- package/build/src/akuma/projection-read.js +135 -0
- package/build/src/akuma/projection.d.ts +1 -16
- package/build/src/akuma/projection.js +77 -154
- package/build/src/akuma/provider-recipe.js +11 -5
- package/build/src/akuma/provider.js +105 -62
- package/build/src/akuma/providers/acp/config.js +5 -6
- package/build/src/akuma/providers/acp/core.js +17 -10
- package/build/src/akuma/providers/acp/events.js +38 -29
- package/build/src/akuma/providers/acp/index.js +13 -9
- package/build/src/akuma/providers/claude/events.js +8 -12
- package/build/src/akuma/providers/claude/index.d.ts +1 -0
- package/build/src/akuma/providers/claude/index.js +55 -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 +25 -13
- package/build/src/akuma/providers/grok-build/index.js +22 -25
- package/build/src/akuma/providers/index.js +2 -18
- package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
- package/build/src/akuma/providers/opencode-sdk/index.js +26 -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 -40
- package/build/src/akuma/publication.js +9 -9
- package/build/src/akuma/request-execution.d.ts +6 -0
- package/build/src/akuma/request-execution.js +86 -0
- package/build/src/akuma/request-serve.d.ts +12 -1
- package/build/src/akuma/request-serve.js +19 -122
- 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-turn.d.ts → turn-drive.d.ts} +2 -25
- package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
- package/build/src/akuma-body.js +6 -5
- package/build/src/body/amend.d.ts +4 -1
- package/build/src/body/amend.js +14 -6
- 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 +2 -70
- 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 +8 -1
- package/build/src/cli/commands/akuma-invoke.js +18 -6
- package/build/src/cli/commands/akuma.d.ts +0 -6
- package/build/src/cli/commands/akuma.js +11 -24
- 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-invoke.d.ts +25 -0
- package/build/src/cli/commands/contract-invoke.js +193 -0
- package/build/src/cli/commands/contract.d.ts +109 -10
- package/build/src/cli/commands/contract.js +339 -16
- 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.js +187 -67
- package/build/src/cli/commands/task-query.js +48 -24
- package/build/src/cli/commands/task.js +150 -38
- package/build/src/cli/coordinates.js +2 -4
- package/build/src/cli/invoke.js +92 -208
- package/build/src/cli/main.js +4 -2
- package/build/src/cli/parse.d.ts +3 -90
- package/build/src/cli/parse.js +27 -233
- package/build/src/cli/render/akuma-activity.d.ts +38 -0
- package/build/src/cli/render/akuma-activity.js +362 -0
- 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.d.ts +1 -1
- package/build/src/cli/render/akuma.js +61 -353
- 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 +81 -8
- package/build/src/cli/render/contract-history.d.ts +2 -0
- package/build/src/cli/render/contract-history.js +83 -0
- 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.d.ts +1 -2
- package/build/src/cli/render/contract.js +41 -142
- package/build/src/cli/render/kanshi.js +239 -215
- package/build/src/cli/render/receipt.js +2 -9
- package/build/src/cli/render/refusal.js +10 -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 +34 -14
- package/build/src/cli/render/terminal.js +16 -5
- package/build/src/cli/result.d.ts +3 -3
- 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 -23
- package/build/src/contract-worktree.js +79 -155
- 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 -57
- 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 +22 -2
- package/build/src/git/reconcile.js +38 -274
- package/build/src/git/repository.d.ts +2 -0
- package/build/src/git/repository.js +13 -22
- package/build/src/git/scratch.d.ts +8 -5
- package/build/src/git/scratch.js +29 -2
- package/build/src/git/target-placement.d.ts +9 -10
- package/build/src/git/target-placement.js +59 -62
- package/build/src/git/tender.js +13 -46
- package/build/src/git/terminal-reconcile.d.ts +21 -0
- package/build/src/git/terminal-reconcile.js +278 -0
- 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 +60 -48
- package/build/src/kanshi/report.d.ts +11 -23
- 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 +15 -4
- package/build/src/library/continuation.js +24 -16
- package/build/src/library/contract-bind.d.ts +26 -0
- package/build/src/library/contract-bind.js +106 -0
- package/build/src/library/contract-operations.d.ts +77 -0
- package/build/src/library/contract-operations.js +130 -0
- package/build/src/library/contract-types.d.ts +95 -0
- package/build/src/library/contract-types.js +1 -0
- package/build/src/library/contract.d.ts +19 -154
- package/build/src/library/contract.js +46 -261
- 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/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 +1 -3
- package/build/src/protocol/deliver.js +15 -39
- package/build/src/protocol/intent.d.ts +1 -0
- package/build/src/protocol/intent.js +9 -7
- package/build/src/protocol/operations.d.ts +3 -7
- 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/holder.js +13 -10
- package/build/src/settlement/settle.js +39 -6
- package/build/src/task/board.js +12 -6
- 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 +5 -2
- package/build/src/task/index.d.ts +0 -1
- package/build/src/task/index.js +119 -34
- package/build/src/task/input.js +29 -7
- package/build/src/task/mutation.js +61 -19
- package/build/src/task/operations.d.ts +6 -1
- package/build/src/task/operations.js +83 -29
- package/build/src/task/query.js +55 -26
- package/build/src/task/store.d.ts +3 -1
- package/build/src/task/store.js +81 -17
- 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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { abortableDelay } from "./abort.js";
|
|
2
|
+
import { heartExists, readHeart } from "./heart/index.js";
|
|
3
|
+
const BODY_CONTROL_OBSERVATION_MS = 100;
|
|
4
|
+
export const CONTROL_RESPONSE_MS = 1_000;
|
|
5
|
+
export class BodySupervisor {
|
|
6
|
+
paths;
|
|
7
|
+
bodySequence;
|
|
8
|
+
leash;
|
|
9
|
+
signal;
|
|
10
|
+
controller = new AbortController();
|
|
11
|
+
finished = new AbortController();
|
|
12
|
+
observer;
|
|
13
|
+
waiter;
|
|
14
|
+
observation;
|
|
15
|
+
stopping;
|
|
16
|
+
constructor(paths, bodySequence, leash, observation) {
|
|
17
|
+
this.paths = paths;
|
|
18
|
+
this.bodySequence = bodySequence;
|
|
19
|
+
this.leash = leash;
|
|
20
|
+
this.signal = this.controller.signal;
|
|
21
|
+
this.observation = observation;
|
|
22
|
+
this.observer = this.observe();
|
|
23
|
+
}
|
|
24
|
+
static async open(paths, bodySequence, leash) {
|
|
25
|
+
return new BodySupervisor(paths, bodySequence, leash, await readHeart(paths));
|
|
26
|
+
}
|
|
27
|
+
get reason() {
|
|
28
|
+
return this.stopping;
|
|
29
|
+
}
|
|
30
|
+
current() {
|
|
31
|
+
return this.observation;
|
|
32
|
+
}
|
|
33
|
+
async recordHung(diagnostic, at) {
|
|
34
|
+
await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
|
|
35
|
+
}
|
|
36
|
+
async refresh() {
|
|
37
|
+
this.publish(await readHeart(this.paths));
|
|
38
|
+
return this.observation;
|
|
39
|
+
}
|
|
40
|
+
next(after) {
|
|
41
|
+
if (this.observation !== after)
|
|
42
|
+
return Promise.resolve(this.observation);
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
this.waiter = resolve;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
cancel(reason) {
|
|
48
|
+
if (this.stopping !== undefined)
|
|
49
|
+
return;
|
|
50
|
+
this.stopping = reason;
|
|
51
|
+
this.controller.abort(new Error(reason === "control" ? "Akuma Body interrupted by durable control" : "Akuma Heart disappeared"));
|
|
52
|
+
this.waiter?.(this.observation);
|
|
53
|
+
this.waiter = undefined;
|
|
54
|
+
}
|
|
55
|
+
async close() {
|
|
56
|
+
this.finished.abort();
|
|
57
|
+
await this.observer;
|
|
58
|
+
this.waiter = undefined;
|
|
59
|
+
}
|
|
60
|
+
publish(snapshot) {
|
|
61
|
+
this.observation = snapshot;
|
|
62
|
+
this.waiter?.(snapshot);
|
|
63
|
+
this.waiter = undefined;
|
|
64
|
+
}
|
|
65
|
+
async observe() {
|
|
66
|
+
for (;;) {
|
|
67
|
+
const snapshot = this.observation;
|
|
68
|
+
if (!(await heartExists(this.paths)))
|
|
69
|
+
return this.cancel("heart-gone");
|
|
70
|
+
if (snapshot.stop?.bodySequence === this.bodySequence || snapshot.pause?.bodySequence === this.bodySequence) {
|
|
71
|
+
return this.cancel("control");
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await abortableDelay(BODY_CONTROL_OBSERVATION_MS, this.finished.signal);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await this.refresh();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (await heartExists(this.paths))
|
|
84
|
+
throw error;
|
|
85
|
+
return this.cancel("heart-gone");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -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
|
-
|
|
5
|
+
import { type OwnedProcess, type RunLogReference } from "../runtime/proc/run.js";
|
|
6
|
+
export declare const LEASH_HELD_EXIT = 75;
|
|
7
|
+
export { CONTROL_RESPONSE_MS } from "./body-supervisor.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,34 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { abortableDelay } from "./abort.js";
|
|
3
|
-
import { BodySupervisor
|
|
4
|
-
import {
|
|
3
|
+
import { BodySupervisor } from "./body-supervisor.js";
|
|
4
|
+
import { driveTurn, turnRecipe } from "./turn-drive.js";
|
|
5
|
+
import { HeldAkumaLeash, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, readNonterminalRequests, watchHeart, } from "./heart/index.js";
|
|
5
6
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
6
7
|
import { clearBodyRequestTransport, settleBodyRequests, } from "./request-serve.js";
|
|
7
|
-
import { spawnDetachedProcess, } from "../runtime/proc/run.js";
|
|
8
|
-
const LEASH_RETRY_MS =
|
|
9
|
-
export
|
|
8
|
+
import { handoffProcess, spawnDetachedProcess, } from "../runtime/proc/run.js";
|
|
9
|
+
const LEASH_RETRY_MS = 100;
|
|
10
|
+
export const LEASH_HELD_EXIT = 75;
|
|
11
|
+
export { CONTROL_RESPONSE_MS } from "./body-supervisor.js";
|
|
10
12
|
function missing(error) {
|
|
11
13
|
return isHeartAbsent(error) || error.code === "ENOENT";
|
|
12
14
|
}
|
|
13
15
|
async function putDownIfControlled(paths, supervisor, bodySequence, now) {
|
|
14
|
-
if (supervisor.reason === "control" && await heartExists(paths)) {
|
|
16
|
+
if (supervisor.reason === "control" && (await heartExists(paths))) {
|
|
15
17
|
await breakBody(paths, { sequence: bodySequence, end: "put-down", at: now() });
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
|
-
async function takeLeash(paths) {
|
|
20
|
+
async function takeLeash(paths, refuseIfHeld = false) {
|
|
19
21
|
for (;;) {
|
|
20
22
|
try {
|
|
21
23
|
const leash = await HeldAkumaLeash.try(paths);
|
|
22
24
|
if (leash !== null)
|
|
23
25
|
return leash;
|
|
26
|
+
if (refuseIfHeld)
|
|
27
|
+
return "held";
|
|
24
28
|
}
|
|
25
29
|
catch (error) {
|
|
26
30
|
if (missing(error))
|
|
27
|
-
return
|
|
31
|
+
return "absent";
|
|
28
32
|
throw error;
|
|
29
33
|
}
|
|
30
34
|
await abortableDelay(LEASH_RETRY_MS);
|
|
@@ -37,7 +41,7 @@ async function bornSoul(launch, leash, now) {
|
|
|
37
41
|
if (launch.seed === undefined)
|
|
38
42
|
throw new Error("Akuma wake has no born soul");
|
|
39
43
|
const candidate = { ...launch.seed, createdAt: now };
|
|
40
|
-
if (await leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
|
|
44
|
+
if ((await leash.birth(launch.paths, candidate, launch.birthSession)) === "sealed")
|
|
41
45
|
return null;
|
|
42
46
|
const born = (await readHeart(launch.paths)).soul;
|
|
43
47
|
if (born === null || born.id !== candidate.id) {
|
|
@@ -71,11 +75,25 @@ async function persistTurn(paths, turnSequence, result, completedAt) {
|
|
|
71
75
|
return result.kind === "answered" && result.session === undefined ? "failed" : result.kind;
|
|
72
76
|
}
|
|
73
77
|
function defaultRuntime() {
|
|
78
|
+
return { now: () => new Date().toISOString(), spawnChild: handoffAkumaBody, spawnBody: spawnAkumaBody };
|
|
79
|
+
}
|
|
80
|
+
export async function bodyProcessInput(launch, bodyModuleUrl = import.meta.url) {
|
|
81
|
+
const encoded = Buffer.from(JSON.stringify(launch), "utf8").toString("base64url");
|
|
82
|
+
const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
|
|
83
|
+
if (actorId === undefined)
|
|
84
|
+
throw new Error("Akuma wake has no born soul");
|
|
85
|
+
const source = bodyModuleUrl.endsWith(".ts");
|
|
86
|
+
const entry = fileURLToPath(new URL(source ? "../akuma-body.ts" : "../akuma-body.js", bodyModuleUrl));
|
|
74
87
|
return {
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
argv: source ? [process.execPath, "--import", "tsx", entry, encoded] : [process.execPath, entry, encoded],
|
|
89
|
+
cwd: await launchCwd(launch),
|
|
90
|
+
env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
|
|
91
|
+
log: launch.paths.log,
|
|
77
92
|
};
|
|
78
93
|
}
|
|
94
|
+
async function handoffAkumaBody(launch) {
|
|
95
|
+
await handoffProcess(await bodyProcessInput(launch));
|
|
96
|
+
}
|
|
79
97
|
async function prepareBodyStart(paths, leash) {
|
|
80
98
|
const before = await readHeart(paths);
|
|
81
99
|
if (before.pause !== null)
|
|
@@ -132,7 +150,7 @@ async function runBodyTurns(input) {
|
|
|
132
150
|
adapter,
|
|
133
151
|
bodySequence,
|
|
134
152
|
supervisor,
|
|
135
|
-
runtimeSpawn: runtime.spawnChild ??
|
|
153
|
+
runtimeSpawn: runtime.spawnChild ?? handoffAkumaBody,
|
|
136
154
|
body: initial ?? "",
|
|
137
155
|
...(initial === undefined ? {} : { call: initial }),
|
|
138
156
|
launchTells,
|
|
@@ -149,8 +167,8 @@ async function runBodyTurns(input) {
|
|
|
149
167
|
await breakBody(launch.paths, { sequence: bodySequence, end: "put-down", at: runtime.now() });
|
|
150
168
|
return;
|
|
151
169
|
}
|
|
152
|
-
if (result.kind === "resume-unsupported"
|
|
153
|
-
|
|
170
|
+
if (result.kind === "resume-unsupported" ||
|
|
171
|
+
(await persistTurn(launch.paths, result.turnSequence, result, runtime.now())) === "failed") {
|
|
154
172
|
await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
|
|
155
173
|
return;
|
|
156
174
|
}
|
|
@@ -158,18 +176,38 @@ async function runBodyTurns(input) {
|
|
|
158
176
|
initial = undefined;
|
|
159
177
|
}
|
|
160
178
|
}
|
|
179
|
+
export async function handoffPendingTells(paths, spawn = spawnAkumaBody) {
|
|
180
|
+
try {
|
|
181
|
+
if ((await readHeart(paths)).pending.length === 0)
|
|
182
|
+
return;
|
|
183
|
+
(await spawn({ paths, refuseIfHeld: true })).release();
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
/* a later Heart interaction retries the unchanged pending Tell */
|
|
187
|
+
}
|
|
188
|
+
}
|
|
161
189
|
export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()) {
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
190
|
+
const acquired = await takeLeash(launch.paths, launch.refuseIfHeld);
|
|
191
|
+
if (acquired === "held")
|
|
192
|
+
return "held";
|
|
193
|
+
if (acquired === "absent")
|
|
164
194
|
return;
|
|
195
|
+
const leash = acquired;
|
|
196
|
+
let bodyStarted = false;
|
|
165
197
|
try {
|
|
166
198
|
const soul = await bornSoul(launch, leash, runtime.now());
|
|
167
199
|
if (soul === null)
|
|
168
200
|
return;
|
|
201
|
+
if (launch.seed === undefined && launch.initialBody === undefined) {
|
|
202
|
+
const [heart, requests] = await Promise.all([readHeart(launch.paths), readNonterminalRequests(launch.paths)]);
|
|
203
|
+
if (heart.pending.length === 0 && requests.length === 0)
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
169
206
|
const selected = adapter ?? (await resolveProviderExecution(soul.provider)).adapter;
|
|
170
|
-
if (!await prepareBodyStart(launch.paths, leash))
|
|
207
|
+
if (!(await prepareBodyStart(launch.paths, leash)))
|
|
171
208
|
return;
|
|
172
209
|
const body = await leash.recordBody(launch.paths, { leashTakenAt: runtime.now() });
|
|
210
|
+
bodyStarted = true;
|
|
173
211
|
const supervisor = await BodySupervisor.open(launch.paths, body.sequence, leash);
|
|
174
212
|
const execution = { launch, soul, adapter: selected, bodySequence: body.sequence, supervisor, runtime };
|
|
175
213
|
try {
|
|
@@ -181,7 +219,7 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
181
219
|
}
|
|
182
220
|
}
|
|
183
221
|
catch (error) {
|
|
184
|
-
if (!await heartExists(launch.paths))
|
|
222
|
+
if (!(await heartExists(launch.paths)))
|
|
185
223
|
return;
|
|
186
224
|
try {
|
|
187
225
|
await leash.sealIfUnborn(launch.paths, {
|
|
@@ -189,30 +227,102 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
189
227
|
at: runtime.now(),
|
|
190
228
|
});
|
|
191
229
|
}
|
|
192
|
-
catch {
|
|
230
|
+
catch {
|
|
231
|
+
/* the original Body failure remains authoritative */
|
|
232
|
+
}
|
|
193
233
|
throw error;
|
|
194
234
|
}
|
|
195
235
|
finally {
|
|
196
236
|
leash.release();
|
|
237
|
+
if (bodyStarted)
|
|
238
|
+
await handoffPendingTells(launch.paths, runtime.spawnBody ?? spawnAkumaBody);
|
|
197
239
|
}
|
|
198
240
|
}
|
|
199
241
|
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();
|
|
242
|
+
return await spawnDetachedProcess(await bodyProcessInput(launch));
|
|
211
243
|
}
|
|
212
244
|
export async function runAkumaBody(launch, upstream) {
|
|
213
|
-
await driveAkumaBody(launch, undefined, {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
})
|
|
245
|
+
return await driveAkumaBody(launch, undefined, { ...defaultRuntime(), upstream });
|
|
246
|
+
}
|
|
247
|
+
const DIRECT_TELL_WAKE = {
|
|
248
|
+
observeHeart: watchHeart,
|
|
249
|
+
spawn: async (paths) => await spawnAkumaBody({ paths, refuseIfHeld: true }),
|
|
250
|
+
};
|
|
251
|
+
function diagnostic(error) {
|
|
252
|
+
return error instanceof Error ? error.message : String(error);
|
|
253
|
+
}
|
|
254
|
+
function preAdmissionDiagnostic(exit) {
|
|
255
|
+
return exit.code === null ? `pre-admission signal ${exit.signal ?? "unknown"}` : `pre-admission exit ${exit.code}`;
|
|
256
|
+
}
|
|
257
|
+
async function settledWake(paths, tellId, after) {
|
|
258
|
+
const heart = await readHeart(paths);
|
|
259
|
+
if (tellId !== undefined && !heart.pending.some((tell) => tell.id === tellId))
|
|
260
|
+
return { kind: "told" };
|
|
261
|
+
if (heart.latestBody !== null && heart.latestBody.sequence > after) {
|
|
262
|
+
return { kind: "pursuing", bodySequence: heart.latestBody.sequence };
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
function failedChild(exit) {
|
|
267
|
+
return {
|
|
268
|
+
kind: "failed",
|
|
269
|
+
diagnostic: preAdmissionDiagnostic(exit),
|
|
270
|
+
child: { code: exit.code, signal: exit.signal, log: exit.log },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async function awaitWake(paths, tellId, after, child, changed) {
|
|
274
|
+
let nextChange = changed.next();
|
|
275
|
+
for (;;) {
|
|
276
|
+
const settled = await settledWake(paths, tellId, after);
|
|
277
|
+
if (settled !== null) {
|
|
278
|
+
child.release();
|
|
279
|
+
return settled;
|
|
280
|
+
}
|
|
281
|
+
const winner = await Promise.race([
|
|
282
|
+
nextChange.then(({ done }) => ({ kind: done ? "closed" : "changed" }), (error) => ({ kind: "observer-failed", error })),
|
|
283
|
+
child.exited.then((exit) => ({ kind: "exited", exit })),
|
|
284
|
+
]);
|
|
285
|
+
if (winner.kind === "changed") {
|
|
286
|
+
nextChange = changed.next();
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const final = await settledWake(paths, tellId, after);
|
|
290
|
+
if (final !== null) {
|
|
291
|
+
child.release();
|
|
292
|
+
return final;
|
|
293
|
+
}
|
|
294
|
+
if (winner.kind === "exited") {
|
|
295
|
+
return winner.exit.code === LEASH_HELD_EXIT ? { kind: "held" } : failedChild(winner.exit);
|
|
296
|
+
}
|
|
297
|
+
await child.terminate();
|
|
298
|
+
await child.exited;
|
|
299
|
+
return winner.kind === "observer-failed"
|
|
300
|
+
? { kind: "failed", diagnostic: diagnostic(winner.error) }
|
|
301
|
+
: { kind: "failed", diagnostic: "Heart observer closed" };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async function wakePendingTells(paths, tellId, runtime) {
|
|
305
|
+
const watching = new AbortController();
|
|
306
|
+
let changed;
|
|
307
|
+
try {
|
|
308
|
+
const beforeHeart = await readHeart(paths);
|
|
309
|
+
if (tellId === undefined && beforeHeart.pending.length === 0)
|
|
310
|
+
return null;
|
|
311
|
+
const before = beforeHeart.latestBody?.sequence ?? 0;
|
|
312
|
+
changed = await runtime.observeHeart(paths, watching.signal);
|
|
313
|
+
return await awaitWake(paths, tellId, before, await runtime.spawn(paths), changed);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
watching.abort();
|
|
320
|
+
await changed?.return(undefined);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
export async function wakeRecordedTell(paths, tellId, runtime = DIRECT_TELL_WAKE) {
|
|
324
|
+
return {
|
|
325
|
+
admission: { tellId, fact: "recorded" },
|
|
326
|
+
wake: (await wakePendingTells(paths, tellId, runtime)),
|
|
327
|
+
};
|
|
218
328
|
}
|
|
@@ -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
|
}
|