@astrosheep/keiyaku 4.0.3 → 4.0.5
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 +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +15 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -13
- package/build/src/akuma/akuma.d.ts +34 -12
- package/build/src/akuma/akuma.js +118 -63
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +33 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +6 -1
- package/build/src/akuma/body.js +36 -27
- package/build/src/akuma/heart/facts.d.ts +82 -14
- package/build/src/akuma/heart/index.d.ts +5 -18
- package/build/src/akuma/heart/index.js +14 -84
- 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 -26
- package/build/src/akuma/heart/rows.js +0 -50
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +22 -10
- package/build/src/akuma/heart/soul.js +2 -36
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +15 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +5 -2
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +2 -7
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +10 -6
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/acp/index.js +0 -1
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +7 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +0 -2
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -3
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/pi/index.js +11 -4
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +1 -1
- package/build/src/akuma/request-serve.d.ts +87 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +124 -0
- package/build/src/akuma/request-wire.js +380 -0
- package/build/src/akuma/requests.d.ts +41 -41
- package/build/src/akuma/requests.js +75 -374
- package/build/src/akuma-body.d.ts +8 -0
- package/build/src/akuma-body.js +84 -0
- 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.d.ts +4 -1
- package/build/src/cli/accepted.js +39 -9
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +8 -8
- package/build/src/cli/commands/contract.js +3 -3
- package/build/src/cli/commands/install.js +2 -2
- package/build/src/cli/commands/task-invoke.js +98 -9
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/coordinates.d.ts +2 -0
- package/build/src/cli/coordinates.js +12 -5
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +116 -75
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +3 -2
- package/build/src/cli/parse.js +25 -8
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +138 -24
- package/build/src/cli/render/audit.js +18 -8
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +243 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +37 -2
- 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 +3 -1
- package/build/src/cli/result.d.ts +52 -9
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/contract-worktree.d.ts +1 -2
- package/build/src/contract-worktree.js +7 -9
- 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/dispatch/index.js +50 -39
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +7 -8
- package/build/src/git/integration.js +62 -9
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +26 -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 +8 -2
- package/build/src/git/reconcile.js +63 -14
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +4 -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 +1 -1
- package/build/src/git/target-placement.js +9 -7
- package/build/src/git/tender.d.ts +20 -5
- package/build/src/git/tender.js +45 -13
- package/build/src/git/terminal-seal.d.ts +16 -2
- package/build/src/git/terminal-seal.js +15 -5
- package/build/src/git/workspace.d.ts +2 -1
- package/build/src/git/workspace.js +13 -5
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +27 -23
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/audit.d.ts +2 -1
- package/build/src/library/audit.js +1 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +4 -4
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +5 -1
- package/build/src/library/configuration.js +71 -18
- package/build/src/library/contract.d.ts +70 -3
- package/build/src/library/contract.js +213 -27
- package/build/src/library/delivery.d.ts +1 -1
- package/build/src/library/fleet.d.ts +70 -12
- package/build/src/library/fleet.js +236 -56
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +1 -5
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/library/mutation.d.ts +2 -1
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +3 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +3 -1
- package/build/src/library/repo.js +8 -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 +140 -0
- package/build/src/protocol/bind.d.ts +2 -3
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/completion.d.ts +32 -0
- package/build/src/protocol/completion.js +130 -0
- package/build/src/protocol/deliver.d.ts +37 -0
- package/build/src/protocol/deliver.js +160 -0
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -1
- package/build/src/protocol/operations.d.ts +65 -198
- package/build/src/protocol/operations.js +21 -634
- package/build/src/protocol/placement.d.ts +4 -13
- package/build/src/protocol/placement.js +3 -15
- package/build/src/protocol/read/status.d.ts +4 -2
- package/build/src/protocol/read/status.js +27 -15
- 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 +33 -0
- package/build/src/protocol/review.js +148 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/fence.d.ts +1 -1
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +2 -1
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -4
- package/build/src/task/board.js +73 -29
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +11 -4
- package/build/src/task/index.d.ts +4 -4
- package/build/src/task/index.js +8 -131
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +39 -0
- package/build/src/task/mutation.js +85 -0
- package/build/src/task/operations.d.ts +4 -19
- package/build/src/task/operations.js +64 -18
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/task/store.js +0 -2
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/verification/execution.js +2 -1
- package/build/src/workspace-place.d.ts +1 -4
- package/build/src/workspace-place.js +0 -15
- package/package.json +5 -3
|
@@ -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,20 +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
|
-
archetype: string;
|
|
46
|
-
body: string;
|
|
47
|
-
cwd: string | null;
|
|
48
|
-
world: string;
|
|
49
|
-
recipe_json: string;
|
|
50
|
-
admitted_at: string;
|
|
51
|
-
state: RequestFact["state"];
|
|
52
|
-
child: string | null;
|
|
53
|
-
diagnostic: string | null;
|
|
54
|
-
evidence: string | null;
|
|
55
|
-
}>;
|
|
56
41
|
export type ActivityRow = Readonly<{
|
|
57
42
|
sequence: number;
|
|
58
43
|
turn_sequence: number;
|
|
@@ -78,7 +63,6 @@ export declare function decodeSessionRow(row: SessionRow): SessionFact;
|
|
|
78
63
|
export declare function decodeBodyRow(row: BodyRow): BodyFact;
|
|
79
64
|
export declare function decodeTurnRow(row: TurnRow): TurnFact;
|
|
80
65
|
export declare function decodeCallRow(row: CallRow): CallFact;
|
|
81
|
-
export declare function decodeRequestRow(row: RequestRow): RequestFact;
|
|
82
66
|
export declare function encodeActivityEvent(event: unknown): string;
|
|
83
67
|
export declare function decodeActivityRow(row: ActivityRow): ActivityFact;
|
|
84
68
|
export declare function sealExists(database: DatabaseSync): boolean;
|
|
@@ -101,15 +85,6 @@ export declare function insertActivityFact(database: DatabaseSync, input: Readon
|
|
|
101
85
|
event: unknown;
|
|
102
86
|
at: string;
|
|
103
87
|
}>): number;
|
|
104
|
-
export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
|
|
105
|
-
admittedAt: string;
|
|
106
|
-
}>): void;
|
|
107
|
-
export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
|
|
108
|
-
export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
|
|
109
|
-
export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
|
|
110
|
-
export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
|
|
111
|
-
export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
|
|
112
|
-
export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
|
|
113
88
|
export declare function insertStopControl(database: DatabaseSync, bodySequence: number, at: string): void;
|
|
114
89
|
export declare function insertPauseControl(database: DatabaseSync, at: string): void;
|
|
115
90
|
export declare function stopFact(database: DatabaseSync): StopFact | null;
|
|
@@ -66,25 +66,6 @@ export function decodeTurnRow(row) {
|
|
|
66
66
|
export function decodeCallRow(row) {
|
|
67
67
|
return { kind: "call", sequence: row.sequence, turnSequence: row.turn_sequence, body: row.body, at: row.at };
|
|
68
68
|
}
|
|
69
|
-
export function decodeRequestRow(row) {
|
|
70
|
-
const input = {
|
|
71
|
-
id: row.id,
|
|
72
|
-
archetype: row.archetype,
|
|
73
|
-
body: row.body,
|
|
74
|
-
...(row.cwd === null ? {} : { cwd: row.cwd }),
|
|
75
|
-
world: row.world,
|
|
76
|
-
recipe: parsed(row.recipe_json),
|
|
77
|
-
admittedAt: row.admitted_at,
|
|
78
|
-
};
|
|
79
|
-
if (row.state === "reserved" || row.state === "served") {
|
|
80
|
-
return { ...input, state: row.state, child: row.child };
|
|
81
|
-
}
|
|
82
|
-
if (row.state === "refused")
|
|
83
|
-
return { ...input, state: row.state, diagnostic: row.diagnostic };
|
|
84
|
-
if (row.state === "voided")
|
|
85
|
-
return { ...input, state: row.state, evidence: row.evidence };
|
|
86
|
-
return { ...input, state: "admitted" };
|
|
87
|
-
}
|
|
88
69
|
export function encodeActivityEvent(event) {
|
|
89
70
|
return json(event);
|
|
90
71
|
}
|
|
@@ -129,37 +110,6 @@ export function insertActivityFact(database, input) {
|
|
|
129
110
|
.run(sequence, input.turnSequence, encodeActivityEvent(input.event), input.at);
|
|
130
111
|
return sequence;
|
|
131
112
|
}
|
|
132
|
-
const REQUEST_COLUMNS = `sequence, id, archetype, body, cwd, world, recipe_json, admitted_at,
|
|
133
|
-
state, child, diagnostic, evidence`;
|
|
134
|
-
export function insertRequestFact(database, input) {
|
|
135
|
-
database.prepare(`INSERT OR IGNORE INTO requests(id, archetype, body, cwd, world, recipe_json, admitted_at, state)
|
|
136
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 'admitted')`).run(input.id, input.archetype, input.body, input.cwd ?? null, input.world, json(input.recipe), input.admittedAt);
|
|
137
|
-
}
|
|
138
|
-
export function requestFact(database, id) {
|
|
139
|
-
const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
|
|
140
|
-
return row === undefined ? null : decodeRequestRow(row);
|
|
141
|
-
}
|
|
142
|
-
export function nonterminalRequestFacts(database) {
|
|
143
|
-
const rows = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
|
|
144
|
-
WHERE state IN ('admitted', 'reserved') ORDER BY sequence`).all();
|
|
145
|
-
return rows.map(decodeRequestRow);
|
|
146
|
-
}
|
|
147
|
-
export function updateRequestReserved(database, id, child) {
|
|
148
|
-
database.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
|
|
149
|
-
.run(child, id);
|
|
150
|
-
}
|
|
151
|
-
export function updateRequestServed(database, id, child) {
|
|
152
|
-
database.prepare(`UPDATE requests SET state = 'served', child = ?
|
|
153
|
-
WHERE id = ? AND state = 'reserved'`).run(child, id);
|
|
154
|
-
}
|
|
155
|
-
export function updateRequestRefused(database, id, diagnostic) {
|
|
156
|
-
database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
|
|
157
|
-
WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
|
|
158
|
-
}
|
|
159
|
-
export function updateRequestVoided(database, id, evidence) {
|
|
160
|
-
database.prepare(`UPDATE requests SET state = 'voided', child = NULL, evidence = ?
|
|
161
|
-
WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
|
|
162
|
-
}
|
|
163
113
|
export function insertStopControl(database, bodySequence, at) {
|
|
164
114
|
database.prepare("INSERT OR IGNORE INTO control(kind, value_json, at) VALUES ('stop', ?, ?)")
|
|
165
115
|
.run(json({ bodySequence }), at);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DatabaseSync } from "node:sqlite";
|
|
2
2
|
export declare function assertHeartSchemaVersion(database: DatabaseSync): void;
|
|
3
3
|
export declare function assertLeashSchemaVersion(database: DatabaseSync): void;
|
|
4
|
-
export declare const HEART_SCHEMA = "\n CREATE TABLE IF NOT EXISTS akuma_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version =
|
|
4
|
+
export declare const HEART_SCHEMA = "\n CREATE TABLE IF NOT EXISTS akuma_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version = 19)\n ) STRICT;\n INSERT OR IGNORE INTO akuma_schema(singleton, version) VALUES (1, 19);\n CREATE TABLE IF NOT EXISTS soul (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n soul_json TEXT NOT NULL CHECK (json_valid(soul_json))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS bodies (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n leash_taken_at TEXT NOT NULL,\n hung_diagnostic TEXT,\n hung_at TEXT,\n end TEXT CHECK (end IN ('exited', 'broke-off', 'put-down')),\n ended_at TEXT,\n CHECK ((hung_diagnostic IS NULL AND hung_at IS NULL) OR (hung_diagnostic IS NOT NULL AND hung_at IS NOT NULL AND end IS NULL))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS sessions (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n coordinate_json TEXT NOT NULL CHECK (json_valid(coordinate_json)),\n cwd TEXT NOT NULL,\n options_json TEXT NOT NULL CHECK (json_valid(options_json)),\n admitted_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS turns (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n body_sequence INTEGER NOT NULL REFERENCES bodies(sequence),\n started_at TEXT NOT NULL,\n end_sequence INTEGER UNIQUE REFERENCES timeline(sequence) ON DELETE SET NULL,\n outcome TEXT CHECK (outcome IN ('answered', 'failed')),\n history_id TEXT UNIQUE,\n session_json TEXT CHECK (session_json IS NULL OR json_valid(session_json)),\n answer TEXT,\n diagnostic TEXT,\n completed_at TEXT,\n CHECK (\n (outcome = 'answered' AND session_json IS NOT NULL AND answer IS NOT NULL AND diagnostic IS NULL)\n OR (outcome = 'failed' AND history_id IS NULL AND session_json IS NULL AND answer IS NULL AND diagnostic IS NOT NULL)\n OR (outcome IS NULL AND end_sequence IS NULL AND history_id IS NULL AND session_json IS NULL AND answer IS NULL AND diagnostic IS NULL AND completed_at IS NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS timeline (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('turn-start', 'call', 'activity', 'tell', 'turn-end'))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS calls (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS activity (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n event_json TEXT NOT NULL CHECK (json_valid(event_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tells (\n id TEXT PRIMARY KEY,\n sequence INTEGER NOT NULL UNIQUE REFERENCES timeline(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n recorded_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_deliveries (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n tell_id TEXT NOT NULL REFERENCES tells(id) ON DELETE CASCADE,\n route TEXT NOT NULL CHECK (route IN ('launch', 'live')),\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT NOT NULL,\n receipt TEXT CHECK (receipt IN ('unavailable', 'required')),\n delivered_at TEXT NOT NULL,\n CHECK ((route = 'launch' AND receipt IS NULL) OR (route = 'live' AND receipt IS NOT NULL)),\n UNIQUE (tell_id, turn_sequence, fence)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_receipts (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n evidence TEXT NOT NULL CHECK (evidence IN ('exact', 'fence')),\n tell_id TEXT REFERENCES tells(id) ON DELETE CASCADE,\n turn_sequence INTEGER REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT,\n kind TEXT NOT NULL,\n received_at TEXT NOT NULL,\n CHECK (\n (evidence = 'exact' AND tell_id IS NOT NULL AND turn_sequence IS NULL AND fence IS NULL)\n OR (evidence = 'fence' AND tell_id IS NULL AND turn_sequence IS NOT NULL AND fence IS NOT NULL)\n )\n ) STRICT;\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_exact\n ON tell_receipts(tell_id, kind) WHERE evidence = 'exact';\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_fence\n ON tell_receipts(turn_sequence, fence, kind) WHERE evidence = 'fence';\n CREATE TABLE IF NOT EXISTS requests (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n requester TEXT NOT NULL,\n action TEXT NOT NULL CHECK (\n action IN (\n 'akuma.call', 'akuma.wait', 'akuma.tell', 'akuma.kill',\n 'contract.deliver', 'contract.review',\n 'task.add', 'task.addDocument', 'task.compose', 'task.update', 'task.start',\n 'task.stop', 'task.hold', 'task.resume', 'task.done', 'task.drop'\n )\n ),\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n admitted_at TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('admitted', 'reserved', 'served', 'refused', 'voided')),\n child TEXT,\n service_json TEXT CHECK (service_json IS NULL OR json_valid(service_json)),\n diagnostic TEXT,\n evidence TEXT,\n CHECK (\n (state = 'admitted' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'reserved' AND action = 'akuma.call'\n AND child IS NOT NULL AND service_json IS NULL\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'served'\n AND ((action = 'akuma.call' AND child IS NOT NULL AND service_json IS NULL)\n OR (action != 'akuma.call' AND child IS NULL AND service_json IS NOT NULL))\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'refused' AND child IS NULL AND service_json IS NULL AND diagnostic IS NOT NULL AND evidence IS NULL)\n OR (state = 'voided' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS control (\n kind TEXT PRIMARY KEY CHECK (kind IN ('stop', 'pause')),\n value_json TEXT NOT NULL CHECK (json_valid(value_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS kills (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n body_sequence INTEGER NOT NULL UNIQUE REFERENCES bodies(sequence),\n evidence TEXT NOT NULL CHECK (evidence = 'killed'),\n at TEXT NOT NULL\n ) STRICT;\n";
|
|
5
5
|
export declare const LEASH_SCHEMA = "\n PRAGMA journal_mode=DELETE;\n CREATE TABLE IF NOT EXISTS leash_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version = 4)\n ) STRICT;\n INSERT OR IGNORE INTO leash_schema(singleton, version) VALUES (1, 4);\n CREATE TABLE IF NOT EXISTS seal (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n evidence TEXT NOT NULL,\n at TEXT NOT NULL\n ) STRICT;\n";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const HEART_SCHEMA_VERSION =
|
|
1
|
+
const HEART_SCHEMA_VERSION = 19;
|
|
2
2
|
const LEASH_SCHEMA_VERSION = 4;
|
|
3
3
|
function assertSchemaVersion(database, table, expected) {
|
|
4
4
|
const row = database.prepare(`SELECT version FROM ${table} WHERE singleton = 1`).get();
|
|
@@ -108,21 +108,33 @@ export const HEART_SCHEMA = `
|
|
|
108
108
|
CREATE TABLE IF NOT EXISTS requests (
|
|
109
109
|
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
110
|
id TEXT NOT NULL UNIQUE,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
requester TEXT NOT NULL,
|
|
112
|
+
action TEXT NOT NULL CHECK (
|
|
113
|
+
action IN (
|
|
114
|
+
'akuma.call', 'akuma.wait', 'akuma.tell', 'akuma.kill',
|
|
115
|
+
'contract.deliver', 'contract.review',
|
|
116
|
+
'task.add', 'task.addDocument', 'task.compose', 'task.update', 'task.start',
|
|
117
|
+
'task.stop', 'task.hold', 'task.resume', 'task.done', 'task.drop'
|
|
118
|
+
)
|
|
119
|
+
),
|
|
120
|
+
payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),
|
|
116
121
|
admitted_at TEXT NOT NULL,
|
|
117
122
|
state TEXT NOT NULL CHECK (state IN ('admitted', 'reserved', 'served', 'refused', 'voided')),
|
|
118
123
|
child TEXT,
|
|
124
|
+
service_json TEXT CHECK (service_json IS NULL OR json_valid(service_json)),
|
|
119
125
|
diagnostic TEXT,
|
|
120
126
|
evidence TEXT,
|
|
121
127
|
CHECK (
|
|
122
|
-
(state = 'admitted' AND child IS NULL AND diagnostic IS NULL AND evidence IS NULL)
|
|
123
|
-
OR (state
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
(state = 'admitted' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)
|
|
129
|
+
OR (state = 'reserved' AND action = 'akuma.call'
|
|
130
|
+
AND child IS NOT NULL AND service_json IS NULL
|
|
131
|
+
AND diagnostic IS NULL AND evidence IS NULL)
|
|
132
|
+
OR (state = 'served'
|
|
133
|
+
AND ((action = 'akuma.call' AND child IS NOT NULL AND service_json IS NULL)
|
|
134
|
+
OR (action != 'akuma.call' AND child IS NULL AND service_json IS NOT NULL))
|
|
135
|
+
AND diagnostic IS NULL AND evidence IS NULL)
|
|
136
|
+
OR (state = 'refused' AND child IS NULL AND service_json IS NULL AND diagnostic IS NOT NULL AND evidence IS NULL)
|
|
137
|
+
OR (state = 'voided' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)
|
|
126
138
|
)
|
|
127
139
|
) STRICT;
|
|
128
140
|
CREATE TABLE IF NOT EXISTS control (
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { parseAkuId } from "../identity.js";
|
|
2
2
|
import { decodeProviderOptions, decodeProviderRecipe, decodeReadonlyRestraint, } from "../provider-recipe.js";
|
|
3
|
+
import { effectiveAllowedActions } from "../allowed.js";
|
|
3
4
|
function record(value) {
|
|
4
5
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
5
6
|
? value
|
|
6
7
|
: null;
|
|
7
8
|
}
|
|
8
|
-
function unknownKey(value, allowed) {
|
|
9
|
-
return Object.keys(value).find((key) => !allowed.includes(key));
|
|
10
|
-
}
|
|
11
9
|
function nonblank(value, what) {
|
|
12
10
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13
11
|
throw new Error(`Akuma soul ${what} must be a nonblank string`);
|
|
@@ -27,14 +25,9 @@ function decodeOrigin(value) {
|
|
|
27
25
|
if (origin === null)
|
|
28
26
|
throw new Error("Akuma soul origin must be an object");
|
|
29
27
|
if (origin.kind === "direct") {
|
|
30
|
-
if (unknownKey(origin, ["kind"]) !== undefined)
|
|
31
|
-
throw new Error("Akuma soul direct origin must have only kind");
|
|
32
28
|
return { kind: "direct" };
|
|
33
29
|
}
|
|
34
30
|
if (origin.kind === "request") {
|
|
35
|
-
if (unknownKey(origin, ["kind", "parent", "requestId"]) !== undefined) {
|
|
36
|
-
throw new Error("Akuma soul request origin must have only kind, parent, and requestId");
|
|
37
|
-
}
|
|
38
31
|
return {
|
|
39
32
|
kind: "request",
|
|
40
33
|
parent: soulAkuIdentity(origin.parent, "request parent").id,
|
|
@@ -42,9 +35,6 @@ function decodeOrigin(value) {
|
|
|
42
35
|
};
|
|
43
36
|
}
|
|
44
37
|
if (origin.kind === "fork") {
|
|
45
|
-
if (unknownKey(origin, ["at", "kind", "parent"]) !== undefined) {
|
|
46
|
-
throw new Error("Akuma soul fork origin must have only kind, parent, and at");
|
|
47
|
-
}
|
|
48
38
|
return {
|
|
49
39
|
kind: "fork",
|
|
50
40
|
parent: soulAkuIdentity(origin.parent, "fork parent").id,
|
|
@@ -53,34 +43,10 @@ function decodeOrigin(value) {
|
|
|
53
43
|
}
|
|
54
44
|
throw new Error("Akuma soul origin must be direct, request, or fork");
|
|
55
45
|
}
|
|
56
|
-
function decodeConfinement(value) {
|
|
57
|
-
const confinement = record(value);
|
|
58
|
-
if (confinement === null)
|
|
59
|
-
throw new Error("Akuma soul confinement must be an object");
|
|
60
|
-
if (confinement.kind === "unconfined") {
|
|
61
|
-
if (unknownKey(confinement, ["kind"]) !== undefined)
|
|
62
|
-
throw new Error("Akuma soul unconfined confinement must have only kind");
|
|
63
|
-
return { kind: "unconfined" };
|
|
64
|
-
}
|
|
65
|
-
if (confinement.kind === "declared") {
|
|
66
|
-
if (unknownKey(confinement, ["kind", "writableRoots"]) !== undefined) {
|
|
67
|
-
throw new Error("Akuma soul declared confinement must have only kind and writableRoots");
|
|
68
|
-
}
|
|
69
|
-
if (!Array.isArray(confinement.writableRoots)
|
|
70
|
-
|| confinement.writableRoots.some((root) => typeof root !== "string" || root.trim().length === 0)) {
|
|
71
|
-
throw new Error("Akuma soul declared confinement writableRoots must be nonblank strings");
|
|
72
|
-
}
|
|
73
|
-
return { kind: "declared", writableRoots: [...confinement.writableRoots] };
|
|
74
|
-
}
|
|
75
|
-
throw new Error("Akuma soul confinement must be unconfined or declared");
|
|
76
|
-
}
|
|
77
46
|
function validateSoul(value) {
|
|
78
47
|
const soul = record(value);
|
|
79
48
|
if (soul === null)
|
|
80
49
|
throw new Error("Akuma soul must be an object");
|
|
81
|
-
if (unknownKey(soul, ["archetype", "confinement", "createdAt", "cwd", "description", "id", "options", "origin", "provider", "readonly"]) !== undefined) {
|
|
82
|
-
throw new Error("Akuma soul has an unknown field");
|
|
83
|
-
}
|
|
84
50
|
const identity = soulAkuIdentity(soul.id, "id");
|
|
85
51
|
const id = identity.id;
|
|
86
52
|
const archetype = nonblank(soul.archetype, "name");
|
|
@@ -102,7 +68,7 @@ function validateSoul(value) {
|
|
|
102
68
|
...(restraint === undefined ? {} : { readonly: restraint }),
|
|
103
69
|
cwd: nonblank(soul.cwd, "cwd"),
|
|
104
70
|
origin: decodeOrigin(soul.origin),
|
|
105
|
-
|
|
71
|
+
allowed: effectiveAllowedActions(soul.allowed),
|
|
106
72
|
createdAt: nonblank(soul.createdAt, "createdAt"),
|
|
107
73
|
};
|
|
108
74
|
}
|
|
@@ -9,6 +9,7 @@ export declare class HeartAbsentError extends Error {
|
|
|
9
9
|
export declare function isHeartAbsent(error: unknown): error is HeartAbsentError;
|
|
10
10
|
export declare function withHeart<T>(paths: AkumaPaths, body: (database: DatabaseSync) => T): Promise<T>;
|
|
11
11
|
export declare function transaction<T>(database: DatabaseSync, body: () => T): T;
|
|
12
|
+
export declare function readTransaction<T>(database: DatabaseSync, body: () => T): T;
|
|
12
13
|
export declare function initializeHeart(paths: AkumaPaths): Promise<void>;
|
|
13
14
|
export declare class HeldAkumaLeash {
|
|
14
15
|
private readonly database;
|
|
@@ -84,6 +84,21 @@ export function transaction(database, body) {
|
|
|
84
84
|
throw error;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
+
export function readTransaction(database, body) {
|
|
88
|
+
database.exec("BEGIN DEFERRED");
|
|
89
|
+
try {
|
|
90
|
+
const result = body();
|
|
91
|
+
database.exec("COMMIT");
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
try {
|
|
96
|
+
database.exec("ROLLBACK");
|
|
97
|
+
}
|
|
98
|
+
catch { /* preserve the original failure */ }
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
87
102
|
export async function initializeHeart(paths) {
|
|
88
103
|
const heart = new DatabaseSync(paths.heart);
|
|
89
104
|
try {
|
|
@@ -8,8 +8,4 @@ export type ActivityFactSlice = Readonly<{
|
|
|
8
8
|
highest: number | null;
|
|
9
9
|
}>;
|
|
10
10
|
export declare function pruneActivityFacts(database: DatabaseSync, limit: number): void;
|
|
11
|
-
export declare function activityFactSlice(database: DatabaseSync
|
|
12
|
-
before?: number;
|
|
13
|
-
since?: number;
|
|
14
|
-
limit: number;
|
|
15
|
-
}>): ActivityFactSlice;
|
|
11
|
+
export declare function activityFactSlice(database: DatabaseSync): ActivityFactSlice;
|
|
@@ -64,23 +64,14 @@ function decodeTimelineRow(database, row) {
|
|
|
64
64
|
}
|
|
65
65
|
return decodeTellAtSequence(database, row.sequence);
|
|
66
66
|
}
|
|
67
|
-
export function activityFactSlice(database
|
|
67
|
+
export function activityFactSlice(database) {
|
|
68
68
|
const bounds = database.prepare("SELECT MIN(sequence) AS lowest, MAX(sequence) AS highest FROM timeline")
|
|
69
69
|
.get();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
rows = database.prepare(`SELECT sequence, kind FROM timeline WHERE sequence > ? ORDER BY sequence LIMIT ?`)
|
|
78
|
-
.all(input.since, input.limit);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
rows = database.prepare(`SELECT * FROM (
|
|
82
|
-
SELECT sequence, kind FROM timeline ORDER BY sequence DESC LIMIT ?
|
|
83
|
-
) ORDER BY sequence`).all(input.limit);
|
|
84
|
-
}
|
|
85
|
-
return { rows: rows.map((row) => decodeTimelineRow(database, row)), lowestRetained: bounds.lowest, highest: bounds.highest };
|
|
70
|
+
const rows = database.prepare("SELECT sequence, kind FROM timeline ORDER BY sequence")
|
|
71
|
+
.all();
|
|
72
|
+
return {
|
|
73
|
+
rows: rows.map((row) => decodeTimelineRow(database, row)),
|
|
74
|
+
lowestRetained: bounds.lowest,
|
|
75
|
+
highest: bounds.highest,
|
|
76
|
+
};
|
|
86
77
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { type AkuId } from "./identity.js";
|
|
2
2
|
import type { WorldRoot } from "../world.js";
|
|
3
3
|
export { Akuma, AkumaHandle, AkumaNotBornError } from "./akuma.js";
|
|
4
|
+
export type { AkumaCallInput } from "./akuma.js";
|
|
5
|
+
export { ALLOWED_ACTIONS } from "./allowed.js";
|
|
6
|
+
export type { AllowedAction, AllowedActions } from "./allowed.js";
|
|
4
7
|
export type { WorldRoot } from "../world.js";
|
|
5
8
|
export declare function probeBornAkuma(worldPath: WorldRoot, id: AkuId): Promise<boolean>;
|
|
6
9
|
export { AkumaArchetypeError, listArchetypeDefinitions, listArchetypes } from "./archetype.js";
|
|
7
10
|
export type { ArchetypeCatalogRow } from "./archetype.js";
|
|
8
11
|
export { AkumaBodyRequestError } from "./requests.js";
|
|
9
12
|
export type { AkumaList, AkumaListInput, AkumaListRow, AkumaStatus, ActivityHistory, ActivityRow, ActivitySnapshot, ActivitySnapshotEntry, ForkReceipt, InterruptReceipt, TellResult, UnbornAkumaListRow, } from "./akuma.js";
|
|
10
|
-
export type { ActiveToolRow, ClosedTurn, ClosedTurnRow, CompletedToolRow, HistoryCursor, HistoryPage, IdleSnapshotRow, OpenSnapshotRow, OpenTurn, OpenTurnRow, OutcomeRow, RetainedWindow, Snapshot, SnapshotRow, TellRow, TurnLedger, TurnOutcome, TurnStartRow, UnsettledToolRow, } from "./akuma.js";
|
|
13
|
+
export type { ActiveToolRow, ClosedTurn, ClosedTurnRow, CompletedToolRow, HistoryCursor, HistoryPage, IdleSnapshotRow, OpenSnapshotRow, OpenTurn, OpenTurnRow, OutcomeRow, ReportedFileChange, RetainedWindow, Snapshot, SnapshotRow, TellRow, TurnLedger, TurnOutcome, TurnStartRow, UnsettledToolRow, } from "./akuma.js";
|
|
11
14
|
export type { AgentEvent, ToolCall, ToolEvent, ToolResult } from "./provider.js";
|
|
12
15
|
export type { AkuId } from "./identity.js";
|
|
13
|
-
export type {
|
|
16
|
+
export type { KillEvidence, ResumeCoordinate, TurnFact, } from "./heart/index.js";
|
|
14
17
|
export type { ProviderOptions, ReadonlyRestraint } from "./provider-recipe.js";
|
package/build/src/akuma/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readSoul } from "./heart/index.js";
|
|
2
2
|
import { pathsForAkuId } from "./identity.js";
|
|
3
3
|
export { Akuma, AkumaHandle, AkumaNotBornError } from "./akuma.js";
|
|
4
|
+
export { ALLOWED_ACTIONS } from "./allowed.js";
|
|
4
5
|
export async function probeBornAkuma(worldPath, id) {
|
|
5
6
|
const soul = await readSoul(pathsForAkuId(worldPath, id));
|
|
6
7
|
return soul !== null;
|