@astrosheep/keiyaku 4.5.6 → 4.5.8
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 +10 -0
- 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/opencode.js +3 -2
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +10 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +53 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +28 -49
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +21 -15
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +148 -89
- package/build/src/akuma/abort.d.ts +2 -1
- package/build/src/akuma/abort.js +6 -23
- package/build/src/akuma/akuma-handle.js +4 -2
- package/build/src/akuma/akuma-observe.d.ts +2 -0
- package/build/src/akuma/akuma-observe.js +1 -0
- package/build/src/akuma/akuma-product.js +9 -7
- package/build/src/akuma/akuma.d.ts +372 -9
- package/build/src/akuma/akuma.js +31 -1
- package/build/src/akuma/allowed.d.ts +1 -1
- package/build/src/akuma/allowed.js +1 -0
- package/build/src/akuma/body.d.ts +7 -5
- package/build/src/akuma/body.js +25 -36
- package/build/src/akuma/call-request.d.ts +80 -0
- package/build/src/akuma/call-request.js +211 -0
- package/build/src/akuma/heart/facts.d.ts +6 -78
- package/build/src/akuma/heart/index.d.ts +3 -3
- package/build/src/akuma/heart/index.js +2 -2
- package/build/src/akuma/heart/request-authority.d.ts +6 -4
- package/build/src/akuma/heart/request-authority.js +31 -35
- package/build/src/akuma/heart/request-rows.js +12 -23
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +5 -13
- package/build/src/akuma/heart/storage.d.ts +0 -1
- package/build/src/akuma/heart/storage.js +0 -54
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/projection-read.d.ts +1 -0
- package/build/src/akuma/projection-read.js +8 -1
- package/build/src/akuma/projection.d.ts +856 -137
- package/build/src/akuma/projection.js +190 -0
- package/build/src/akuma/provider.d.ts +30 -3
- package/build/src/akuma/provider.js +104 -0
- package/build/src/akuma/providers/acp/core.d.ts +2 -2
- package/build/src/akuma/providers/acp/core.js +13 -6
- package/build/src/akuma/providers/acp/index.js +5 -4
- package/build/src/akuma/providers/claude/index.d.ts +3 -3
- package/build/src/akuma/providers/claude/index.js +16 -5
- package/build/src/akuma/providers/codex-app-server/events.js +1 -1
- package/build/src/akuma/providers/codex-app-server/index.js +18 -6
- package/build/src/akuma/providers/grok-build/index.js +5 -4
- package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
- package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
- package/build/src/akuma/providers/pi/index.js +42 -14
- package/build/src/akuma/publication.js +75 -13
- package/build/src/akuma/request-lifecycle.d.ts +3 -9
- package/build/src/akuma/request-lifecycle.js +12 -25
- package/build/src/akuma/request-rendezvous.d.ts +26 -0
- package/build/src/akuma/request-rendezvous.js +100 -0
- package/build/src/akuma/request-serve.d.ts +5 -65
- package/build/src/akuma/request-serve.js +92 -204
- package/build/src/akuma/request-wire.d.ts +109 -115
- package/build/src/akuma/request-wire.js +63 -318
- package/build/src/akuma/requests.d.ts +19 -47
- package/build/src/akuma/requests.js +51 -107
- package/build/src/akuma/turn-drive.d.ts +5 -3
- package/build/src/akuma/turn-drive.js +32 -10
- package/build/src/akuma-body.d.ts +7 -2
- package/build/src/akuma-body.js +68 -46
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +2 -2
- package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
- package/build/src/cli/commands/akuma-invoke.js +17 -9
- package/build/src/cli/commands/akuma.js +7 -7
- package/build/src/cli/commands/contract-invoke.d.ts +3 -2
- package/build/src/cli/commands/contract-invoke.js +26 -19
- package/build/src/cli/commands/contract.d.ts +13 -13
- package/build/src/cli/commands/contract.js +32 -17
- package/build/src/cli/commands/install.d.ts +1 -1
- package/build/src/cli/commands/install.js +1 -1
- package/build/src/cli/commands/task-invoke.d.ts +7 -2
- package/build/src/cli/commands/task-invoke.js +67 -158
- package/build/src/cli/commands/task.d.ts +1 -1
- package/build/src/cli/commands/task.js +60 -22
- package/build/src/cli/coordinates.d.ts +2 -0
- package/build/src/cli/coordinates.js +18 -2
- package/build/src/cli/invoke.js +39 -25
- package/build/src/cli/main.js +2 -1
- package/build/src/cli/parse.d.ts +2 -2
- package/build/src/cli/parse.js +37 -22
- package/build/src/cli/render/akuma-activity.d.ts +4 -1
- package/build/src/cli/render/akuma-activity.js +45 -30
- package/build/src/cli/render/akuma-tool.d.ts +5 -1
- package/build/src/cli/render/akuma.js +5 -9
- package/build/src/cli/render/audit.js +1 -1
- package/build/src/cli/render/catalog.js +80 -21
- package/build/src/cli/render/contract-observation.d.ts +2 -0
- package/build/src/cli/render/contract-observation.js +11 -0
- package/build/src/cli/render/contract.js +25 -65
- package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
- package/build/src/cli/render/kanshi-akuma.js +111 -0
- package/build/src/cli/render/kanshi.js +108 -213
- package/build/src/cli/render/receipt.d.ts +1 -2
- package/build/src/cli/render/receipt.js +76 -26
- package/build/src/cli/render/refusal.js +2 -10
- package/build/src/cli/render/region.js +5 -2
- package/build/src/cli/render/settings.d.ts +1 -1
- package/build/src/cli/render/settings.js +27 -8
- package/build/src/cli/render/task.js +28 -10
- package/build/src/cli/render/terminal.d.ts +28 -0
- package/build/src/cli/render/terminal.js +65 -0
- package/build/src/cli/result.d.ts +3 -3
- package/build/src/cli/runtime.js +3 -1
- package/build/src/cli/selectors.d.ts +2 -1
- package/build/src/cli/selectors.js +4 -3
- package/build/src/cli/square-edge.js +120 -36
- package/build/src/cli/usage.js +2 -1
- package/build/src/contract-guidance.d.ts +5 -0
- package/build/src/contract-guidance.js +121 -0
- package/build/src/contract-worktree.d.ts +0 -1
- package/build/src/contract-worktree.js +32 -83
- package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
- package/build/src/coordination/sqlite-transaction-lock.js +5 -0
- package/build/src/core/facts/codec.d.ts +2 -0
- package/build/src/core/facts/codec.js +4 -0
- package/build/src/core/verbs/placement.d.ts +6 -1
- package/build/src/core/verbs/placement.js +5 -3
- package/build/src/dispatch/index.d.ts +0 -2
- package/build/src/dispatch/index.js +54 -55
- package/build/src/git/admission.js +2 -2
- package/build/src/git/hooks.d.ts +9 -15
- package/build/src/git/hooks.js +23 -317
- package/build/src/git/nuke.d.ts +2 -1
- package/build/src/git/nuke.js +36 -18
- package/build/src/git/private-state-seat.d.ts +12 -0
- package/build/src/git/private-state-seat.js +39 -0
- package/build/src/git/process.d.ts +6 -0
- package/build/src/git/process.js +19 -7
- package/build/src/git/read-observation.js +29 -20
- package/build/src/git/reconcile.d.ts +11 -0
- package/build/src/git/reconcile.js +64 -11
- package/build/src/git/repository.d.ts +8 -2
- package/build/src/git/repository.js +42 -21
- package/build/src/git/target-placement.d.ts +1 -0
- package/build/src/git/target-placement.js +5 -6
- package/build/src/git/tender.d.ts +1 -0
- package/build/src/git/terminal-reconcile.d.ts +1 -1
- package/build/src/git/terminal-reconcile.js +22 -20
- package/build/src/git/workspace.d.ts +14 -0
- package/build/src/git/workspace.js +44 -0
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +16 -16
- package/build/src/kanshi/report.d.ts +2 -2
- package/build/src/library/address.d.ts +9 -3
- package/build/src/library/address.js +3 -2
- package/build/src/library/akuma-creation.d.ts +4 -3
- package/build/src/library/akuma-creation.js +19 -13
- package/build/src/library/audit.d.ts +1 -1
- package/build/src/library/audit.js +5 -3
- package/build/src/library/catalog.d.ts +3 -1
- package/build/src/library/catalog.js +20 -6
- package/build/src/library/composition.d.ts +371 -0
- package/build/src/library/composition.js +28 -0
- package/build/src/library/configuration.js +8 -2
- package/build/src/library/continuation.js +66 -30
- package/build/src/library/contract-bind.js +10 -2
- package/build/src/library/contract-forwarding-reconciliation-result.d.ts +96 -0
- package/build/src/library/contract-forwarding-reconciliation-result.js +83 -0
- package/build/src/library/contract-forwarding-result.d.ts +6723 -0
- package/build/src/library/contract-forwarding-result.js +521 -0
- package/build/src/library/contract-forwarding.d.ts +72 -0
- package/build/src/library/contract-forwarding.js +125 -0
- package/build/src/library/contract-handle.d.ts +36 -0
- package/build/src/library/contract-handle.js +313 -0
- package/build/src/library/contract-operations.d.ts +1044 -67
- package/build/src/library/contract-operations.js +190 -118
- package/build/src/library/contract.d.ts +11 -32
- package/build/src/library/contract.js +16 -289
- package/build/src/library/fleet-result.d.ts +1454 -0
- package/build/src/library/fleet-result.js +96 -0
- package/build/src/library/fleet.d.ts +112 -54
- package/build/src/library/fleet.js +211 -71
- package/build/src/library/input.d.ts +2 -0
- package/build/src/library/input.js +4 -0
- package/build/src/library/keiyaku.d.ts +371 -20
- package/build/src/library/keiyaku.js +8 -21
- package/build/src/library/mutation.d.ts +3 -3
- package/build/src/library/mutation.js +5 -9
- package/build/src/library/nuke.d.ts +1 -1
- package/build/src/library/nuke.js +4 -3
- package/build/src/library/reconcile.d.ts +4 -0
- package/build/src/library/reconcile.js +2 -0
- package/build/src/library/refusal.d.ts +0 -9
- package/build/src/library/refusal.js +0 -7
- package/build/src/protocol/amend.js +56 -50
- package/build/src/protocol/attempt.d.ts +2 -0
- package/build/src/protocol/attempt.js +17 -5
- package/build/src/protocol/completion.js +7 -0
- package/build/src/protocol/deliver.js +6 -2
- package/build/src/protocol/operations.d.ts +4 -1
- package/build/src/protocol/operations.js +5 -2
- package/build/src/protocol/placement.d.ts +1 -0
- package/build/src/protocol/placement.js +52 -38
- package/build/src/protocol/read/observation.d.ts +1 -4
- package/build/src/protocol/read/observation.js +2 -15
- package/build/src/protocol/reintegrate.js +62 -58
- package/build/src/protocol/review.js +5 -0
- package/build/src/protocol/run.js +17 -13
- package/build/src/runtime/proc/run.d.ts +8 -0
- package/build/src/runtime/proc/run.js +27 -0
- package/build/src/runtime/proc/termination.js +26 -22
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/holder.d.ts +2 -1
- package/build/src/settlement/holder.js +15 -4
- package/build/src/settlement/settle.js +99 -33
- package/build/src/task/board.d.ts +68 -27
- package/build/src/task/board.js +31 -1
- package/build/src/task/catalog.d.ts +4 -0
- package/build/src/task/catalog.js +23 -0
- package/build/src/task/compose-language.d.ts +4 -0
- package/build/src/task/compose-language.js +40 -7
- package/build/src/task/compose-parser.d.ts +2 -1
- package/build/src/task/compose-parser.js +5 -3
- package/build/src/task/compose.d.ts +1 -0
- package/build/src/task/compose.js +4 -1
- package/build/src/task/context.d.ts +7 -1
- package/build/src/task/context.js +25 -6
- package/build/src/task/index.d.ts +18 -5
- package/build/src/task/index.js +97 -16
- package/build/src/task/mutation-result.d.ts +923 -0
- package/build/src/task/mutation-result.js +140 -0
- package/build/src/task/mutation.d.ts +552 -28
- package/build/src/task/mutation.js +304 -97
- package/build/src/task/operations.d.ts +1 -4
- package/build/src/task/operations.js +0 -6
- package/build/src/task/public.d.ts +2 -0
- package/build/src/task/public.js +1 -0
- package/build/src/workspace-place.d.ts +7 -2
- package/build/src/workspace-place.js +34 -24
- package/build/src/world.d.ts +1 -0
- package/build/src/world.js +11 -0
- package/package.json +8 -6
- package/build/src/akuma/request-execution.d.ts +0 -8
- package/build/src/akuma/request-execution.js +0 -98
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Soul } from "./heart/index.js";
|
|
2
|
+
import { type AkuId, type AkumaPaths } from "./identity.js";
|
|
3
|
+
import { type ErasedRequestCommand, type RequestCommand } from "./request-wire.js";
|
|
4
|
+
import type { OwnedProcess } from "../runtime/proc/run.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { type WorldRoot } from "../world.js";
|
|
7
|
+
declare const akumaCallRecipeSchema: z.ZodObject<{
|
|
8
|
+
description: z.ZodOptional<z.ZodString>;
|
|
9
|
+
allowed: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./allowed.js").AllowedActions, unknown>>;
|
|
10
|
+
provider: z.ZodPipe<z.ZodUnknown, z.ZodTransform<Readonly<{
|
|
11
|
+
name: string;
|
|
12
|
+
kind: "acp" | "claude-agent-sdk" | "codex-app-server" | "grok-build" | "opencode-sdk" | "pi";
|
|
13
|
+
executable?: string;
|
|
14
|
+
config?: Readonly<Record<string, unknown>>;
|
|
15
|
+
env?: Readonly<Record<string, string>>;
|
|
16
|
+
}>, unknown>>;
|
|
17
|
+
options: z.ZodPipe<z.ZodUnknown, z.ZodTransform<Readonly<{
|
|
18
|
+
model?: string;
|
|
19
|
+
effort?: string;
|
|
20
|
+
readonly?: true;
|
|
21
|
+
network?: "disabled" | "enabled";
|
|
22
|
+
systemPrompt?: string;
|
|
23
|
+
systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
|
|
24
|
+
}>, unknown>>;
|
|
25
|
+
readonly: z.ZodOptional<z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./provider-recipe.js").ReadonlyRestraint, unknown>>>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
declare const akumaCallPayloadSchema: z.ZodObject<{
|
|
28
|
+
world: z.ZodString;
|
|
29
|
+
archetype: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
30
|
+
body: z.ZodString;
|
|
31
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
32
|
+
recipe: z.ZodObject<{
|
|
33
|
+
description: z.ZodOptional<z.ZodString>;
|
|
34
|
+
allowed: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./allowed.js").AllowedActions, unknown>>;
|
|
35
|
+
provider: z.ZodPipe<z.ZodUnknown, z.ZodTransform<Readonly<{
|
|
36
|
+
name: string;
|
|
37
|
+
kind: "acp" | "claude-agent-sdk" | "codex-app-server" | "grok-build" | "opencode-sdk" | "pi";
|
|
38
|
+
executable?: string;
|
|
39
|
+
config?: Readonly<Record<string, unknown>>;
|
|
40
|
+
env?: Readonly<Record<string, string>>;
|
|
41
|
+
}>, unknown>>;
|
|
42
|
+
options: z.ZodPipe<z.ZodUnknown, z.ZodTransform<Readonly<{
|
|
43
|
+
model?: string;
|
|
44
|
+
effort?: string;
|
|
45
|
+
readonly?: true;
|
|
46
|
+
network?: "disabled" | "enabled";
|
|
47
|
+
systemPrompt?: string;
|
|
48
|
+
systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
|
|
49
|
+
}>, unknown>>;
|
|
50
|
+
readonly: z.ZodOptional<z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./provider-recipe.js").ReadonlyRestraint, unknown>>>;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
export type AkumaCallRecipe = z.infer<typeof akumaCallRecipeSchema>;
|
|
54
|
+
export type AkumaCallRequest = z.infer<typeof akumaCallPayloadSchema> & Readonly<{
|
|
55
|
+
action: "akuma.call";
|
|
56
|
+
}>;
|
|
57
|
+
export type AkumaCallRequestChildLaunch = Readonly<{
|
|
58
|
+
paths: AkumaPaths;
|
|
59
|
+
seed: Omit<Soul, "createdAt">;
|
|
60
|
+
initialBody: string;
|
|
61
|
+
}>;
|
|
62
|
+
type CallExecutionContext = Readonly<{
|
|
63
|
+
id: string;
|
|
64
|
+
requester: string;
|
|
65
|
+
signal: AbortSignal;
|
|
66
|
+
world: WorldRoot;
|
|
67
|
+
paths: AkumaPaths;
|
|
68
|
+
parent: Soul;
|
|
69
|
+
spawn(launch: AkumaCallRequestChildLaunch): Promise<OwnedProcess | void>;
|
|
70
|
+
admissionOpen(): boolean;
|
|
71
|
+
}>;
|
|
72
|
+
export declare function decodeAkumaCallRequest(value: unknown): AkumaCallRequest | null;
|
|
73
|
+
export declare function akumaCallRequestCommand(): RequestCommand<AkumaCallRequest, AkuId, never, AkuId, CallExecutionContext>;
|
|
74
|
+
export declare function akumaCallRequestCommands(): Readonly<Record<"akuma.call", ErasedRequestCommand>>;
|
|
75
|
+
export declare function requestForwardedAkumaCall(input: Omit<AkumaCallRequest, "action"> & Readonly<{
|
|
76
|
+
directory: string;
|
|
77
|
+
id: string;
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
}>): Promise<AkuId>;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { isAbsolute, resolve } from "node:path";
|
|
2
|
+
import { clipAllowedActions, decodeAllowedActions } from "./allowed.js";
|
|
3
|
+
import { refuseRequest, reserveRequest } from "./heart/index.js";
|
|
4
|
+
import { archetypeName, parseAkuId } from "./identity.js";
|
|
5
|
+
import { publishAkuma } from "./publication.js";
|
|
6
|
+
import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
|
|
7
|
+
import { decodeProviderExecution } from "./providers/index.js";
|
|
8
|
+
import { requestBodyCommand } from "./request-rendezvous.js";
|
|
9
|
+
import { eraseRequestCommand } from "./request-wire.js";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { World } from "../world.js";
|
|
12
|
+
function schemaDecode(decode, message) {
|
|
13
|
+
return z.unknown().transform((value, context) => {
|
|
14
|
+
try {
|
|
15
|
+
return decode(value);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
context.addIssue({ code: "custom", message });
|
|
19
|
+
return z.NEVER;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const absolutePathSchema = z.string().refine((value) => isAbsolute(value) && resolve(value) === value);
|
|
24
|
+
const akuIdSchema = z.string().transform((value, context) => {
|
|
25
|
+
try {
|
|
26
|
+
const id = parseAkuId(value).id;
|
|
27
|
+
if (id !== value) {
|
|
28
|
+
context.addIssue({ code: "custom", message: "expected canonical AkuId" });
|
|
29
|
+
return z.NEVER;
|
|
30
|
+
}
|
|
31
|
+
return id;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
context.addIssue({ code: "custom", message: "expected canonical AkuId" });
|
|
35
|
+
return z.NEVER;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const archetypeSchema = z.string().transform((value, context) => {
|
|
39
|
+
try {
|
|
40
|
+
return archetypeName(value);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
context.addIssue({ code: "custom", message: "expected archetype" });
|
|
44
|
+
return z.NEVER;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const allowedActionsSchema = schemaDecode(decodeAllowedActions, "expected allowed actions");
|
|
48
|
+
const providerExecutionSchema = schemaDecode(decodeProviderExecution, "expected provider execution");
|
|
49
|
+
const providerOptionsSchema = schemaDecode(decodeProviderOptions, "expected provider options");
|
|
50
|
+
const readonlyRestraintSchema = schemaDecode(decodeReadonlyRestraint, "expected readonly restraint");
|
|
51
|
+
const akumaCallRecipeSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
description: z.string().trim().min(1).optional(),
|
|
54
|
+
allowed: allowedActionsSchema,
|
|
55
|
+
provider: providerExecutionSchema,
|
|
56
|
+
options: providerOptionsSchema,
|
|
57
|
+
readonly: readonlyRestraintSchema.optional(),
|
|
58
|
+
})
|
|
59
|
+
.strict()
|
|
60
|
+
.superRefine((recipe, context) => {
|
|
61
|
+
if ((recipe.options.readonly === true) !== (recipe.readonly !== undefined)) {
|
|
62
|
+
context.addIssue({ code: "custom", message: "readonly recipe fields disagree" });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const akumaCallPayloadSchema = z
|
|
66
|
+
.object({
|
|
67
|
+
world: absolutePathSchema,
|
|
68
|
+
archetype: archetypeSchema,
|
|
69
|
+
body: z.string(),
|
|
70
|
+
cwd: absolutePathSchema.optional(),
|
|
71
|
+
recipe: akumaCallRecipeSchema,
|
|
72
|
+
})
|
|
73
|
+
.strict();
|
|
74
|
+
function carriesLaunchWorld(value) {
|
|
75
|
+
const upstream = object(value);
|
|
76
|
+
return upstream !== null && typeof upstream.launchWorld === "function";
|
|
77
|
+
}
|
|
78
|
+
function decodeCallExecutionContext(value) {
|
|
79
|
+
const context = object(value);
|
|
80
|
+
const paths = object(context?.paths);
|
|
81
|
+
const parent = object(context?.parent);
|
|
82
|
+
const parentId = akuIdSchema.safeParse(parent?.id);
|
|
83
|
+
if (context === null ||
|
|
84
|
+
typeof context.id !== "string" ||
|
|
85
|
+
context.id.trim() === "" ||
|
|
86
|
+
typeof context.requester !== "string" ||
|
|
87
|
+
typeof context.signal !== "object" ||
|
|
88
|
+
context.signal === null ||
|
|
89
|
+
typeof context.signal.throwIfAborted !== "function" ||
|
|
90
|
+
paths === null ||
|
|
91
|
+
!["directory", "heart", "leash", "log", "requests"].every((key) => typeof paths[key] === "string") ||
|
|
92
|
+
parent === null ||
|
|
93
|
+
!parentId.success ||
|
|
94
|
+
typeof parent.cwd !== "string" ||
|
|
95
|
+
!Array.isArray(parent.allowed) ||
|
|
96
|
+
typeof context.spawn !== "function" ||
|
|
97
|
+
typeof context.admissionOpen !== "function")
|
|
98
|
+
throw new Error("invalid Akuma call execution context");
|
|
99
|
+
if (!carriesLaunchWorld(context.upstream))
|
|
100
|
+
throw new Error("Akuma call requires a minted launch World");
|
|
101
|
+
return {
|
|
102
|
+
id: context.id,
|
|
103
|
+
requester: context.requester,
|
|
104
|
+
signal: context.signal,
|
|
105
|
+
world: context.upstream.launchWorld(),
|
|
106
|
+
paths: context.paths,
|
|
107
|
+
parent: context.parent,
|
|
108
|
+
spawn: context.spawn,
|
|
109
|
+
admissionOpen: context.admissionOpen,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function object(value) {
|
|
113
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
114
|
+
? value
|
|
115
|
+
: null;
|
|
116
|
+
}
|
|
117
|
+
export function decodeAkumaCallRequest(value) {
|
|
118
|
+
const parsed = akumaCallPayloadSchema.safeParse(value);
|
|
119
|
+
return parsed.success ? { ...parsed.data, action: "akuma.call" } : null;
|
|
120
|
+
}
|
|
121
|
+
function callPayload(request) {
|
|
122
|
+
const { action: _action, ...payload } = request;
|
|
123
|
+
return payload;
|
|
124
|
+
}
|
|
125
|
+
async function executeAkumaCall(request, context) {
|
|
126
|
+
const world = context.world;
|
|
127
|
+
const requestWorld = await World.prove(request.world);
|
|
128
|
+
if (requestWorld !== world) {
|
|
129
|
+
await refuseRequest(context.paths, context.id, `request world ${requestWorld} does not match ${world}`);
|
|
130
|
+
throw new Error(`request world ${requestWorld} does not match ${world}`);
|
|
131
|
+
}
|
|
132
|
+
const recipe = {
|
|
133
|
+
...(request.recipe.description === undefined ? {} : { description: request.recipe.description }),
|
|
134
|
+
allowed: clipAllowedActions(request.recipe.allowed, context.parent.allowed),
|
|
135
|
+
provider: request.recipe.provider,
|
|
136
|
+
options: request.recipe.options,
|
|
137
|
+
...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
|
|
138
|
+
};
|
|
139
|
+
const published = await publishAkuma({
|
|
140
|
+
worldPath: world,
|
|
141
|
+
archetype: request.archetype,
|
|
142
|
+
signal: context.signal,
|
|
143
|
+
launch: async (allocated) => {
|
|
144
|
+
if (!context.admissionOpen())
|
|
145
|
+
throw new Error("body closed request admission");
|
|
146
|
+
await reserveRequest(context.paths, context.id, allocated.id);
|
|
147
|
+
return await context.spawn({
|
|
148
|
+
paths: allocated.paths,
|
|
149
|
+
seed: {
|
|
150
|
+
id: allocated.id,
|
|
151
|
+
archetype: allocated.archetype,
|
|
152
|
+
...recipe,
|
|
153
|
+
cwd: request.cwd ?? context.parent.cwd,
|
|
154
|
+
origin: { kind: "request", parent: context.parent.id, requestId: context.id },
|
|
155
|
+
},
|
|
156
|
+
initialBody: request.body,
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
return { result: published.id, child: published.id };
|
|
161
|
+
}
|
|
162
|
+
export function akumaCallRequestCommand() {
|
|
163
|
+
return {
|
|
164
|
+
action: "akuma.call",
|
|
165
|
+
encodeRequest: callPayload,
|
|
166
|
+
decodeRequest: decodeAkumaCallRequest,
|
|
167
|
+
encodeResult: (result) => result,
|
|
168
|
+
decodeResult: (result) => {
|
|
169
|
+
const child = akuIdSchema.safeParse(result);
|
|
170
|
+
if (!child.success)
|
|
171
|
+
throw new Error("Akuma call returned an invalid child");
|
|
172
|
+
return child.data;
|
|
173
|
+
},
|
|
174
|
+
encodeService: (service) => service,
|
|
175
|
+
decodeService: (_service) => {
|
|
176
|
+
throw new Error("Akuma call has no service evidence");
|
|
177
|
+
},
|
|
178
|
+
projectService: (_service) => {
|
|
179
|
+
throw new Error("Akuma call has no service evidence");
|
|
180
|
+
},
|
|
181
|
+
projectChild: (child) => {
|
|
182
|
+
const id = akuIdSchema.safeParse(child);
|
|
183
|
+
if (!id.success)
|
|
184
|
+
throw new Error("Akuma call stored an invalid child reference");
|
|
185
|
+
return id.data;
|
|
186
|
+
},
|
|
187
|
+
decodeReference: (reference) => {
|
|
188
|
+
const child = akuIdSchema.safeParse(reference);
|
|
189
|
+
if (!child.success)
|
|
190
|
+
throw new Error("Akuma call stored an invalid child reference");
|
|
191
|
+
return child.data;
|
|
192
|
+
},
|
|
193
|
+
isPermitted: (allowed) => allowed.includes("akuma.call"),
|
|
194
|
+
decodeExecutionContext: decodeCallExecutionContext,
|
|
195
|
+
execute: executeAkumaCall,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function akumaCallRequestCommands() {
|
|
199
|
+
return { "akuma.call": eraseRequestCommand(akumaCallRequestCommand()) };
|
|
200
|
+
}
|
|
201
|
+
export async function requestForwardedAkumaCall(input) {
|
|
202
|
+
const { directory, id, signal, ...request } = input;
|
|
203
|
+
const response = await requestBodyCommand({
|
|
204
|
+
directory,
|
|
205
|
+
id,
|
|
206
|
+
command: akumaCallRequestCommand(),
|
|
207
|
+
value: { ...request, action: "akuma.call" },
|
|
208
|
+
...(signal === undefined ? {} : { signal }),
|
|
209
|
+
});
|
|
210
|
+
return response.kind === "returned" ? response.result : response.reference;
|
|
211
|
+
}
|
|
@@ -2,7 +2,6 @@ import type { AkuId } from "../identity.js";
|
|
|
2
2
|
import type { ResumeCoordinate } from "../coordinate.js";
|
|
3
3
|
import type { ProviderExecution, ProviderOptions, ReadonlyRestraint } from "../provider-recipe.js";
|
|
4
4
|
import type { AllowedActions } from "../allowed.js";
|
|
5
|
-
import type { TaskMutationAction, TaskMutationRequest } from "../../task/mutation.js";
|
|
6
5
|
export type { ResumeCoordinate } from "../coordinate.js";
|
|
7
6
|
export type AkumaOrigin = Readonly<{
|
|
8
7
|
kind: "direct";
|
|
@@ -123,75 +122,10 @@ export type TellReceiptInput = Readonly<{
|
|
|
123
122
|
turnSequence: number;
|
|
124
123
|
fence: string;
|
|
125
124
|
}>);
|
|
126
|
-
export type TaskRequestInput = Readonly<{
|
|
127
|
-
id: string;
|
|
128
|
-
action: TaskMutationAction;
|
|
129
|
-
world: string;
|
|
130
|
-
request: TaskMutationRequest;
|
|
131
|
-
}>;
|
|
132
125
|
export type RequestInput = Readonly<{
|
|
133
126
|
id: string;
|
|
134
|
-
action:
|
|
135
|
-
|
|
136
|
-
body: string;
|
|
137
|
-
cwd?: string;
|
|
138
|
-
world: string;
|
|
139
|
-
recipe: RequestRecipe;
|
|
140
|
-
}> | Readonly<{
|
|
141
|
-
id: string;
|
|
142
|
-
action: "akuma.wait";
|
|
143
|
-
targets: readonly AkuId[];
|
|
144
|
-
completion: "any" | "all";
|
|
145
|
-
timeoutMs?: number;
|
|
146
|
-
}> | Readonly<{
|
|
147
|
-
id: string;
|
|
148
|
-
action: "akuma.tell";
|
|
149
|
-
target: AkuId;
|
|
150
|
-
body: string;
|
|
151
|
-
}> | Readonly<{
|
|
152
|
-
id: string;
|
|
153
|
-
action: "akuma.kill";
|
|
154
|
-
targets: readonly AkuId[];
|
|
155
|
-
}> | Readonly<{
|
|
156
|
-
id: string;
|
|
157
|
-
action: "contract.deliver";
|
|
158
|
-
repoRoot: string;
|
|
159
|
-
contractId: string;
|
|
160
|
-
message?: string;
|
|
161
|
-
includeDirty: boolean;
|
|
162
|
-
materializeConflict: boolean;
|
|
163
|
-
}> | Readonly<{
|
|
164
|
-
id: string;
|
|
165
|
-
action: "contract.review";
|
|
166
|
-
repoRoot: string;
|
|
167
|
-
contractId: string;
|
|
168
|
-
verdict: "satisfied" | "unsatisfied";
|
|
169
|
-
summary?: string;
|
|
170
|
-
}> | TaskRequestInput;
|
|
171
|
-
export type UpstreamRequestService = Readonly<{
|
|
172
|
-
action: "akuma.wait";
|
|
173
|
-
}> | Readonly<{
|
|
174
|
-
action: "akuma.tell";
|
|
175
|
-
target: AkuId;
|
|
176
|
-
tellId: string;
|
|
177
|
-
}> | Readonly<{
|
|
178
|
-
action: "akuma.kill";
|
|
179
|
-
results: readonly Readonly<{
|
|
180
|
-
id: AkuId;
|
|
181
|
-
evidence: KillEvidence;
|
|
182
|
-
}>[];
|
|
183
|
-
}> | Readonly<{
|
|
184
|
-
action: "contract.deliver";
|
|
185
|
-
repoRoot: string;
|
|
186
|
-
contractId: string;
|
|
187
|
-
deliveryFactId: string;
|
|
188
|
-
}> | Readonly<{
|
|
189
|
-
action: "contract.review";
|
|
190
|
-
repoRoot: string;
|
|
191
|
-
contractId: string;
|
|
192
|
-
reviewFactId: string;
|
|
193
|
-
}> | Readonly<{
|
|
194
|
-
action: TaskMutationRequest["action"];
|
|
127
|
+
action: string;
|
|
128
|
+
payloadJson: string;
|
|
195
129
|
}>;
|
|
196
130
|
type AdmittedRequest = RequestInput & Readonly<{
|
|
197
131
|
requester: AkuId;
|
|
@@ -199,21 +133,15 @@ type AdmittedRequest = RequestInput & Readonly<{
|
|
|
199
133
|
}>;
|
|
200
134
|
export type RequestFact = (AdmittedRequest & Readonly<{
|
|
201
135
|
state: "admitted";
|
|
202
|
-
}>) | (
|
|
203
|
-
action: "akuma.call";
|
|
204
|
-
}> & Readonly<{
|
|
136
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
205
137
|
state: "reserved";
|
|
206
138
|
child: AkuId;
|
|
207
|
-
}>) | (
|
|
208
|
-
action: "akuma.call";
|
|
209
|
-
}> & Readonly<{
|
|
139
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
210
140
|
state: "served";
|
|
211
141
|
child: AkuId;
|
|
212
|
-
}>) | (
|
|
213
|
-
action: "akuma.call";
|
|
214
|
-
}> & Readonly<{
|
|
142
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
215
143
|
state: "served";
|
|
216
|
-
|
|
144
|
+
serviceJson: string;
|
|
217
145
|
}>) | (AdmittedRequest & Readonly<{
|
|
218
146
|
state: "refused";
|
|
219
147
|
diagnostic: string;
|
|
@@ -3,10 +3,10 @@ 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, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash,
|
|
7
|
-
export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
6
|
+
export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
|
|
7
|
+
export { admitRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
8
8
|
export { life, lifeAt } from "./facts.js";
|
|
9
|
-
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe,
|
|
9
|
+
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
|
|
10
10
|
export type { ResumeCoordinate } from "../coordinate.js";
|
|
11
11
|
export declare function readSoul(paths: AkumaPaths): Promise<Soul | null>;
|
|
12
12
|
export declare function heartExists(paths: AkumaPaths): Promise<boolean>;
|
|
@@ -4,8 +4,8 @@ import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingT
|
|
|
4
4
|
import { activityFactSlice, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
|
|
5
5
|
import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart, withReadOnlyHeart, } from "./storage.js";
|
|
6
6
|
import { soulFact } from "./soul.js";
|
|
7
|
-
export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash,
|
|
8
|
-
export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
7
|
+
export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
|
|
8
|
+
export { admitRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
9
9
|
export { life, lifeAt } from "./facts.js";
|
|
10
10
|
const ACTIVITY_LIMIT = 5_000;
|
|
11
11
|
export async function readSoul(paths) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { RequestFact, RequestInput
|
|
1
|
+
import { type AkuId, type AkumaPaths } from "../identity.js";
|
|
2
|
+
import type { RequestFact, RequestInput } from "./facts.js";
|
|
3
|
+
export declare function isRequestInputConflict(error: unknown): error is Error;
|
|
3
4
|
export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
|
|
4
5
|
admittedAt: string;
|
|
6
|
+
permitted: boolean;
|
|
5
7
|
}>): Promise<RequestFact>;
|
|
6
8
|
export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
7
|
-
export declare function serveRequest(paths: AkumaPaths, id: string, child:
|
|
8
|
-
export declare function serveUpstreamRequest(paths: AkumaPaths, id: string,
|
|
9
|
+
export declare function serveRequest(paths: AkumaPaths, id: string, child: string): Promise<RequestFact>;
|
|
10
|
+
export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, serviceJson: string): Promise<RequestFact>;
|
|
9
11
|
export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
|
|
10
12
|
export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
|
|
11
13
|
export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
|
|
@@ -1,39 +1,37 @@
|
|
|
1
|
+
import { parseAkuId } from "../identity.js";
|
|
1
2
|
import { insertRequestFact, nonterminalRequestFacts, requestFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./request-rows.js";
|
|
2
3
|
import { transaction, withHeart } from "./storage.js";
|
|
3
4
|
import { soulFact } from "./soul.js";
|
|
4
|
-
import { clipAllowedActions } from "../allowed.js";
|
|
5
5
|
import { requestPayloadJson } from "./request-rows.js";
|
|
6
|
+
// This stays out of the public Heart index: only transport service projects it as a refusal.
|
|
7
|
+
class RequestInputConflictError extends Error {
|
|
8
|
+
constructor(id) {
|
|
9
|
+
super(`Akuma request ${id} reused different input`);
|
|
10
|
+
this.name = "RequestInputConflictError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function isRequestInputConflict(error) {
|
|
14
|
+
return error instanceof RequestInputConflictError;
|
|
15
|
+
}
|
|
6
16
|
export async function admitRequest(paths, input) {
|
|
7
17
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
8
18
|
const soul = soulFact(heart);
|
|
9
19
|
if (soul === null)
|
|
10
20
|
throw new Error("Akuma request admission requires a born Soul");
|
|
11
|
-
const
|
|
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;
|
|
21
|
+
const { permitted, ...request } = input;
|
|
23
22
|
insertRequestFact(heart, {
|
|
24
|
-
...
|
|
23
|
+
...request,
|
|
25
24
|
requester: soul.id,
|
|
26
|
-
|
|
27
|
-
...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
|
|
25
|
+
...(permitted ? {} : { refusal: `not-allowed: ${input.action}` }),
|
|
28
26
|
});
|
|
29
27
|
const fact = requestFact(heart, input.id);
|
|
30
28
|
if (fact === null)
|
|
31
29
|
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
32
30
|
if (fact.requester !== soul.id ||
|
|
33
|
-
fact.id !==
|
|
34
|
-
fact.action !==
|
|
35
|
-
requestPayloadJson(fact) !== requestPayloadJson(
|
|
36
|
-
throw new
|
|
31
|
+
fact.id !== input.id ||
|
|
32
|
+
fact.action !== input.action ||
|
|
33
|
+
requestPayloadJson(fact) !== requestPayloadJson(input)) {
|
|
34
|
+
throw new RequestInputConflictError(input.id);
|
|
37
35
|
}
|
|
38
36
|
return fact;
|
|
39
37
|
}));
|
|
@@ -43,43 +41,41 @@ export async function reserveRequest(paths, id, child) {
|
|
|
43
41
|
const before = requestFact(heart, id);
|
|
44
42
|
if (before === null)
|
|
45
43
|
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
44
|
if (before.state === "admitted")
|
|
49
45
|
updateRequestReserved(heart, id, child);
|
|
50
|
-
else if ((before.state !== "reserved" && before.state !== "served") ||
|
|
46
|
+
else if ((before.state !== "reserved" && before.state !== "served") ||
|
|
47
|
+
!("child" in before) ||
|
|
48
|
+
before.child !== child) {
|
|
51
49
|
throw new Error(`Akuma request ${id} cannot reserve ${child}`);
|
|
52
50
|
}
|
|
53
51
|
return requestFact(heart, id);
|
|
54
52
|
}));
|
|
55
53
|
}
|
|
56
54
|
export async function serveRequest(paths, id, child) {
|
|
55
|
+
const parsed = parseAkuId(child);
|
|
56
|
+
if (parsed.id !== child)
|
|
57
|
+
throw new Error(`Akuma request ${id} has a noncanonical child`);
|
|
57
58
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
58
59
|
const before = requestFact(heart, id);
|
|
59
60
|
if (before === null)
|
|
60
61
|
throw new Error(`unknown Akuma request ${id}`);
|
|
61
|
-
if (before.
|
|
62
|
-
|
|
63
|
-
if (before.state
|
|
64
|
-
updateRequestServed(heart, id, child);
|
|
65
|
-
else if (before.state !== "served" || before.child !== child) {
|
|
62
|
+
if (before.state === "reserved" && before.child === parsed.id)
|
|
63
|
+
updateRequestServed(heart, id, parsed.id);
|
|
64
|
+
else if (before.state !== "served" || !("child" in before) || before.child !== parsed.id) {
|
|
66
65
|
throw new Error(`Akuma request ${id} cannot serve ${child}`);
|
|
67
66
|
}
|
|
68
67
|
return requestFact(heart, id);
|
|
69
68
|
}));
|
|
70
69
|
}
|
|
71
|
-
export async function serveUpstreamRequest(paths, id,
|
|
70
|
+
export async function serveUpstreamRequest(paths, id, serviceJson) {
|
|
72
71
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
73
72
|
const before = requestFact(heart, id);
|
|
74
73
|
if (before === null)
|
|
75
74
|
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
75
|
if (before.state === "admitted")
|
|
80
|
-
updateUpstreamRequestServed(heart, id,
|
|
81
|
-
else if (before.state !== "served") {
|
|
82
|
-
throw new Error(`Akuma request ${id} cannot be served
|
|
76
|
+
updateUpstreamRequestServed(heart, id, serviceJson);
|
|
77
|
+
else if (before.state !== "served" || !("serviceJson" in before) || before.serviceJson !== serviceJson) {
|
|
78
|
+
throw new Error(`Akuma request ${id} cannot be served with this service reference`);
|
|
83
79
|
}
|
|
84
80
|
return requestFact(heart, id);
|
|
85
81
|
}));
|
|
@@ -1,45 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
function json(value) {
|
|
3
|
-
return JSON.stringify(value);
|
|
4
|
-
}
|
|
5
|
-
function parsed(value) {
|
|
1
|
+
function assertJsonText(value) {
|
|
6
2
|
if (typeof value !== "string")
|
|
7
3
|
throw new Error("Akuma authority contains non-text JSON");
|
|
8
|
-
|
|
4
|
+
JSON.parse(value);
|
|
5
|
+
return value;
|
|
9
6
|
}
|
|
10
7
|
export function requestPayloadJson(input) {
|
|
11
|
-
|
|
12
|
-
return json(payload);
|
|
8
|
+
return assertJsonText(input.payloadJson);
|
|
13
9
|
}
|
|
14
10
|
function decodeRequestRow(row) {
|
|
15
|
-
const knownAction = ["akuma.call", "akuma.wait", "akuma.tell", "akuma.kill", "contract.deliver", "contract.review"].includes(row.action) || isTaskMutationAction(row.action);
|
|
16
|
-
if (!knownAction)
|
|
17
|
-
throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
|
|
18
|
-
const payload = parsed(row.payload_json);
|
|
19
11
|
const input = {
|
|
20
12
|
id: row.id,
|
|
21
13
|
requester: row.requester,
|
|
22
14
|
action: row.action,
|
|
23
|
-
|
|
15
|
+
payloadJson: assertJsonText(row.payload_json),
|
|
24
16
|
admittedAt: row.admitted_at,
|
|
25
17
|
};
|
|
26
18
|
if (row.state === "reserved") {
|
|
27
|
-
if (
|
|
19
|
+
if (row.child === null || row.service_json !== null) {
|
|
28
20
|
throw new Error("Akuma authority contains an invalid reserved request");
|
|
29
21
|
}
|
|
30
22
|
return { ...input, state: row.state, child: row.child };
|
|
31
23
|
}
|
|
32
24
|
if (row.state === "served") {
|
|
33
|
-
if (
|
|
34
|
-
if (row.child === null)
|
|
35
|
-
throw new Error("Akuma authority contains a served call without a child");
|
|
25
|
+
if (row.child !== null && row.service_json === null) {
|
|
36
26
|
return { ...input, state: row.state, child: row.child };
|
|
37
27
|
}
|
|
38
|
-
if (row.
|
|
39
|
-
|
|
28
|
+
if (row.child === null && row.service_json !== null) {
|
|
29
|
+
return { ...input, state: row.state, serviceJson: assertJsonText(row.service_json) };
|
|
40
30
|
}
|
|
41
|
-
|
|
42
|
-
return { ...input, state: row.state, service };
|
|
31
|
+
throw new Error("Akuma authority contains an invalid served request");
|
|
43
32
|
}
|
|
44
33
|
if (row.state === "refused")
|
|
45
34
|
return { ...input, state: row.state, diagnostic: row.diagnostic };
|
|
@@ -82,8 +71,8 @@ export function updateRequestServed(database, id, child) {
|
|
|
82
71
|
export function updateUpstreamRequestServed(database, id, service) {
|
|
83
72
|
database
|
|
84
73
|
.prepare(`UPDATE requests SET state = 'served', service_json = ?
|
|
85
|
-
WHERE id = ? AND state = 'admitted'
|
|
86
|
-
.run(
|
|
74
|
+
WHERE id = ? AND state = 'admitted'`)
|
|
75
|
+
.run(assertJsonText(service), id);
|
|
87
76
|
}
|
|
88
77
|
export function updateRequestRefused(database, id, diagnostic) {
|
|
89
78
|
database
|