@astrosheep/keiyaku 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/akuma/akuma.d.ts +34 -11
- package/build/src/akuma/akuma.js +121 -61
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +40 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +3 -1
- package/build/src/akuma/body.js +19 -8
- package/build/src/akuma/heart/facts.d.ts +49 -7
- package/build/src/akuma/heart/index.d.ts +4 -8
- package/build/src/akuma/heart/index.js +76 -15
- package/build/src/akuma/heart/rows.d.ts +7 -5
- package/build/src/akuma/heart/rows.js +98 -11
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +15 -10
- package/build/src/akuma/heart/soul.js +3 -1
- package/build/src/akuma/heart/storage.d.ts +2 -0
- package/build/src/akuma/heart/storage.js +20 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +4 -1
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +4 -4
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -2
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +36 -7
- package/build/src/akuma/requests.d.ts +70 -4
- package/build/src/akuma/requests.js +288 -77
- package/build/src/akuma-body.d.ts +1 -0
- package/build/src/akuma-body.js +31 -0
- package/build/src/cli/accepted.d.ts +4 -1
- package/build/src/cli/accepted.js +26 -4
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +3 -3
- package/build/src/cli/commands/contract.js +1 -1
- package/build/src/cli/commands/install.js +4 -4
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/commands/task.js +4 -4
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +5 -1
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +90 -73
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +1 -0
- package/build/src/cli/parse.js +8 -4
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +84 -13
- package/build/src/cli/render/audit.js +16 -2
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +203 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +9 -1
- package/build/src/cli/render/text.js +3 -1
- package/build/src/cli/result.d.ts +38 -3
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/cli/usage.d.ts +2 -0
- package/build/src/cli/usage.js +4 -0
- package/build/src/contract-worktree.js +3 -0
- package/build/src/dispatch/index.js +50 -39
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +2 -6
- package/build/src/git/integration.js +8 -8
- package/build/src/git/read-observation.js +1 -0
- package/build/src/git/reconcile.d.ts +6 -1
- package/build/src/git/reconcile.js +55 -9
- package/build/src/git/tender.d.ts +19 -4
- package/build/src/git/tender.js +44 -13
- package/build/src/git/terminal-seal.d.ts +14 -0
- package/build/src/git/terminal-seal.js +15 -2
- package/build/src/git/workspace.d.ts +1 -0
- package/build/src/git/workspace.js +12 -4
- package/build/src/index.d.ts +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +20 -22
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/bind.js +3 -3
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +4 -0
- package/build/src/library/configuration.js +19 -0
- package/build/src/library/contract.d.ts +15 -1
- package/build/src/library/contract.js +51 -4
- package/build/src/library/fleet.d.ts +47 -9
- package/build/src/library/fleet.js +184 -44
- package/build/src/library/input.js +1 -0
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/protocol/bind.d.ts +1 -2
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/operations.d.ts +4 -1
- package/build/src/protocol/operations.js +33 -27
- package/build/src/protocol/read/status.d.ts +3 -1
- package/build/src/protocol/read/status.js +24 -14
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/settle.d.ts +1 -0
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -3
- package/build/src/task/board.js +73 -26
- package/build/src/task/index.d.ts +3 -3
- package/build/src/task/index.js +7 -5
- package/build/src/task/operations.d.ts +2 -19
- package/build/src/task/operations.js +29 -7
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/verification/execution.js +2 -1
- package/package.json +3 -1
|
@@ -31,7 +31,7 @@ keiyaku -C <repo> review [<contract>|@<contract>] --satisfied
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
34
|
+
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
35
35
|
keiyaku -C <repo> wait <akuma-selector>... [--any | --all]
|
|
36
36
|
keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
|
|
37
37
|
```
|
|
@@ -13,7 +13,7 @@ is accepted; the identity underneath never changes.
|
|
|
13
13
|
## Start One
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
keiyaku -C <cwd> call <akuma-name> [--alias @name] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
16
|
+
keiyaku -C <cwd> call <akuma-name> [--alias @name] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Give the worker's initial prompt as one argument (quote it when it contains
|
|
@@ -102,6 +102,11 @@ Decidable without consulting you.>
|
|
|
102
102
|
KEIYAKU
|
|
103
103
|
~~~~
|
|
104
104
|
|
|
105
|
+
Use separate fences for checks that need separate timeouts or results. Fences
|
|
106
|
+
run top-to-bottom, and later fences may use earlier outputs. Put setup/build
|
|
107
|
+
before its consumers; use `&&` in one fence only when the consumer must stop
|
|
108
|
+
if setup fails.
|
|
109
|
+
|
|
105
110
|
Each declaration may set an individual timeout in its fence info string, using
|
|
106
111
|
an explicit duration unit such as `bash timeout=5m`. Omit it for an unbounded
|
|
107
112
|
declaration; there is no Verification-wide timeout.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-task
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Use when recording, decomposing, or tracking a complex task, typically
|
|
5
|
+
involving three or more steps.
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Keiyaku Task
|
|
@@ -165,19 +165,20 @@ old Contract onto a different delivery.
|
|
|
165
165
|
|
|
166
166
|
## Audit Before Delivery
|
|
167
167
|
|
|
168
|
-
Audit is
|
|
168
|
+
Audit is an evidence window for a prospective delivery:
|
|
169
169
|
|
|
170
170
|
```bash
|
|
171
171
|
keiyaku audit <contract> --diff
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
report
|
|
174
|
+
It aggregates facts from the same candidate preparation used by `deliver`:
|
|
175
|
+
the prospective candidate and integration identities, the requested diff, the
|
|
176
|
+
declared Verification commands and their observed results, and the target
|
|
177
|
+
placement observation. A terminal run may record subject-bound `verified`
|
|
178
|
+
testimony for those observed commands; it does not deliver, request placement,
|
|
179
|
+
satisfy a review gate, or decide whether the candidate should land. The
|
|
180
|
+
coordinator judges the returned facts; a worker's completion report is not a
|
|
181
|
+
substitute for them.
|
|
181
182
|
|
|
182
183
|
## Deliver
|
|
183
184
|
|
|
@@ -3,6 +3,7 @@ import { type AkuId } from "./identity.js";
|
|
|
3
3
|
import { type ActivityHistory, type ActivitySnapshot } from "./projection.js";
|
|
4
4
|
import { type Settings } from "../settings.js";
|
|
5
5
|
import type { WorldRoot } from "../world.js";
|
|
6
|
+
import { type AllowedAction } from "./allowed.js";
|
|
6
7
|
export type AkumaListRow = Readonly<{
|
|
7
8
|
id: AkuId;
|
|
8
9
|
archetype: string;
|
|
@@ -36,6 +37,22 @@ export type AkumaList = Readonly<{
|
|
|
36
37
|
export type AkumaListInput = Readonly<{
|
|
37
38
|
archetype?: string;
|
|
38
39
|
}>;
|
|
40
|
+
export type AkumaCallExecution = Readonly<{
|
|
41
|
+
cwd: string;
|
|
42
|
+
source: "input" | "caller" | "process" | "world";
|
|
43
|
+
}>;
|
|
44
|
+
declare const CALL_EXECUTION: unique symbol;
|
|
45
|
+
declare const CALL_WITH_CONTEXT: unique symbol;
|
|
46
|
+
export type AkumaCallInput = Readonly<{
|
|
47
|
+
archetype: string;
|
|
48
|
+
body: string;
|
|
49
|
+
cwd?: string;
|
|
50
|
+
allowed?: readonly AllowedAction[];
|
|
51
|
+
}>;
|
|
52
|
+
type AkumaCallContext = Readonly<{
|
|
53
|
+
initiatorCwd?: string;
|
|
54
|
+
cwdCanonical?: true;
|
|
55
|
+
}>;
|
|
39
56
|
export type TellResult = Readonly<{
|
|
40
57
|
admission: Readonly<{
|
|
41
58
|
tellId: string;
|
|
@@ -76,8 +93,7 @@ export declare class AkumaNotBornError extends Error {
|
|
|
76
93
|
readonly kind = "akuma-not-born";
|
|
77
94
|
constructor(id: AkuId);
|
|
78
95
|
}
|
|
79
|
-
|
|
80
|
-
export declare function readActionFeedbackStatus(worldPath: WorldRoot, id: AkuId): Promise<AkumaStatus>;
|
|
96
|
+
export declare function tellAkumaWithId(worldPath: WorldRoot, id: AkuId, body: string, tellId: string, recordedAt?: string): Promise<TellResult>;
|
|
81
97
|
export type BudgetedStatusObservation = Readonly<{
|
|
82
98
|
status: AkumaStatus;
|
|
83
99
|
ordinarySelected: number;
|
|
@@ -86,10 +102,13 @@ export type BudgetedStatusObservation = Readonly<{
|
|
|
86
102
|
export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, input: Readonly<{
|
|
87
103
|
ordinaryBudget: number;
|
|
88
104
|
}>): Promise<BudgetedStatusObservation>;
|
|
105
|
+
/** Package-internal birth projection used by the composed Library call result. */
|
|
106
|
+
export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
|
|
89
107
|
export declare class AkumaHandle {
|
|
90
108
|
readonly id: AkuId;
|
|
91
109
|
private readonly worldPath;
|
|
92
|
-
|
|
110
|
+
readonly [CALL_EXECUTION]?: AkumaCallExecution;
|
|
111
|
+
constructor(id: AkuId, worldPath: WorldRoot, execution?: AkumaCallExecution);
|
|
93
112
|
private get paths();
|
|
94
113
|
status(): Promise<AkumaStatus>;
|
|
95
114
|
history(input?: Readonly<{
|
|
@@ -108,26 +127,30 @@ export declare class AkumaHandle {
|
|
|
108
127
|
kill(): Promise<KillEvidence>;
|
|
109
128
|
lastAnswer(): Promise<LastAnswer>;
|
|
110
129
|
}
|
|
130
|
+
/** Package-internal provenance retained only by the handle returned from call. */
|
|
131
|
+
export declare function akumaCallExecution(handle: AkumaHandle): AkumaCallExecution | undefined;
|
|
111
132
|
export type LastAnswer = Readonly<{
|
|
112
133
|
kind: "answer";
|
|
113
134
|
answer: string;
|
|
114
135
|
}> | Readonly<{
|
|
115
136
|
kind: "no-answer";
|
|
116
137
|
}>;
|
|
138
|
+
type AkumaConfiguration = Readonly<{
|
|
139
|
+
home?: string;
|
|
140
|
+
settings?: Settings;
|
|
141
|
+
}>;
|
|
117
142
|
export declare class Akuma {
|
|
118
143
|
private readonly path;
|
|
119
|
-
private readonly
|
|
144
|
+
private readonly configuration;
|
|
120
145
|
private constructor();
|
|
121
|
-
static of(root: WorldRoot,
|
|
122
|
-
private settings;
|
|
146
|
+
static of(root: WorldRoot, input?: AkumaConfiguration): Akuma;
|
|
123
147
|
of(input: Readonly<{
|
|
124
148
|
id: string;
|
|
125
149
|
}>): AkumaHandle;
|
|
126
150
|
listArchetypes(): Promise<readonly string[]>;
|
|
127
|
-
call(input:
|
|
128
|
-
|
|
129
|
-
body: string;
|
|
130
|
-
cwd?: string;
|
|
131
|
-
}>): Promise<AkumaHandle>;
|
|
151
|
+
call(input: AkumaCallInput): Promise<AkumaHandle>;
|
|
152
|
+
[CALL_WITH_CONTEXT](input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
|
|
132
153
|
list(input?: AkumaListInput): Promise<AkumaList>;
|
|
133
154
|
}
|
|
155
|
+
/** Package-internal call path for a composition owner that already canonicalized cwd. */
|
|
156
|
+
export declare function callAkumaWithContext(akuma: Akuma, input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
|
package/build/src/akuma/akuma.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { readdir, realpath, stat } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { CONTROL_RESPONSE_MS, spawnAkumaBody } from "./body.js";
|
|
5
|
-
import { HeldAkumaLeash, activitySlice, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
|
|
5
|
+
import { HeldAkumaLeash, activitySlice, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
|
|
6
6
|
import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, parseAkuId, pathsForAkuId, archetypeName, } from "./identity.js";
|
|
7
7
|
import { ordinarySelectedCount, ordinarySnapshotBudget, projectTurns, selectActivitySnapshot, selectHistory, selectSnapshot, } from "./projection.js";
|
|
8
8
|
import { listArchetypes as readArchetypes, loadArchetype } from "./archetype.js";
|
|
@@ -10,7 +10,22 @@ import { publishAkuma } from "./publication.js";
|
|
|
10
10
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
11
11
|
import { injectedBodyRequests, requestBodyCall } from "./requests.js";
|
|
12
12
|
import { settings as readSettings } from "../settings.js";
|
|
13
|
+
import { decodeAllowedActions, } from "./allowed.js";
|
|
13
14
|
const POLL_MS = 25;
|
|
15
|
+
async function canonicalBirthCwd(input) {
|
|
16
|
+
const selected = resolve(input);
|
|
17
|
+
try {
|
|
18
|
+
const canonical = await realpath(selected);
|
|
19
|
+
if (!(await stat(canonical)).isDirectory())
|
|
20
|
+
throw new Error("not a directory");
|
|
21
|
+
return canonical;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`cwd is not an existing directory: ${input}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const CALL_EXECUTION = Symbol("akuma-call-execution");
|
|
28
|
+
const CALL_WITH_CONTEXT = Symbol("akuma-call-with-context");
|
|
14
29
|
export class AkumaNotBornError extends Error {
|
|
15
30
|
id;
|
|
16
31
|
kind = "akuma-not-born";
|
|
@@ -33,9 +48,8 @@ async function takeLeashUntil(paths, deadline) {
|
|
|
33
48
|
await wait(POLL_MS);
|
|
34
49
|
}
|
|
35
50
|
}
|
|
36
|
-
async function recordTellBody(paths, akuma, body) {
|
|
37
|
-
const
|
|
38
|
-
const admitted = await recordTell(paths, { kind: "tell", id, body, recordedAt: new Date().toISOString() });
|
|
51
|
+
async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt = new Date().toISOString()) {
|
|
52
|
+
const admitted = await recordTell(paths, { kind: "tell", id, body, recordedAt });
|
|
39
53
|
if (admitted.kind === "not-born")
|
|
40
54
|
throw new AkumaNotBornError(akuma);
|
|
41
55
|
return { kind: "recorded", tellId: admitted.tell.id };
|
|
@@ -48,10 +62,34 @@ async function wakeTell(paths, tellId) {
|
|
|
48
62
|
catch (error) {
|
|
49
63
|
return {
|
|
50
64
|
admission: { tellId, fact: "recorded" },
|
|
51
|
-
wake: { kind: "failed", diagnostic:
|
|
65
|
+
wake: { kind: "failed", diagnostic: diagnostic(error) },
|
|
52
66
|
};
|
|
53
67
|
}
|
|
54
68
|
}
|
|
69
|
+
export async function tellAkumaWithId(worldPath, id, body, tellId, recordedAt = new Date().toISOString()) {
|
|
70
|
+
const paths = pathsForAkuId(worldPath, id);
|
|
71
|
+
const recorded = await recordTellBody(paths, id, body, tellId, recordedAt);
|
|
72
|
+
return await wakeTell(paths, recorded.tellId);
|
|
73
|
+
}
|
|
74
|
+
function diagnostic(error) {
|
|
75
|
+
return error instanceof Error ? error.message : String(error);
|
|
76
|
+
}
|
|
77
|
+
async function fleetListRow(paths, expected) {
|
|
78
|
+
const snapshot = await readHeart(paths);
|
|
79
|
+
if (snapshot.soul !== null)
|
|
80
|
+
return await bornListRow(paths, expected, snapshot);
|
|
81
|
+
try {
|
|
82
|
+
if (await probeLeash(paths) === "held")
|
|
83
|
+
return { id: expected, life: "unborn" };
|
|
84
|
+
const seal = await readSeal(paths);
|
|
85
|
+
return seal === null ? { id: expected, life: "unborn" } : { id: expected, life: "stillborn", seal };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (isHeartAbsent(error))
|
|
89
|
+
return { id: expected, life: "unborn" };
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
55
93
|
async function bornListRow(paths, expected, snapshot) {
|
|
56
94
|
snapshot ??= await readHeart(paths);
|
|
57
95
|
if (snapshot.soul === null)
|
|
@@ -81,7 +119,7 @@ async function bornStatus(paths, expected, budget) {
|
|
|
81
119
|
const resumeUnsupported = current.life === "stranded"
|
|
82
120
|
&& snapshot.latestSession?.provider === snapshot.soul.provider.name
|
|
83
121
|
&& resolveProviderExecution(snapshot.soul.provider).adapter.resume === undefined;
|
|
84
|
-
const slice = await activitySlice(paths
|
|
122
|
+
const slice = await activitySlice(paths);
|
|
85
123
|
return {
|
|
86
124
|
id: current.id,
|
|
87
125
|
life: current.life,
|
|
@@ -92,10 +130,6 @@ async function bornStatus(paths, expected, budget) {
|
|
|
92
130
|
: selectSnapshot(projectTurns(slice.rows), budget),
|
|
93
131
|
};
|
|
94
132
|
}
|
|
95
|
-
/** Package-internal action observation; it uses the same snapshot selector as status. */
|
|
96
|
-
export async function readActionFeedbackStatus(worldPath, id) {
|
|
97
|
-
return await bornStatus(pathsForAkuId(worldPath, id), id);
|
|
98
|
-
}
|
|
99
133
|
/** Package-internal budgeted observation; Fleet allocates, Akuma still selects. */
|
|
100
134
|
export async function readBudgetedStatus(worldPath, id, input) {
|
|
101
135
|
if (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0) {
|
|
@@ -104,15 +138,22 @@ export async function readBudgetedStatus(worldPath, id, input) {
|
|
|
104
138
|
const status = await bornStatus(pathsForAkuId(worldPath, id), id, ordinarySnapshotBudget(input.ordinaryBudget));
|
|
105
139
|
return { status, ordinarySelected: ordinarySelectedCount(status.timeline) };
|
|
106
140
|
}
|
|
107
|
-
|
|
108
|
-
|
|
141
|
+
/** Package-internal birth projection used by the composed Library call result. */
|
|
142
|
+
export async function readAkumaBirthCwd(worldPath, id) {
|
|
143
|
+
const soul = await readSoul(pathsForAkuId(worldPath, id));
|
|
144
|
+
if (soul === null)
|
|
145
|
+
throw new AkumaNotBornError(id);
|
|
146
|
+
return soul.cwd;
|
|
109
147
|
}
|
|
110
148
|
export class AkumaHandle {
|
|
111
149
|
id;
|
|
112
150
|
worldPath;
|
|
113
|
-
|
|
151
|
+
[CALL_EXECUTION];
|
|
152
|
+
constructor(id, worldPath, execution) {
|
|
114
153
|
this.id = id;
|
|
115
154
|
this.worldPath = worldPath;
|
|
155
|
+
if (execution !== undefined)
|
|
156
|
+
this[CALL_EXECUTION] = execution;
|
|
116
157
|
}
|
|
117
158
|
get paths() {
|
|
118
159
|
return pathsForAkuId(this.worldPath, this.id);
|
|
@@ -133,11 +174,7 @@ export class AkumaHandle {
|
|
|
133
174
|
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 5_000) {
|
|
134
175
|
throw new TypeError("Akuma history limit must be a positive safe integer no greater than 5000");
|
|
135
176
|
}
|
|
136
|
-
const slice = await activitySlice(this.paths
|
|
137
|
-
...(input.before === undefined ? {} : { before: input.before }),
|
|
138
|
-
...(input.since === undefined ? {} : { since: input.since }),
|
|
139
|
-
limit: 5_000,
|
|
140
|
-
});
|
|
177
|
+
const slice = await activitySlice(this.paths);
|
|
141
178
|
return selectHistory(projectTurns(slice.rows, {
|
|
142
179
|
lowestRetained: slice.lowestRetained,
|
|
143
180
|
highest: slice.highest,
|
|
@@ -254,6 +291,7 @@ export class AkumaHandle {
|
|
|
254
291
|
provider: source.provider,
|
|
255
292
|
options: source.options,
|
|
256
293
|
...(source.readonly === undefined ? {} : { readonly: source.readonly }),
|
|
294
|
+
allowed: source.allowed,
|
|
257
295
|
cwd: source.cwd,
|
|
258
296
|
origin: { kind: "fork", parent: this.id, at: input.at },
|
|
259
297
|
confinement: source.confinement,
|
|
@@ -264,7 +302,11 @@ export class AkumaHandle {
|
|
|
264
302
|
return { kind: "forked", child: child.id };
|
|
265
303
|
}
|
|
266
304
|
catch (error) {
|
|
267
|
-
return {
|
|
305
|
+
return {
|
|
306
|
+
kind: "upstream-forked",
|
|
307
|
+
childSession,
|
|
308
|
+
diagnostic: diagnostic(error),
|
|
309
|
+
};
|
|
268
310
|
}
|
|
269
311
|
}
|
|
270
312
|
async kill() {
|
|
@@ -309,52 +351,75 @@ export class AkumaHandle {
|
|
|
309
351
|
: { kind: "no-answer" };
|
|
310
352
|
}
|
|
311
353
|
}
|
|
354
|
+
/** Package-internal provenance retained only by the handle returned from call. */
|
|
355
|
+
export function akumaCallExecution(handle) {
|
|
356
|
+
return handle[CALL_EXECUTION];
|
|
357
|
+
}
|
|
312
358
|
export class Akuma {
|
|
313
359
|
path;
|
|
314
|
-
|
|
315
|
-
constructor(path,
|
|
360
|
+
configuration;
|
|
361
|
+
constructor(path, configuration) {
|
|
316
362
|
this.path = path;
|
|
317
|
-
this.
|
|
363
|
+
this.configuration = configuration;
|
|
318
364
|
}
|
|
319
|
-
static of(root,
|
|
365
|
+
static of(root, input = {}) {
|
|
320
366
|
if (typeof root !== "string")
|
|
321
367
|
throw new TypeError("Akuma.of root must be a WorldRoot");
|
|
322
|
-
return new Akuma(root,
|
|
323
|
-
}
|
|
324
|
-
async settings() {
|
|
325
|
-
return this.configuredSettings ?? await readSettings({ root: this.path });
|
|
368
|
+
return new Akuma(root, input);
|
|
326
369
|
}
|
|
327
370
|
of(input) {
|
|
328
371
|
return new AkumaHandle(parseAkuId(input.id).id, this.path);
|
|
329
372
|
}
|
|
330
373
|
async listArchetypes() {
|
|
331
|
-
return readArchetypes({
|
|
374
|
+
return readArchetypes(this.configuration.home === undefined ? {} : { home: this.configuration.home });
|
|
332
375
|
}
|
|
333
376
|
async call(input) {
|
|
377
|
+
return await this[CALL_WITH_CONTEXT](input, { initiatorCwd: process.cwd() });
|
|
378
|
+
}
|
|
379
|
+
async [CALL_WITH_CONTEXT](input, context) {
|
|
334
380
|
const name = archetypeName(input.archetype);
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
const
|
|
381
|
+
const home = this.configuration.home === undefined ? {} : { home: this.configuration.home };
|
|
382
|
+
const settings = this.configuration.settings ?? await readSettings({ root: this.path, ...home });
|
|
383
|
+
const archetype = await loadArchetype({ name, ...home, settings });
|
|
384
|
+
const allowed = input.allowed === undefined
|
|
385
|
+
? archetype.allowed
|
|
386
|
+
: decodeAllowedActions(input.allowed, "Akuma call allowed");
|
|
387
|
+
const requests = injectedBodyRequests();
|
|
388
|
+
const requestRecipe = Object.freeze({
|
|
339
389
|
...(archetype.description === undefined ? {} : { description: archetype.description }),
|
|
340
390
|
provider: archetype.provider,
|
|
341
391
|
options: archetype.options,
|
|
342
392
|
...(archetype.readonly === undefined ? {} : { readonly: archetype.readonly }),
|
|
343
|
-
|
|
393
|
+
allowed,
|
|
344
394
|
});
|
|
345
|
-
const requests = injectedBodyRequests();
|
|
346
395
|
if (requests !== null) {
|
|
396
|
+
const cwd = input.cwd === undefined
|
|
397
|
+
? undefined
|
|
398
|
+
: context?.cwdCanonical === true ? input.cwd : await canonicalBirthCwd(input.cwd);
|
|
347
399
|
const child = await requestBodyCall({
|
|
348
400
|
directory: requests,
|
|
349
401
|
id: randomUUID(),
|
|
350
402
|
world: this.path,
|
|
351
403
|
archetype: name,
|
|
352
404
|
body: input.body,
|
|
353
|
-
cwd,
|
|
354
|
-
recipe,
|
|
405
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
406
|
+
recipe: requestRecipe,
|
|
407
|
+
});
|
|
408
|
+
const bornCwd = await readAkumaBirthCwd(this.path, child);
|
|
409
|
+
return new AkumaHandle(child, this.path, {
|
|
410
|
+
cwd: bornCwd,
|
|
411
|
+
source: cwd === undefined ? "caller" : "input",
|
|
355
412
|
});
|
|
356
|
-
return new AkumaHandle(child, this.path);
|
|
357
413
|
}
|
|
414
|
+
const initiatorCwd = context.initiatorCwd;
|
|
415
|
+
const selectedCwd = input.cwd ?? initiatorCwd ?? this.path;
|
|
416
|
+
const cwd = input.cwd !== undefined && context?.cwdCanonical === true
|
|
417
|
+
? input.cwd
|
|
418
|
+
: await canonicalBirthCwd(selectedCwd);
|
|
419
|
+
const recipe = Object.freeze({
|
|
420
|
+
...requestRecipe,
|
|
421
|
+
confinement: archetype.adapter.confinement({ cwd, options: archetype.options }),
|
|
422
|
+
});
|
|
358
423
|
const published = await publishAkuma({
|
|
359
424
|
worldPath: this.path,
|
|
360
425
|
archetype: archetype.name,
|
|
@@ -363,18 +428,17 @@ export class Akuma {
|
|
|
363
428
|
seed: {
|
|
364
429
|
id: allocated.id,
|
|
365
430
|
archetype: allocated.archetype,
|
|
366
|
-
...
|
|
367
|
-
provider: archetype.provider,
|
|
368
|
-
options: archetype.options,
|
|
369
|
-
...(archetype.readonly === undefined ? {} : { readonly: archetype.readonly }),
|
|
431
|
+
...recipe,
|
|
370
432
|
cwd,
|
|
371
433
|
origin: { kind: "direct" },
|
|
372
|
-
confinement: recipe.confinement,
|
|
373
434
|
},
|
|
374
435
|
initialBody: input.body,
|
|
375
436
|
}),
|
|
376
437
|
});
|
|
377
|
-
return new AkumaHandle(published.id, this.path
|
|
438
|
+
return new AkumaHandle(published.id, this.path, {
|
|
439
|
+
cwd,
|
|
440
|
+
source: input.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
|
|
441
|
+
});
|
|
378
442
|
}
|
|
379
443
|
async list(input = {}) {
|
|
380
444
|
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
@@ -399,28 +463,20 @@ export class Akuma {
|
|
|
399
463
|
}
|
|
400
464
|
const rows = [];
|
|
401
465
|
for (const name of names) {
|
|
466
|
+
let physical;
|
|
402
467
|
try {
|
|
403
|
-
|
|
404
|
-
if (selected !== undefined && physical.archetype !== selected)
|
|
405
|
-
continue;
|
|
406
|
-
const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
|
|
407
|
-
const snapshot = await readHeart(paths);
|
|
408
|
-
if (snapshot.soul !== null) {
|
|
409
|
-
rows.push(await bornListRow(paths, physical.id, snapshot));
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
if (await probeLeash(paths) === "held") {
|
|
413
|
-
rows.push({ id: physical.id, life: "unborn" });
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
const seal = await readSeal(paths);
|
|
417
|
-
rows.push(seal === null
|
|
418
|
-
? { id: physical.id, life: "unborn" }
|
|
419
|
-
: { id: physical.id, life: "stillborn", seal });
|
|
468
|
+
physical = akuIdFromDirectoryName(name);
|
|
420
469
|
}
|
|
421
470
|
catch {
|
|
422
471
|
continue;
|
|
423
472
|
}
|
|
473
|
+
if (selected !== undefined && physical.archetype !== selected)
|
|
474
|
+
continue;
|
|
475
|
+
const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
|
|
476
|
+
try {
|
|
477
|
+
rows.push(await fleetListRow(paths, physical.id));
|
|
478
|
+
}
|
|
479
|
+
catch { }
|
|
424
480
|
}
|
|
425
481
|
return {
|
|
426
482
|
rows,
|
|
@@ -428,3 +484,7 @@ export class Akuma {
|
|
|
428
484
|
};
|
|
429
485
|
}
|
|
430
486
|
}
|
|
487
|
+
/** Package-internal call path for a composition owner that already canonicalized cwd. */
|
|
488
|
+
export async function callAkumaWithContext(akuma, input, context) {
|
|
489
|
+
return await akuma[CALL_WITH_CONTEXT](input, context);
|
|
490
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.deliver", "task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
|
|
2
|
+
export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
|
|
3
|
+
export type AllowedActions = readonly AllowedAction[];
|
|
4
|
+
export declare function isAllowedAction(value: unknown): value is AllowedAction;
|
|
5
|
+
export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
|
|
6
|
+
export declare function effectiveAllowedActions(value: unknown): AllowedActions;
|
|
7
|
+
export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const ALLOWED_ACTIONS = Object.freeze([
|
|
2
|
+
"akuma.call",
|
|
3
|
+
"akuma.kill",
|
|
4
|
+
"akuma.tell",
|
|
5
|
+
"contract.deliver",
|
|
6
|
+
"task.add",
|
|
7
|
+
"task.addDocument",
|
|
8
|
+
"task.compose",
|
|
9
|
+
"task.done",
|
|
10
|
+
"task.drop",
|
|
11
|
+
"task.hold",
|
|
12
|
+
"task.resume",
|
|
13
|
+
"task.start",
|
|
14
|
+
"task.stop",
|
|
15
|
+
"task.update",
|
|
16
|
+
]);
|
|
17
|
+
const ALLOWED_ACTION_SET = new Set(ALLOWED_ACTIONS);
|
|
18
|
+
export function isAllowedAction(value) {
|
|
19
|
+
return typeof value === "string" && ALLOWED_ACTION_SET.has(value);
|
|
20
|
+
}
|
|
21
|
+
export function decodeAllowedActions(value, label = "allowed") {
|
|
22
|
+
if (!Array.isArray(value))
|
|
23
|
+
throw new TypeError(`${label} must be an array`);
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (const action of value) {
|
|
26
|
+
if (!isAllowedAction(action))
|
|
27
|
+
throw new TypeError(`${label} contains an unknown action: ${String(action)}`);
|
|
28
|
+
if (seen.has(action))
|
|
29
|
+
throw new TypeError(`${label} contains a duplicate action: ${action}`);
|
|
30
|
+
seen.add(action);
|
|
31
|
+
}
|
|
32
|
+
return Object.freeze([...seen].sort());
|
|
33
|
+
}
|
|
34
|
+
export function effectiveAllowedActions(value) {
|
|
35
|
+
return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
|
|
36
|
+
}
|
|
37
|
+
export function clipAllowedActions(requested, parent) {
|
|
38
|
+
const ceiling = new Set(parent);
|
|
39
|
+
return Object.freeze(requested.filter((action) => ceiling.has(action)));
|
|
40
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Settings } from "../settings.js";
|
|
2
2
|
import type { ProviderAdapter } from "./provider.js";
|
|
3
3
|
import { type ProviderExecution, type ProviderOptions, type ReadonlyRestraint } from "./provider-recipe.js";
|
|
4
|
+
import { type AllowedActions } from "./allowed.js";
|
|
4
5
|
type DecodedArchetype = Readonly<{
|
|
5
6
|
name: string;
|
|
6
7
|
path: string;
|
|
7
8
|
provider: string;
|
|
8
9
|
description?: string;
|
|
9
10
|
options: ProviderOptions;
|
|
11
|
+
allowed: AllowedActions;
|
|
10
12
|
}>;
|
|
11
13
|
export type ArchetypeCatalogRow = Readonly<{
|
|
12
14
|
name: string;
|
|
@@ -27,14 +29,15 @@ export declare class AkumaArchetypeError extends Error {
|
|
|
27
29
|
readonly kind = "akuma-archetype";
|
|
28
30
|
constructor(archetype: string, searched: readonly string[], reason: string, guidance?: string);
|
|
29
31
|
}
|
|
30
|
-
export declare function listArchetypes(input
|
|
31
|
-
|
|
32
|
+
export declare function listArchetypes(input?: Readonly<{
|
|
33
|
+
home?: string;
|
|
32
34
|
}>): Promise<readonly string[]>;
|
|
33
|
-
export declare function listArchetypeDefinitions(input
|
|
34
|
-
|
|
35
|
+
export declare function listArchetypeDefinitions(input?: Readonly<{
|
|
36
|
+
home?: string;
|
|
35
37
|
}>): Promise<readonly ArchetypeCatalogRow[]>;
|
|
36
38
|
export declare function loadArchetype(input: Readonly<{
|
|
37
39
|
name: string;
|
|
40
|
+
home?: string;
|
|
38
41
|
settings: Settings;
|
|
39
42
|
}>): Promise<AdmittedArchetype>;
|
|
40
43
|
export {};
|