@astrosheep/keiyaku 4.5.7 → 4.5.9
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 +12 -11
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +33 -12
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +35 -96
- 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 +392 -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/archetype.js +1 -1
- package/build/src/akuma/body.d.ts +8 -5
- package/build/src/akuma/body.js +12 -5
- 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 +16 -78
- package/build/src/akuma/heart/facts.js +11 -0
- package/build/src/akuma/heart/index.d.ts +3 -2
- package/build/src/akuma/heart/index.js +4 -1
- package/build/src/akuma/heart/request-authority.d.ts +5 -4
- package/build/src/akuma/heart/request-authority.js +20 -34
- 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/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 +907 -137
- package/build/src/akuma/projection.js +193 -9
- package/build/src/akuma/provider.d.ts +30 -3
- package/build/src/akuma/provider.js +111 -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/events.d.ts +2 -2
- package/build/src/akuma/providers/claude/events.js +16 -3
- 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.d.ts +2 -2
- package/build/src/akuma/providers/codex-app-server/events.js +25 -4
- 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 +72 -58
- package/build/src/akuma/request-lifecycle.d.ts +4 -10
- package/build/src/akuma/request-lifecycle.js +7 -5
- 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 +6 -66
- package/build/src/akuma/request-serve.js +83 -210
- 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 +39 -11
- package/build/src/akuma-body.d.ts +7 -2
- package/build/src/akuma-body.js +68 -46
- package/build/src/body/amend.js +18 -19
- 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 +8 -8
- package/build/src/cli/commands/contract-invoke.d.ts +3 -2
- package/build/src/cli/commands/contract-invoke.js +42 -30
- package/build/src/cli/commands/contract.d.ts +16 -20
- package/build/src/cli/commands/contract.js +58 -25
- package/build/src/cli/commands/install.d.ts +2 -1
- package/build/src/cli/commands/install.js +3 -2
- package/build/src/cli/commands/task-invoke.d.ts +3 -0
- package/build/src/cli/commands/task-invoke.js +23 -140
- package/build/src/cli/commands/task.js +54 -20
- 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 +56 -27
- package/build/src/cli/render/akuma-activity.d.ts +3 -0
- package/build/src/cli/render/akuma-activity.js +45 -29
- package/build/src/cli/render/akuma-tool.d.ts +5 -1
- package/build/src/cli/render/akuma.js +4 -8
- 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 +35 -62
- package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
- package/build/src/cli/render/kanshi-akuma.js +132 -0
- package/build/src/cli/render/kanshi.js +131 -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 +22 -5
- package/build/src/cli/render/terminal.d.ts +32 -1
- package/build/src/cli/render/terminal.js +77 -1
- package/build/src/cli/result.d.ts +3 -3
- package/build/src/cli/runtime.js +4 -2
- 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 +39 -21
- 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 +26 -11
- package/build/src/git/read-observation.js +29 -20
- package/build/src/git/reconcile.d.ts +13 -1
- package/build/src/git/reconcile.js +70 -11
- package/build/src/git/ref-migration.d.ts +22 -0
- package/build/src/git/ref-migration.js +55 -0
- package/build/src/git/repository.d.ts +10 -6
- package/build/src/git/repository.js +16 -8
- package/build/src/git/target-placement.d.ts +1 -0
- package/build/src/git/target-placement.js +5 -0
- 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 +14 -6
- package/build/src/library/audit.js +13 -23
- package/build/src/library/catalog.d.ts +3 -1
- package/build/src/library/catalog.js +20 -6
- package/build/src/library/composition.d.ts +385 -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 +102 -0
- package/build/src/library/contract-forwarding-reconciliation-result.js +92 -0
- package/build/src/library/contract-forwarding-result.d.ts +4464 -0
- package/build/src/library/contract-forwarding-result.js +521 -0
- package/build/src/library/contract-forwarding.d.ts +71 -0
- package/build/src/library/contract-forwarding.js +114 -0
- package/build/src/library/contract-handle.d.ts +38 -0
- package/build/src/library/contract-handle.js +314 -0
- package/build/src/library/contract-operations.d.ts +214 -69
- package/build/src/library/contract-operations.js +189 -118
- package/build/src/library/contract-types.d.ts +7 -3
- package/build/src/library/contract.d.ts +21 -34
- package/build/src/library/contract.js +29 -287
- package/build/src/library/fleet-result.d.ts +1580 -0
- package/build/src/library/fleet-result.js +98 -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 +3 -1
- package/build/src/library/input.js +11 -1
- package/build/src/library/keiyaku.d.ts +388 -20
- package/build/src/library/keiyaku.js +13 -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 +8 -1
- 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 +9 -0
- package/build/src/runtime/proc/run.js +51 -4
- 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.js +33 -7
- package/build/src/task/compose-parser.d.ts +1 -1
- package/build/src/task/compose-parser.js +1 -1
- package/build/src/task/compose.js +2 -0
- package/build/src/task/index.d.ts +12 -4
- 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 Akuma name" });
|
|
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";
|
|
@@ -101,6 +100,16 @@ export type TellFact = Readonly<{
|
|
|
101
100
|
state: "pending" | "told";
|
|
102
101
|
deliveries: readonly TellDelivery[];
|
|
103
102
|
}>;
|
|
103
|
+
export type TellRow = Readonly<{
|
|
104
|
+
kind: "tell";
|
|
105
|
+
sequence: number;
|
|
106
|
+
at: string;
|
|
107
|
+
tellId: string;
|
|
108
|
+
text: string;
|
|
109
|
+
state: "pending" | "told";
|
|
110
|
+
deliveries: readonly TellDelivery[];
|
|
111
|
+
}>;
|
|
112
|
+
export declare function projectTell(fact: TellFact): TellRow;
|
|
104
113
|
export type TellDeliveryInput = Readonly<{
|
|
105
114
|
tellId: string;
|
|
106
115
|
turnSequence: number;
|
|
@@ -123,75 +132,10 @@ export type TellReceiptInput = Readonly<{
|
|
|
123
132
|
turnSequence: number;
|
|
124
133
|
fence: string;
|
|
125
134
|
}>);
|
|
126
|
-
export type TaskRequestInput = Readonly<{
|
|
127
|
-
id: string;
|
|
128
|
-
action: TaskMutationAction;
|
|
129
|
-
world: string;
|
|
130
|
-
request: TaskMutationRequest;
|
|
131
|
-
}>;
|
|
132
135
|
export type RequestInput = Readonly<{
|
|
133
136
|
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"];
|
|
137
|
+
action: string;
|
|
138
|
+
payloadJson: string;
|
|
195
139
|
}>;
|
|
196
140
|
type AdmittedRequest = RequestInput & Readonly<{
|
|
197
141
|
requester: AkuId;
|
|
@@ -199,21 +143,15 @@ type AdmittedRequest = RequestInput & Readonly<{
|
|
|
199
143
|
}>;
|
|
200
144
|
export type RequestFact = (AdmittedRequest & Readonly<{
|
|
201
145
|
state: "admitted";
|
|
202
|
-
}>) | (
|
|
203
|
-
action: "akuma.call";
|
|
204
|
-
}> & Readonly<{
|
|
146
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
205
147
|
state: "reserved";
|
|
206
148
|
child: AkuId;
|
|
207
|
-
}>) | (
|
|
208
|
-
action: "akuma.call";
|
|
209
|
-
}> & Readonly<{
|
|
149
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
210
150
|
state: "served";
|
|
211
151
|
child: AkuId;
|
|
212
|
-
}>) | (
|
|
213
|
-
action: "akuma.call";
|
|
214
|
-
}> & Readonly<{
|
|
152
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
215
153
|
state: "served";
|
|
216
|
-
|
|
154
|
+
serviceJson: string;
|
|
217
155
|
}>) | (AdmittedRequest & Readonly<{
|
|
218
156
|
state: "refused";
|
|
219
157
|
diagnostic: string;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export function projectTell(fact) {
|
|
2
|
+
return {
|
|
3
|
+
kind: "tell",
|
|
4
|
+
sequence: fact.sequence,
|
|
5
|
+
at: fact.recordedAt,
|
|
6
|
+
tellId: fact.id,
|
|
7
|
+
text: fact.body,
|
|
8
|
+
state: fact.state,
|
|
9
|
+
deliveries: fact.deliveries,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
1
12
|
export function life(input) {
|
|
2
13
|
if (input.body?.hung !== undefined)
|
|
3
14
|
return "hung";
|
|
@@ -5,8 +5,8 @@ import type { ActivityFact } from "./rows.js";
|
|
|
5
5
|
import { type ActivityFactSlice } from "./timeline.js";
|
|
6
6
|
export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
|
|
7
7
|
export { admitRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
8
|
-
export { life, lifeAt } from "./facts.js";
|
|
9
|
-
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe,
|
|
8
|
+
export { life, lifeAt, projectTell } from "./facts.js";
|
|
9
|
+
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, SealFact, SessionFact, Soul, StopFact, TellFact, TellRow, 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>;
|
|
@@ -27,6 +27,7 @@ export declare function recordTell(paths: AkumaPaths, tell: Omit<TellFact, "sequ
|
|
|
27
27
|
kind: "recorded";
|
|
28
28
|
tell: TellFact;
|
|
29
29
|
}>>;
|
|
30
|
+
export declare function readTell(paths: AkumaPaths, id: string): Promise<TellFact | null>;
|
|
30
31
|
export declare function recordTellDeliveries(paths: AkumaPaths, inputs: readonly TellDeliveryInput[]): Promise<void>;
|
|
31
32
|
export declare function recordTellReceipt(paths: AkumaPaths, input: TellReceiptInput): Promise<void>;
|
|
32
33
|
export declare function readKill(paths: AkumaPaths, bodySequence: number): Promise<KillFact | null>;
|
|
@@ -6,7 +6,7 @@ import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHea
|
|
|
6
6
|
import { soulFact } from "./soul.js";
|
|
7
7
|
export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
|
|
8
8
|
export { admitRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
|
|
9
|
-
export { life, lifeAt } from "./facts.js";
|
|
9
|
+
export { life, lifeAt, projectTell } from "./facts.js";
|
|
10
10
|
const ACTIVITY_LIMIT = 5_000;
|
|
11
11
|
export async function readSoul(paths) {
|
|
12
12
|
try {
|
|
@@ -67,6 +67,9 @@ export async function recordTell(paths, tell) {
|
|
|
67
67
|
return { kind: "recorded", tell: { sequence, ...tell, state: "pending", deliveries: [] } };
|
|
68
68
|
}));
|
|
69
69
|
}
|
|
70
|
+
export async function readTell(paths, id) {
|
|
71
|
+
return await withHeart(paths, (heart) => readTransaction(heart, () => tellFact(heart, id)));
|
|
72
|
+
}
|
|
70
73
|
export async function recordTellDeliveries(paths, inputs) {
|
|
71
74
|
await withHeart(paths, (heart) => transaction(heart, () => {
|
|
72
75
|
for (const input of inputs) {
|
|
@@ -1,12 +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
3
|
export declare function isRequestInputConflict(error: unknown): error is Error;
|
|
4
4
|
export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
|
|
5
5
|
admittedAt: string;
|
|
6
|
+
permitted: boolean;
|
|
6
7
|
}>): Promise<RequestFact>;
|
|
7
8
|
export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
8
|
-
export declare function serveRequest(paths: AkumaPaths, id: string, child:
|
|
9
|
-
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>;
|
|
10
11
|
export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
|
|
11
12
|
export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
|
|
12
13
|
export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
|
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
// This stays out of the public Heart index: only transport service projects it as a refusal.
|
|
7
7
|
class RequestInputConflictError extends Error {
|
|
@@ -18,31 +18,19 @@ export async function admitRequest(paths, input) {
|
|
|
18
18
|
const soul = soulFact(heart);
|
|
19
19
|
if (soul === null)
|
|
20
20
|
throw new Error("Akuma request admission requires a born Soul");
|
|
21
|
-
const
|
|
22
|
-
const parentAllowed = soul.allowed;
|
|
23
|
-
const permitted = action === "akuma.wait" || parentAllowed.includes(action);
|
|
24
|
-
const normalized = input.action === "akuma.call"
|
|
25
|
-
? {
|
|
26
|
-
...input,
|
|
27
|
-
recipe: {
|
|
28
|
-
...input.recipe,
|
|
29
|
-
allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
: input;
|
|
21
|
+
const { permitted, ...request } = input;
|
|
33
22
|
insertRequestFact(heart, {
|
|
34
|
-
...
|
|
23
|
+
...request,
|
|
35
24
|
requester: soul.id,
|
|
36
|
-
|
|
37
|
-
...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
|
|
25
|
+
...(permitted ? {} : { refusal: `not-allowed: ${input.action}` }),
|
|
38
26
|
});
|
|
39
27
|
const fact = requestFact(heart, input.id);
|
|
40
28
|
if (fact === null)
|
|
41
29
|
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
42
30
|
if (fact.requester !== soul.id ||
|
|
43
|
-
fact.id !==
|
|
44
|
-
fact.action !==
|
|
45
|
-
requestPayloadJson(fact) !== requestPayloadJson(
|
|
31
|
+
fact.id !== input.id ||
|
|
32
|
+
fact.action !== input.action ||
|
|
33
|
+
requestPayloadJson(fact) !== requestPayloadJson(input)) {
|
|
46
34
|
throw new RequestInputConflictError(input.id);
|
|
47
35
|
}
|
|
48
36
|
return fact;
|
|
@@ -53,43 +41,41 @@ export async function reserveRequest(paths, id, child) {
|
|
|
53
41
|
const before = requestFact(heart, id);
|
|
54
42
|
if (before === null)
|
|
55
43
|
throw new Error(`unknown Akuma request ${id}`);
|
|
56
|
-
if (before.action !== "akuma.call")
|
|
57
|
-
throw new Error(`Akuma request ${id} cannot reserve a child`);
|
|
58
44
|
if (before.state === "admitted")
|
|
59
45
|
updateRequestReserved(heart, id, child);
|
|
60
|
-
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) {
|
|
61
49
|
throw new Error(`Akuma request ${id} cannot reserve ${child}`);
|
|
62
50
|
}
|
|
63
51
|
return requestFact(heart, id);
|
|
64
52
|
}));
|
|
65
53
|
}
|
|
66
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`);
|
|
67
58
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
68
59
|
const before = requestFact(heart, id);
|
|
69
60
|
if (before === null)
|
|
70
61
|
throw new Error(`unknown Akuma request ${id}`);
|
|
71
|
-
if (before.
|
|
72
|
-
|
|
73
|
-
if (before.state
|
|
74
|
-
updateRequestServed(heart, id, child);
|
|
75
|
-
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) {
|
|
76
65
|
throw new Error(`Akuma request ${id} cannot serve ${child}`);
|
|
77
66
|
}
|
|
78
67
|
return requestFact(heart, id);
|
|
79
68
|
}));
|
|
80
69
|
}
|
|
81
|
-
export async function serveUpstreamRequest(paths, id,
|
|
70
|
+
export async function serveUpstreamRequest(paths, id, serviceJson) {
|
|
82
71
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
83
72
|
const before = requestFact(heart, id);
|
|
84
73
|
if (before === null)
|
|
85
74
|
throw new Error(`unknown Akuma request ${id}`);
|
|
86
|
-
if (before.action === "akuma.call" || service.action !== before.action) {
|
|
87
|
-
throw new Error(`Akuma request ${id} has a mismatched service reference`);
|
|
88
|
-
}
|
|
89
75
|
if (before.state === "admitted")
|
|
90
|
-
updateUpstreamRequestServed(heart, id,
|
|
91
|
-
else if (before.state !== "served") {
|
|
92
|
-
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`);
|
|
93
79
|
}
|
|
94
80
|
return requestFact(heart, id);
|
|
95
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
|