@astrosheep/keiyaku 4.5.17 → 4.5.18
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/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +5 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +4 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +13 -1
- package/build/src/akuma/akuma-handle.d.ts +18 -7
- package/build/src/akuma/akuma-handle.js +89 -112
- package/build/src/akuma/akuma-instance.d.ts +19 -4
- package/build/src/akuma/akuma-instance.js +102 -38
- package/build/src/akuma/akuma-observe.js +40 -22
- package/build/src/akuma/akuma-product.d.ts +12 -8
- package/build/src/akuma/akuma-product.js +66 -54
- package/build/src/akuma/akuma.d.ts +9 -21
- package/build/src/akuma/akuma.js +11 -67
- package/build/src/akuma/body.js +127 -96
- package/build/src/akuma/call-request.d.ts +2 -2
- package/build/src/akuma/call-request.js +2 -2
- package/build/src/akuma/control.d.ts +8 -0
- package/build/src/akuma/control.js +23 -0
- package/build/src/akuma/dispatch-association.d.ts +13 -0
- package/build/src/akuma/dispatch-association.js +20 -0
- package/build/src/akuma/fleet-execution.js +10 -15
- package/build/src/akuma/fleet-observation.js +1 -1
- package/build/src/akuma/fleet-request.js +9 -2
- package/build/src/akuma/heart/facts.d.ts +3 -0
- package/build/src/akuma/heart/facts.js +6 -0
- package/build/src/akuma/heart/index.d.ts +1 -0
- package/build/src/akuma/heart/index.js +5 -8
- package/build/src/akuma/heart/rows.d.ts +1 -0
- package/build/src/akuma/heart/rows.js +7 -0
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +5 -3
- package/build/src/akuma/heart/tells.d.ts +2 -2
- package/build/src/akuma/heart/tells.js +16 -11
- package/build/src/akuma/index.d.ts +5 -3
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/nuke.js +8 -45
- package/build/src/akuma/providers/claude/events.d.ts +1 -1
- package/build/src/akuma/providers/claude/events.js +2 -1
- package/build/src/akuma/providers/codex-app-server/events.d.ts +2 -2
- package/build/src/akuma/providers/codex-app-server/events.js +6 -2
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +1 -1
- package/build/src/akuma/providers/opencode-sdk/events.js +5 -1
- package/build/src/akuma/providers/pi/events.d.ts +4 -0
- package/build/src/akuma/providers/pi/events.js +8 -3
- package/build/src/akuma/publication.js +2 -11
- package/build/src/akuma/request-rendezvous.d.ts +1 -1
- package/build/src/akuma/request-rendezvous.js +9 -8
- package/build/src/akuma/schema.d.ts +4 -6
- package/build/src/akuma/schema.js +35 -31
- package/build/src/akuma-body.js +3 -2
- package/build/src/cli/accepted.d.ts +1 -2
- package/build/src/cli/accepted.js +18 -16
- package/build/src/cli/commands/akuma-invoke.d.ts +0 -2
- package/build/src/cli/commands/akuma-invoke.js +8 -25
- package/build/src/cli/commands/akuma.d.ts +2 -1
- package/build/src/cli/commands/akuma.js +6 -6
- package/build/src/cli/commands/contract-help.d.ts +2 -1
- package/build/src/cli/commands/contract-help.js +27 -13
- package/build/src/cli/commands/contract-invoke.js +15 -0
- package/build/src/cli/commands/contract.d.ts +1 -0
- package/build/src/cli/commands/contract.js +4 -3
- package/build/src/cli/commands/install.js +8 -7
- package/build/src/cli/commands/task.d.ts +2 -0
- package/build/src/cli/commands/task.js +9 -5
- package/build/src/cli/invoke.d.ts +0 -1
- package/build/src/cli/invoke.js +19 -5
- package/build/src/cli/parse.d.ts +3 -0
- package/build/src/cli/parse.js +26 -13
- package/build/src/cli/render/akuma-activity.js +4 -4
- package/build/src/cli/render/akuma.js +7 -9
- package/build/src/cli/render/audit.js +28 -24
- package/build/src/cli/render/board.js +7 -3
- package/build/src/cli/render/catalog.js +16 -23
- package/build/src/cli/render/contract-history.js +20 -21
- package/build/src/cli/render/contract-observation.d.ts +2 -0
- package/build/src/cli/render/contract-observation.js +30 -6
- package/build/src/cli/render/contract.js +39 -33
- package/build/src/cli/render/kanshi-akuma.js +5 -6
- package/build/src/cli/render/kanshi.js +56 -99
- package/build/src/cli/render/nuke.js +3 -3
- package/build/src/cli/render/receipt.d.ts +15 -5
- package/build/src/cli/render/receipt.js +54 -20
- package/build/src/cli/render/refusal.d.ts +1 -1
- package/build/src/cli/render/refusal.js +55 -45
- package/build/src/cli/render/region.js +8 -8
- package/build/src/cli/render/settings.js +13 -11
- package/build/src/cli/render/status-set.js +13 -8
- package/build/src/cli/render/task.d.ts +1 -0
- package/build/src/cli/render/task.js +15 -14
- package/build/src/cli/render/terminal.d.ts +3 -1
- package/build/src/cli/render/terminal.js +33 -21
- package/build/src/cli/render/value.d.ts +3 -0
- package/build/src/cli/render/value.js +24 -0
- package/build/src/cli/result.d.ts +11 -18
- package/build/src/cli/runtime.d.ts +0 -1
- package/build/src/cli/runtime.js +46 -31
- package/build/src/cli/usage.d.ts +15 -2
- package/build/src/cli/usage.js +59 -6
- package/build/src/contract-guidance.d.ts +1 -0
- package/build/src/contract-guidance.js +26 -2
- package/build/src/contract-worktree.js +2 -1
- package/build/src/core/facts/gate.d.ts +8 -1
- package/build/src/core/facts/gate.js +48 -6
- package/build/src/core/subject.d.ts +4 -3
- package/build/src/core/subject.js +6 -2
- package/build/src/dispatch/association.d.ts +1 -13
- package/build/src/dispatch/association.js +1 -20
- package/build/src/git/hooks.d.ts +1 -1
- package/build/src/git/hooks.js +10 -3
- package/build/src/git/result-codec.js +4 -1
- package/build/src/git/scratch.js +1 -1
- package/build/src/git/target-placement.d.ts +2 -2
- package/build/src/git/target-placement.js +21 -20
- package/build/src/git/terminal-reconcile.js +16 -9
- package/build/src/git/workspace.d.ts +18 -3
- package/build/src/git/workspace.js +21 -4
- package/build/src/kanshi/read.js +3 -3
- package/build/src/library/address.js +2 -2
- package/build/src/library/akuma-creation.d.ts +6 -3
- package/build/src/library/akuma-creation.js +43 -23
- package/build/src/library/audit.js +11 -6
- package/build/src/library/catalog.js +2 -3
- package/build/src/library/composition.d.ts +1 -0
- package/build/src/library/continuation.d.ts +23 -31
- package/build/src/library/continuation.js +97 -99
- package/build/src/library/contract-bind.d.ts +2 -0
- package/build/src/library/contract-bind.js +31 -18
- package/build/src/library/contract-execution.d.ts +39 -0
- package/build/src/library/contract-execution.js +145 -0
- package/build/src/library/contract-forwarding.d.ts +9 -29
- package/build/src/library/contract-forwarding.js +14 -78
- package/build/src/library/contract-handle.js +38 -5
- package/build/src/library/contract-operations.d.ts +29 -0
- package/build/src/library/contract-operations.js +35 -2
- package/build/src/library/contract-types.d.ts +4 -0
- package/build/src/library/delivery.d.ts +2 -3
- package/build/src/library/delivery.js +3 -5
- package/build/src/library/execution-result.d.ts +243 -0
- package/build/src/library/execution-result.js +101 -0
- package/build/src/library/fleet.d.ts +3 -0
- package/build/src/library/fleet.js +6 -5
- package/build/src/library/keiyaku.d.ts +5 -4
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +121 -89
- package/build/src/library/mutation.js +109 -85
- package/build/src/library/reconcile.d.ts +7 -0
- package/build/src/library/reconcile.js +13 -0
- package/build/src/library/refusal.d.ts +6 -0
- package/build/src/library/refusal.js +40 -0
- package/build/src/plugin/runtime.d.ts +2 -2
- package/build/src/plugin/runtime.js +30 -22
- package/build/src/protocol/attempt.d.ts +3 -1
- package/build/src/protocol/attempt.js +13 -5
- package/build/src/protocol/audit.d.ts +8 -1
- package/build/src/protocol/audit.js +17 -8
- package/build/src/protocol/completion.d.ts +20 -16
- package/build/src/protocol/completion.js +162 -136
- package/build/src/protocol/deliver.d.ts +17 -15
- package/build/src/protocol/deliver.js +107 -52
- package/build/src/protocol/intent.d.ts +5 -2
- package/build/src/protocol/intent.js +9 -2
- package/build/src/protocol/operations.d.ts +4 -4
- package/build/src/protocol/operations.js +3 -1
- package/build/src/protocol/outcome.d.ts +9 -0
- package/build/src/protocol/placement.d.ts +3 -1
- package/build/src/protocol/placement.js +13 -0
- package/build/src/protocol/progress.d.ts +82 -0
- package/build/src/protocol/progress.js +142 -0
- package/build/src/protocol/read/status.d.ts +27 -3
- package/build/src/protocol/read/status.js +40 -9
- package/build/src/protocol/reintegrate.d.ts +2 -0
- package/build/src/protocol/reintegrate.js +1 -0
- package/build/src/protocol/result-codec.d.ts +2 -1
- package/build/src/protocol/result-codec.js +79 -49
- package/build/src/protocol/review.d.ts +11 -7
- package/build/src/protocol/review.js +17 -27
- package/build/src/protocol/run.d.ts +2 -0
- package/build/src/protocol/run.js +1 -0
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settings.js +15 -4
- package/build/src/verification/execution.d.ts +2 -2
- package/build/src/verification/execution.js +2 -2
- package/build/src/workspace-place.d.ts +4 -0
- package/build/src/workspace-place.js +15 -7
- package/build/src/world.d.ts +2 -2
- package/build/src/world.js +20 -51
- package/package.json +2 -2
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Keiyaku is a CLI that records plans, writes handoff terms for deliveries,
|
|
5
|
+
and calls delegates. Use this skill when you need to figure out which
|
|
6
|
+
Keiyaku skill or command to reach for.
|
|
4
7
|
---
|
|
5
8
|
|
|
6
9
|
# Keiyaku
|
|
@@ -33,7 +36,7 @@ keiyaku -C <repo> review [<contract>|@<contract>] --satisfied
|
|
|
33
36
|
```
|
|
34
37
|
|
|
35
38
|
```bash
|
|
36
|
-
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--
|
|
39
|
+
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
37
40
|
keiyaku -C <repo> wait <akuma-selector>... [--any | --all]
|
|
38
41
|
keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
|
|
39
42
|
```
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-akuma
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Calling an Akuma: delegating scouting, mechanical chores, or fanned-out
|
|
5
|
+
parallel work instead of doing it inline.
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Keiyaku Akuma
|
|
@@ -13,7 +15,7 @@ is accepted; the identity underneath never changes.
|
|
|
13
15
|
## Start One
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
keiyaku -C <cwd> call <akuma-name> [--alias @name] [--
|
|
18
|
+
keiyaku -C <cwd> call <akuma-name> [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
Give the worker's initial prompt as one argument (quote it when it contains
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-babysit
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Entering babysit mode: supervising parallel work across multiple Akuma and
|
|
5
|
+
Contracts as they advance, instead of doing the work yourself.
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Keiyaku Babysit
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-bind
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Binding a Keiyaku: writing down what a bounded piece of work must deliver and
|
|
5
|
+
how it will be judged, so it can be handed off.
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Keiyaku Bind
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-task
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Organizing, tracking progress and status, or binding work of three or more
|
|
5
|
+
steps as a Keiyaku Task: what to do, in what order, what depends on what,
|
|
6
|
+
what is done and what is blocked.
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Keiyaku Task
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-workflow
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
The Keiyaku workflow: how work moves through Keiyaku from planning through
|
|
5
|
+
binding, commissioning, delivery, review, and landing, and what you decide
|
|
6
|
+
at each stage.
|
|
4
7
|
---
|
|
5
8
|
|
|
6
9
|
# Holding a fulfillment loop
|
|
@@ -111,6 +114,15 @@ immutable commit without moving the branch or the real index — a
|
|
|
111
114
|
prior git commit is optional shaping, never a prerequisite. Deliver
|
|
112
115
|
never satisfies a review gate.
|
|
113
116
|
|
|
117
|
+
Conflict materialization projects an uncommitted merge; it does not resolve
|
|
118
|
+
its contents or record a delivery. After editing the final worktree bytes,
|
|
119
|
+
`deliver --include-dirty` can capture them even while the real index still
|
|
120
|
+
reports `UU`. No `git add` or commit is required, and capture preserves the
|
|
121
|
+
real index. The candidate includes the complete non-ignored worktree, not
|
|
122
|
+
just the Contract's Region or the conflict paths. Candidate capture and target
|
|
123
|
+
placement are separate: this authorization does not permit overwriting
|
|
124
|
+
blocking changes in the target checkout.
|
|
125
|
+
|
|
114
126
|
Audit observes: prospective candidate, integration, Verification,
|
|
115
127
|
diff, target. It records no candidate and satisfies no gate.
|
|
116
128
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { type TellResult } from "./body.js";
|
|
2
|
-
import { type KillEvidence } from "./heart/index.js";
|
|
3
|
-
import { type AkuId } from "./identity.js";
|
|
1
|
+
import { type TellResult, type TellWakeRuntime } from "./body.js";
|
|
2
|
+
import { HeldAkumaLeash, type KillEvidence } from "./heart/index.js";
|
|
3
|
+
import { type AkuId, type AkumaPaths } from "./identity.js";
|
|
4
4
|
import { type ActivityHistory, type ExactHistory } from "./projection.js";
|
|
5
5
|
import type { AkumaCallExecution, AkumaStatus, ForkReceipt, InterruptReceipt } from "./akuma.js";
|
|
6
6
|
import type { WorldRoot } from "../world.js";
|
|
7
7
|
declare const CALL_EXECUTION: unique symbol;
|
|
8
|
+
export declare function settleAkumaKill(paths: AkumaPaths, signal?: AbortSignal, retainLeash?: boolean): Promise<Readonly<{
|
|
9
|
+
evidence: KillEvidence;
|
|
10
|
+
leash?: HeldAkumaLeash;
|
|
11
|
+
}>>;
|
|
12
|
+
export declare function killAkumaWithRecovery(paths: AkumaPaths, recover?: (paths: AkumaPaths) => Promise<void>, signal?: AbortSignal): Promise<KillEvidence>;
|
|
8
13
|
export declare class AkumaHandle {
|
|
9
14
|
readonly id: AkuId;
|
|
10
15
|
private readonly worldPath;
|
|
@@ -23,13 +28,19 @@ export declare class AkumaHandle {
|
|
|
23
28
|
wait(predicate?: (status: AkumaStatus) => boolean, options?: Readonly<{
|
|
24
29
|
timeoutMs?: number;
|
|
25
30
|
}>): Promise<AkumaStatus>;
|
|
26
|
-
tell(body: string): Promise<TellResult>;
|
|
27
|
-
interrupt(body: string
|
|
28
|
-
|
|
31
|
+
tell(body: string, tellId?: string, recordedAt?: string, runtime?: TellWakeRuntime, schemaJson?: string): Promise<TellResult>;
|
|
32
|
+
interrupt(body: string, options?: Readonly<{
|
|
33
|
+
tellId?: string;
|
|
34
|
+
schemaJson?: string;
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
runtime?: TellWakeRuntime;
|
|
37
|
+
}>): Promise<InterruptReceipt>;
|
|
29
38
|
fork(input: Readonly<{
|
|
30
39
|
at: string;
|
|
31
40
|
}>): Promise<ForkReceipt>;
|
|
32
|
-
kill(
|
|
41
|
+
kill(options?: Readonly<{
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}>): Promise<KillEvidence>;
|
|
33
44
|
lastAnswer(): Promise<LastAnswer>;
|
|
34
45
|
}
|
|
35
46
|
/** Package-internal provenance retained only by the handle returned from call. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { handoffPendingTells, wakeRecordedTell } from "./body.js";
|
|
3
3
|
import { HeldAkumaLeash, activitySlice, readForkPoint, readHeart, readKill, readLastAnsweredTurn, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
|
|
4
|
+
import { acquireLeash } from "./control.js";
|
|
4
5
|
import { parsePublicHistoryId, pathsForAkuId } from "./identity.js";
|
|
5
6
|
import { projectTurns, selectHistory, selectExactHistory, } from "./projection.js";
|
|
6
7
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
@@ -18,56 +19,67 @@ function defaultWaitComplete(status) {
|
|
|
18
19
|
return (status.life !== "running" &&
|
|
19
20
|
!status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
|
|
20
21
|
}
|
|
21
|
-
async function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return { kind: "recorded", tellId: admitted.tell.id };
|
|
22
|
+
async function takeLeashUntilSignal(paths, bodySequence, signal, unbounded = false) {
|
|
23
|
+
const leash = await acquireLeash(paths, {
|
|
24
|
+
bodySequence,
|
|
25
|
+
...(signal === undefined && unbounded ? { deadline: Number.POSITIVE_INFINITY } : {}),
|
|
26
|
+
...(signal === undefined ? {} : { signal }),
|
|
27
|
+
});
|
|
28
|
+
if (leash !== null)
|
|
29
|
+
return leash;
|
|
30
|
+
const latestBody = (await readHeart(paths)).latestBody;
|
|
31
|
+
if (latestBody?.sequence === bodySequence && latestBody.hung !== undefined)
|
|
32
|
+
return { kind: "unavailable", evidence: "hung" };
|
|
33
|
+
if (latestBody?.sequence === bodySequence && latestBody.end !== undefined)
|
|
34
|
+
return { kind: "unavailable", evidence: "untidy" };
|
|
35
|
+
return { kind: "unavailable", evidence: "unavailable" };
|
|
36
36
|
}
|
|
37
|
-
async function
|
|
37
|
+
export async function settleAkumaKill(paths, signal, retainLeash = false) {
|
|
38
|
+
const request = await requestStop(paths, new Date().toISOString());
|
|
39
|
+
if (request.kind !== "requested") {
|
|
40
|
+
if (!retainLeash)
|
|
41
|
+
return { evidence: request.kind };
|
|
42
|
+
const leash = await acquireLeash(paths, signal === undefined ? {} : { signal });
|
|
43
|
+
return leash === null ? { evidence: "unavailable" } : { evidence: request.kind, leash };
|
|
44
|
+
}
|
|
45
|
+
const target = request.body;
|
|
46
|
+
const waited = await takeLeashUntilSignal(paths, target.sequence, signal);
|
|
47
|
+
if ("kind" in waited) {
|
|
48
|
+
if ((await readKill(paths, target.sequence)) !== null)
|
|
49
|
+
return { evidence: "killed" };
|
|
50
|
+
return { evidence: waited.evidence };
|
|
51
|
+
}
|
|
52
|
+
const leash = waited;
|
|
53
|
+
let retain = false;
|
|
38
54
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if ((await readKill(paths, target.sequence)) !== null) {
|
|
45
|
-
leash?.release();
|
|
46
|
-
return "killed";
|
|
55
|
+
if ((await readKill(paths, target.sequence)) !== null)
|
|
56
|
+
return { evidence: "killed" };
|
|
57
|
+
const settledBody = (await readHeart(paths)).latestBody;
|
|
58
|
+
if (settledBody?.sequence !== target.sequence) {
|
|
59
|
+
return { evidence: (await readKill(paths, target.sequence)) === null ? "unavailable" : "killed" };
|
|
47
60
|
}
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const body = (await readHeart(paths)).latestBody;
|
|
52
|
-
return body?.sequence === target.sequence && body.hung !== undefined ? "hung" : "unavailable";
|
|
61
|
+
if (settledBody.end !== "put-down") {
|
|
62
|
+
await leash.clearStop(paths);
|
|
63
|
+
return { evidence: "untidy" };
|
|
53
64
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const settled = await leash.settleStop(paths, target.sequence);
|
|
63
|
-
return settled === null ? "unavailable" : "killed";
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
65
|
+
const settled = await leash.settleStop(paths, target.sequence);
|
|
66
|
+
if (settled === null)
|
|
67
|
+
return { evidence: "unavailable" };
|
|
68
|
+
retain = retainLeash;
|
|
69
|
+
return retainLeash ? { evidence: "killed", leash } : { evidence: "killed" };
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (!retain)
|
|
66
73
|
leash.release();
|
|
67
|
-
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function killAkumaWithRecovery(paths, recover, signal) {
|
|
77
|
+
try {
|
|
78
|
+
return (await settleAkumaKill(paths, signal)).evidence;
|
|
68
79
|
}
|
|
69
80
|
finally {
|
|
70
|
-
|
|
81
|
+
if (recover !== undefined)
|
|
82
|
+
void recover(paths).catch(() => undefined);
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
export class AkumaHandle {
|
|
@@ -135,11 +147,19 @@ export class AkumaHandle {
|
|
|
135
147
|
await wait(deadline === undefined ? POLL_MS : Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
|
-
async tell(body) {
|
|
139
|
-
const
|
|
140
|
-
|
|
150
|
+
async tell(body, tellId = randomUUID(), recordedAt = new Date().toISOString(), runtime, schemaJson) {
|
|
151
|
+
const admitted = await recordTell(this.paths, {
|
|
152
|
+
kind: "tell",
|
|
153
|
+
id: tellId,
|
|
154
|
+
body,
|
|
155
|
+
recordedAt,
|
|
156
|
+
...(schemaJson === undefined ? {} : { schemaJson }),
|
|
157
|
+
});
|
|
158
|
+
if (admitted.kind === "not-born")
|
|
159
|
+
throw new AkumaNotBornError(this.id);
|
|
160
|
+
return await wakeRecordedTell(this.paths, admitted.tell.id, runtime);
|
|
141
161
|
}
|
|
142
|
-
async interrupt(body) {
|
|
162
|
+
async interrupt(body, options = {}) {
|
|
143
163
|
const request = await requestPause(this.paths, new Date().toISOString());
|
|
144
164
|
if (request.kind === "not-born") {
|
|
145
165
|
throw new AkumaNotBornError(this.id);
|
|
@@ -147,90 +167,47 @@ export class AkumaHandle {
|
|
|
147
167
|
let putDown = "was-idle";
|
|
148
168
|
let leash = await HeldAkumaLeash.try(this.paths);
|
|
149
169
|
if (leash === null) {
|
|
150
|
-
|
|
170
|
+
const waited = await takeLeashUntilSignal(this.paths, request.body.sequence, options.signal, true);
|
|
171
|
+
if ("kind" in waited)
|
|
172
|
+
return waited;
|
|
173
|
+
leash = waited;
|
|
151
174
|
putDown = "self-aborted";
|
|
152
175
|
}
|
|
153
|
-
if (leash === null) {
|
|
154
|
-
const body = (await readHeart(this.paths)).latestBody;
|
|
155
|
-
return {
|
|
156
|
-
kind: "unavailable",
|
|
157
|
-
evidence: body?.sequence === request.body.sequence && body.hung !== undefined ? "hung" : "unavailable",
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
const settledBody = (await readHeart(this.paths)).latestBody;
|
|
161
|
-
if (settledBody?.sequence === request.body.sequence && settledBody.hung !== undefined) {
|
|
162
|
-
try {
|
|
163
|
-
await leash.clearPause(this.paths);
|
|
164
|
-
}
|
|
165
|
-
finally {
|
|
166
|
-
leash.release();
|
|
167
|
-
}
|
|
168
|
-
return { kind: "unavailable", evidence: "hung" };
|
|
169
|
-
}
|
|
170
|
-
if (settledBody?.sequence !== request.body.sequence || settledBody.end === undefined) {
|
|
171
|
-
try {
|
|
172
|
-
await leash.clearPause(this.paths);
|
|
173
|
-
}
|
|
174
|
-
finally {
|
|
175
|
-
leash.release();
|
|
176
|
-
}
|
|
177
|
-
return { kind: "unavailable", evidence: "untidy" };
|
|
178
|
-
}
|
|
179
|
-
if (request.body.end !== undefined || settledBody.end !== "put-down")
|
|
180
|
-
putDown = "was-idle";
|
|
181
176
|
let recorded;
|
|
182
177
|
try {
|
|
183
|
-
|
|
184
|
-
const admitted = await leash.recordInterruptTell(this.paths, {
|
|
185
|
-
kind: "tell",
|
|
186
|
-
id,
|
|
187
|
-
body,
|
|
188
|
-
recordedAt: new Date().toISOString(),
|
|
189
|
-
});
|
|
190
|
-
if (admitted.kind === "not-born")
|
|
191
|
-
throw new AkumaNotBornError(this.id);
|
|
192
|
-
recorded = { kind: "recorded", tellId: admitted.tell.id };
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
leash.release();
|
|
196
|
-
}
|
|
197
|
-
return { kind: "interrupted", putDown, tell: await wakeRecordedTell(this.paths, recorded.tellId) };
|
|
198
|
-
}
|
|
199
|
-
async interruptSchema(body, schemaJson) {
|
|
200
|
-
const request = await requestPause(this.paths, new Date().toISOString());
|
|
201
|
-
if (request.kind === "not-born")
|
|
202
|
-
throw new AkumaNotBornError(this.id);
|
|
203
|
-
let tellId;
|
|
204
|
-
let leash = await HeldAkumaLeash.try(this.paths);
|
|
205
|
-
if (leash === null)
|
|
206
|
-
leash = await takeLeashUntil(this.paths, performance.now() + CONTROL_RESPONSE_MS);
|
|
207
|
-
if (leash === null)
|
|
208
|
-
throw new Error("schema interrupt could not acquire Body leash");
|
|
209
|
-
try {
|
|
178
|
+
options.signal?.throwIfAborted();
|
|
210
179
|
const settledBody = (await readHeart(this.paths)).latestBody;
|
|
211
180
|
if (settledBody?.sequence === request.body.sequence && settledBody.hung !== undefined) {
|
|
212
181
|
await leash.clearPause(this.paths);
|
|
213
|
-
|
|
182
|
+
return { kind: "unavailable", evidence: "hung" };
|
|
214
183
|
}
|
|
215
184
|
if (settledBody?.sequence !== request.body.sequence || settledBody.end === undefined) {
|
|
216
185
|
await leash.clearPause(this.paths);
|
|
217
|
-
|
|
186
|
+
return { kind: "unavailable", evidence: "untidy" };
|
|
218
187
|
}
|
|
188
|
+
if (request.body.end !== undefined || settledBody.end !== "put-down")
|
|
189
|
+
putDown = "was-idle";
|
|
190
|
+
options.signal?.throwIfAborted();
|
|
191
|
+
const id = randomUUID();
|
|
219
192
|
const admitted = await leash.recordInterruptTell(this.paths, {
|
|
220
193
|
kind: "tell",
|
|
221
|
-
id:
|
|
194
|
+
id: options.tellId ?? id,
|
|
222
195
|
body,
|
|
223
196
|
recordedAt: new Date().toISOString(),
|
|
224
|
-
schemaJson,
|
|
197
|
+
...(options.schemaJson === undefined ? {} : { schemaJson: options.schemaJson }),
|
|
225
198
|
});
|
|
226
199
|
if (admitted.kind === "not-born")
|
|
227
200
|
throw new AkumaNotBornError(this.id);
|
|
228
|
-
|
|
201
|
+
recorded = { kind: "recorded", tellId: admitted.tell.id };
|
|
229
202
|
}
|
|
230
203
|
finally {
|
|
231
204
|
leash.release();
|
|
232
205
|
}
|
|
233
|
-
return
|
|
206
|
+
return {
|
|
207
|
+
kind: "interrupted",
|
|
208
|
+
putDown,
|
|
209
|
+
tell: await wakeRecordedTell(this.paths, recorded.tellId, options.runtime),
|
|
210
|
+
};
|
|
234
211
|
}
|
|
235
212
|
async fork(input) {
|
|
236
213
|
const source = await readSoul(this.paths);
|
|
@@ -296,8 +273,8 @@ export class AkumaHandle {
|
|
|
296
273
|
};
|
|
297
274
|
}
|
|
298
275
|
}
|
|
299
|
-
async kill() {
|
|
300
|
-
return await killAkumaWithRecovery(this.paths);
|
|
276
|
+
async kill(options = {}) {
|
|
277
|
+
return await killAkumaWithRecovery(this.paths, handoffPendingTells, options.signal);
|
|
301
278
|
}
|
|
302
279
|
async lastAnswer() {
|
|
303
280
|
const turn = await readLastAnsweredTurn(this.paths);
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import type { AllowedAction } from "./allowed.js";
|
|
2
|
+
import type { AkumaStatus } from "./akuma.js";
|
|
3
|
+
import type { InterruptReceipt, KillEvidence } from "./akuma.js";
|
|
2
4
|
import { type AkuId } from "./identity.js";
|
|
3
5
|
import { type ActivityHistory } from "./projection.js";
|
|
4
6
|
import type { Settings } from "../settings.js";
|
|
5
7
|
import type { WorldRoot } from "../world.js";
|
|
6
|
-
import type
|
|
8
|
+
import { type Schema } from "./schema.js";
|
|
9
|
+
export type AkumaIdleOptions = Readonly<{
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}>;
|
|
12
|
+
export type AkumaHistoryOptions = Readonly<{
|
|
13
|
+
before?: number;
|
|
14
|
+
since?: number;
|
|
15
|
+
limit?: number;
|
|
16
|
+
}>;
|
|
17
|
+
export type AkumaSignalOptions = Readonly<{
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}>;
|
|
7
20
|
export type AkumaBirthInput = Readonly<{
|
|
8
21
|
root: WorldRoot;
|
|
9
22
|
cwd?: string;
|
|
@@ -25,7 +38,9 @@ export declare class Akuma {
|
|
|
25
38
|
static select(root: WorldRoot, selector: string): Akuma;
|
|
26
39
|
tell(text: string): Promise<string>;
|
|
27
40
|
tell<T>(text: string, options: AkumaTellOptions<T>): Promise<T>;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
status(): Promise<AkumaStatus>;
|
|
42
|
+
interrupt(text: string, options?: AkumaSignalOptions): Promise<InterruptReceipt>;
|
|
43
|
+
idle(options?: AkumaIdleOptions): Promise<void>;
|
|
44
|
+
history(options?: AkumaHistoryOptions): Promise<ActivityHistory>;
|
|
45
|
+
kill(options?: AkumaSignalOptions): Promise<KillEvidence>;
|
|
31
46
|
}
|
|
@@ -1,44 +1,53 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { callReadonly, canonicalBirthCwd } from "./call-input.js";
|
|
3
|
-
import { spawnAkumaBody
|
|
3
|
+
import { spawnAkumaBody } from "./body.js";
|
|
4
4
|
import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
|
|
5
|
-
import { AkumaDecodeError,
|
|
5
|
+
import { AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
|
6
6
|
import { AkumaHandle } from "./akuma-handle.js";
|
|
7
|
-
import { POLL_MS, defaultWaitComplete
|
|
7
|
+
import { POLL_MS, defaultWaitComplete } from "./akuma.js";
|
|
8
8
|
import { bornStatus } from "./akuma-observe.js";
|
|
9
9
|
import { loadArchetype } from "./archetype.js";
|
|
10
|
-
import { activitySlice, readTell, readTurn
|
|
10
|
+
import { activitySlice, readTell, readTurn } from "./heart/index.js";
|
|
11
11
|
import { parseAkuId, pathsForAkuId } from "./identity.js";
|
|
12
12
|
import { birthAkuma, launchAkuma } from "./publication.js";
|
|
13
13
|
import { projectTurns, selectHistory } from "./projection.js";
|
|
14
14
|
import { settings as readSettings } from "../settings.js";
|
|
15
|
+
import { schemaJsonText } from "./schema.js";
|
|
16
|
+
import { abortable } from "./abort.js";
|
|
15
17
|
const HISTORY_LIMIT = 12;
|
|
18
|
+
function signalOption(value) {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (!(value instanceof AbortSignal))
|
|
22
|
+
throw new TypeError("signal must be an AbortSignal");
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
16
25
|
function wait(milliseconds) {
|
|
17
26
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
18
27
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
function recordedTell(result) {
|
|
29
|
+
if (result.wake.kind === "failed")
|
|
30
|
+
throw new AkumaProviderError(result.wake.diagnostic);
|
|
31
|
+
return { tellId: result.admission.tellId };
|
|
32
|
+
}
|
|
33
|
+
async function recordPlainTell(id, root, body, tellId) {
|
|
34
|
+
const admitted = await new AkumaHandle(id, root).tell(body, tellId);
|
|
35
|
+
return recordedTell(admitted);
|
|
24
36
|
}
|
|
25
|
-
async function recordSchemaTell(
|
|
37
|
+
async function recordSchemaTell(input) {
|
|
38
|
+
const { id, body, tellId, options, root } = input;
|
|
26
39
|
if (options.interrupt === true) {
|
|
27
|
-
const interrupted = await new AkumaHandle(id, root).
|
|
28
|
-
|
|
40
|
+
const interrupted = await new AkumaHandle(id, root).interrupt(body, {
|
|
41
|
+
tellId,
|
|
42
|
+
schemaJson: schemaJsonText(options.schema),
|
|
43
|
+
});
|
|
44
|
+
if (interrupted.kind === "unavailable") {
|
|
45
|
+
throw new AkumaProviderError(`schema interrupt unavailable: ${interrupted.evidence}`);
|
|
46
|
+
}
|
|
47
|
+
return recordedTell(interrupted.tell);
|
|
29
48
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
kind: "tell",
|
|
33
|
-
id: tellId,
|
|
34
|
-
body,
|
|
35
|
-
recordedAt,
|
|
36
|
-
schemaJson: options.schema.jsonText,
|
|
37
|
-
};
|
|
38
|
-
const admitted = await recordTell(paths, tell);
|
|
39
|
-
if (admitted.kind === "not-born")
|
|
40
|
-
throw new AkumaNotBornError(id);
|
|
41
|
-
return admitted.tell.id;
|
|
49
|
+
const admitted = await new AkumaHandle(id, root).tell(body, tellId, undefined, undefined, schemaJsonText(options.schema));
|
|
50
|
+
return recordedTell(admitted);
|
|
42
51
|
}
|
|
43
52
|
function outcomeError(outcome) {
|
|
44
53
|
if (outcome.kind === "invalid-output")
|
|
@@ -54,9 +63,6 @@ async function boundOutcome(paths, tell) {
|
|
|
54
63
|
return turn?.end?.outcome ?? null;
|
|
55
64
|
}
|
|
56
65
|
async function awaitTellOutcome(paths, tellId) {
|
|
57
|
-
const wake = await wakeRecordedTell(paths, tellId);
|
|
58
|
-
if (wake.wake.kind === "failed")
|
|
59
|
-
throw new AkumaProviderError(wake.wake.diagnostic);
|
|
60
66
|
for (;;) {
|
|
61
67
|
const tell = await readTell(paths, tellId);
|
|
62
68
|
if (tell === null)
|
|
@@ -126,9 +132,15 @@ export class Akuma {
|
|
|
126
132
|
throw new TypeError("Akuma tell text must be a string");
|
|
127
133
|
const tellId = randomUUID();
|
|
128
134
|
const recorded = options === undefined
|
|
129
|
-
? await recordPlainTell(this.
|
|
130
|
-
: await recordSchemaTell(
|
|
131
|
-
|
|
135
|
+
? await recordPlainTell(this.id, this.root, text, tellId)
|
|
136
|
+
: await recordSchemaTell({
|
|
137
|
+
id: this.id,
|
|
138
|
+
body: text,
|
|
139
|
+
tellId,
|
|
140
|
+
options,
|
|
141
|
+
root: this.root,
|
|
142
|
+
});
|
|
143
|
+
const outcome = await awaitTellOutcome(this.paths, recorded.tellId);
|
|
132
144
|
if (outcome.kind !== "answered")
|
|
133
145
|
outcomeError(outcome);
|
|
134
146
|
if (options === undefined)
|
|
@@ -142,27 +154,79 @@ export class Akuma {
|
|
|
142
154
|
throw new AkumaDecodeError(error instanceof Error ? error.message : "Answer is not valid JSON", outcome.answer);
|
|
143
155
|
}
|
|
144
156
|
try {
|
|
145
|
-
return options.schema.
|
|
157
|
+
return options.schema.decode(parsed);
|
|
146
158
|
}
|
|
147
159
|
catch (error) {
|
|
148
160
|
throw new AkumaDecodeError(error instanceof Error ? error.message : "Answer failed schema decode", outcome.answer);
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
|
-
async
|
|
163
|
+
async status() {
|
|
164
|
+
return (await bornStatus(this.paths, this.id, { aperture: "monitoring" })).status;
|
|
165
|
+
}
|
|
166
|
+
async interrupt(text, options = {}) {
|
|
167
|
+
if (typeof text !== "string")
|
|
168
|
+
throw new TypeError("Akuma interrupt text must be a string");
|
|
169
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
170
|
+
throw new TypeError("Akuma interrupt options must be an object");
|
|
171
|
+
}
|
|
172
|
+
const signal = signalOption(options.signal);
|
|
173
|
+
signal?.throwIfAborted();
|
|
174
|
+
const operation = new AkumaHandle(this.id, this.root).interrupt(text, signal === undefined ? {} : { signal });
|
|
175
|
+
return await abortable(operation, signal ?? new AbortController().signal);
|
|
176
|
+
}
|
|
177
|
+
async idle(options = {}) {
|
|
178
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
179
|
+
throw new TypeError("Akuma idle options must be an object");
|
|
180
|
+
}
|
|
181
|
+
const unknown = Object.keys(options).find((key) => key !== "timeoutMs");
|
|
182
|
+
if (unknown !== undefined)
|
|
183
|
+
throw new TypeError(`Akuma idle options has unknown field: ${unknown}`);
|
|
184
|
+
if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
|
|
185
|
+
throw new TypeError("Akuma idle timeoutMs must be a nonnegative finite millisecond duration");
|
|
186
|
+
}
|
|
187
|
+
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
152
188
|
for (;;) {
|
|
153
189
|
const observed = await bornStatus(this.paths, this.id, { aperture: "monitoring" });
|
|
154
|
-
if (defaultWaitComplete(observed.status))
|
|
190
|
+
if (defaultWaitComplete(observed.status) || (deadline !== undefined && performance.now() >= deadline))
|
|
155
191
|
return;
|
|
156
|
-
await wait(POLL_MS);
|
|
192
|
+
await wait(deadline === undefined ? POLL_MS : Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
|
|
157
193
|
}
|
|
158
194
|
}
|
|
159
|
-
async history() {
|
|
195
|
+
async history(options = {}) {
|
|
196
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
197
|
+
throw new TypeError("Akuma history options must be an object");
|
|
198
|
+
}
|
|
199
|
+
const unknown = Object.keys(options).find((key) => !["before", "since", "limit"].includes(key));
|
|
200
|
+
if (unknown !== undefined)
|
|
201
|
+
throw new TypeError(`Akuma history options has unknown field: ${unknown}`);
|
|
202
|
+
if (options.before !== undefined && options.since !== undefined) {
|
|
203
|
+
throw new TypeError("Akuma history before and since are mutually exclusive");
|
|
204
|
+
}
|
|
205
|
+
for (const [name, value] of [
|
|
206
|
+
["before", options.before],
|
|
207
|
+
["since", options.since],
|
|
208
|
+
]) {
|
|
209
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
|
|
210
|
+
throw new TypeError(`Akuma history ${name} must be a positive safe integer`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const limit = options.limit ?? HISTORY_LIMIT;
|
|
214
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 5_000) {
|
|
215
|
+
throw new TypeError("Akuma history limit must be a positive safe integer no greater than 5000");
|
|
216
|
+
}
|
|
160
217
|
const slice = await activitySlice(this.paths);
|
|
161
218
|
return selectHistory(projectTurns(slice.rows, { lowestRetained: slice.lowestRetained, highest: slice.highest }), {
|
|
162
|
-
|
|
219
|
+
...(options.before === undefined ? {} : { before: options.before }),
|
|
220
|
+
...(options.since === undefined ? {} : { since: options.since }),
|
|
221
|
+
limit,
|
|
163
222
|
});
|
|
164
223
|
}
|
|
165
|
-
async kill() {
|
|
166
|
-
|
|
224
|
+
async kill(options = {}) {
|
|
225
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
226
|
+
throw new TypeError("Akuma kill options must be an object");
|
|
227
|
+
}
|
|
228
|
+
const signal = signalOption(options.signal);
|
|
229
|
+
signal?.throwIfAborted();
|
|
230
|
+
return await abortable(new AkumaHandle(this.id, this.root).kill(signal === undefined ? {} : { signal }), signal ?? new AbortController().signal);
|
|
167
231
|
}
|
|
168
232
|
}
|