@astrosheep/keiyaku 4.6.19 → 4.6.20
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 +7 -3
- package/build/plugins/pi/README.md +111 -0
- package/build/plugins/pi/akuma-tools.ts +310 -0
- package/build/plugins/pi/keiyaku.ts +2 -0
- package/build/src/akuma/akuma-call.d.ts +4 -1
- package/build/src/akuma/akuma-call.js +24 -7
- package/build/src/akuma/akuma-instance.d.ts +28 -8
- package/build/src/akuma/akuma-instance.js +85 -42
- package/build/src/akuma/akuma-owner.d.ts +18 -9
- package/build/src/akuma/akuma-owner.js +31 -25
- package/build/src/akuma/akuma-selection-execution.d.ts +4 -1
- package/build/src/akuma/akuma-selection-execution.js +8 -4
- package/build/src/akuma/akuma.d.ts +17 -0
- package/build/src/akuma/body-launch.d.ts +107 -0
- package/build/src/akuma/body-launch.js +57 -0
- package/build/src/akuma/body-supervisor.d.ts +7 -6
- package/build/src/akuma/body-supervisor.js +17 -12
- package/build/src/akuma/body.d.ts +13 -94
- package/build/src/akuma/body.js +124 -296
- package/build/src/akuma/call-input.d.ts +2 -0
- package/build/src/akuma/call-input.js +21 -1
- package/build/src/akuma/call-request.d.ts +6 -2
- package/build/src/akuma/call-request.js +9 -1
- package/build/src/akuma/detached-body.d.ts +9 -0
- package/build/src/akuma/detached-body.js +94 -0
- package/build/src/akuma/execution-instructions.d.ts +8 -0
- package/build/src/akuma/execution-instructions.js +17 -0
- package/build/src/akuma/heart/facts.d.ts +21 -1
- package/build/src/akuma/heart/facts.js +10 -0
- package/build/src/akuma/heart/index.d.ts +16 -1
- package/build/src/akuma/heart/index.js +95 -30
- package/build/src/akuma/heart/rows.d.ts +14 -2
- package/build/src/akuma/heart/rows.js +48 -4
- package/build/src/akuma/heart/schema.d.ts +4 -1
- package/build/src/akuma/heart/schema.js +87 -30
- package/build/src/akuma/heart/soul.js +4 -0
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +59 -15
- package/build/src/akuma/heart/timeline.d.ts +6 -4
- package/build/src/akuma/heart/timeline.js +14 -4
- package/build/src/akuma/in-process-body.d.ts +5 -0
- package/build/src/akuma/in-process-body.js +57 -0
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/projection.d.ts +44 -1
- package/build/src/akuma/projection.js +13 -1
- package/build/src/akuma/provider.d.ts +4 -0
- package/build/src/akuma/providers/acp/core.js +1 -1
- package/build/src/akuma/providers/acp/index.js +3 -0
- package/build/src/akuma/providers/claude/index.js +8 -5
- package/build/src/akuma/providers/codex-app-server/configuration.d.ts +5 -0
- package/build/src/akuma/providers/codex-app-server/configuration.js +72 -0
- package/build/src/akuma/providers/codex-app-server/index.js +7 -10
- package/build/src/akuma/providers/execution-environment.d.ts +8 -2
- package/build/src/akuma/providers/execution-environment.js +10 -1
- package/build/src/akuma/providers/grok-build/index.js +23 -16
- package/build/src/akuma/providers/index.d.ts +1 -0
- package/build/src/akuma/providers/index.js +8 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +9 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +6 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +4 -3
- package/build/src/akuma/providers/paseo/index.js +8 -2
- package/build/src/akuma/providers/pi/index.js +27 -10
- package/build/src/akuma/publication.d.ts +4 -3
- package/build/src/akuma/publication.js +21 -18
- package/build/src/akuma/selection-observation.d.ts +51 -0
- package/build/src/akuma/selection-owner-port.d.ts +2 -1
- package/build/src/akuma/selection-owner-port.js +4 -3
- package/build/src/akuma/turn-drive.d.ts +4 -2
- package/build/src/akuma/turn-drive.js +58 -21
- package/build/src/akuma-body.d.ts +0 -10
- package/build/src/akuma-body.js +13 -118
- package/build/src/body-host.d.ts +28 -0
- package/build/src/body-host.js +86 -0
- package/build/src/body-requests.d.ts +13 -0
- package/build/src/body-requests.js +131 -0
- package/build/src/cli/commands/akuma-grammar.d.ts +8 -1
- package/build/src/cli/commands/akuma-grammar.js +36 -6
- package/build/src/cli/commands/akuma.js +11 -0
- package/build/src/cli/commands/report.d.ts +6 -0
- package/build/src/cli/commands/report.js +31 -0
- package/build/src/cli/parse.js +1 -0
- package/build/src/cli/render/akuma-activity.js +27 -17
- package/build/src/cli/render/terminal.d.ts +5 -0
- package/build/src/cli/render/terminal.js +7 -0
- package/build/src/cli/runtime.js +4 -1
- package/build/src/index.d.ts +3 -1
- package/build/src/index.js +1 -0
- package/build/src/library/akuma-creation.d.ts +7 -3
- package/build/src/library/akuma-creation.js +23 -11
- package/build/src/library/akumas.d.ts +4 -1
- package/build/src/library/akumas.js +13 -5
- package/build/src/library/selection.d.ts +2 -1
- package/build/src/library/selection.js +6 -3
- package/build/src/plugin/public.d.ts +11 -0
- package/build/src/plugin/runtime.js +1 -0
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/package.json +2 -2
- package/plugins/paseo/plugin/server/keiyaku.ts +1 -0
- package/plugins/paseo/plugin/server/mirror.ts +2 -2
- package/plugins/pi/README.md +111 -0
- package/plugins/pi/akuma-tools.ts +310 -0
- package/plugins/pi/keiyaku.ts +2 -0
package/README.md
CHANGED
|
@@ -212,7 +212,10 @@ lists every recognized namespace.
|
|
|
212
212
|
npm install -g @astrosheep/keiyaku
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
Pi
|
|
215
|
+
Pi's shipped extension provides native `akuma_*` tools through the public SDK,
|
|
216
|
+
plus the `/keiyaku` status overlay. The overlay still invokes `keiyaku` from
|
|
217
|
+
`PATH`; native tools do not. See [`plugins/pi/README.md`](plugins/pi/README.md)
|
|
218
|
+
for host lifetime, detached work, and request-permission scope.
|
|
216
219
|
|
|
217
220
|
Node ≥ 22.19. Product law lives in [`docs/`](docs/README.md).
|
|
218
221
|
|
|
@@ -224,8 +227,9 @@ into each host package so native installations are self-contained. The separate
|
|
|
224
227
|
`plugins/square` workspace is Keiyaku's internal Square observer, not a coding
|
|
225
228
|
harness plugin. Installation recipes and harness package metadata select the built
|
|
226
229
|
assets under `build/plugins` and `build/skills`, except the Pi package metadata,
|
|
227
|
-
which selects the
|
|
228
|
-
|
|
230
|
+
which selects the TypeScript extension and `skills/`. Build a source checkout
|
|
231
|
+
before loading that extension so its package-root SDK is available; published
|
|
232
|
+
packages already include the SDK build.
|
|
229
233
|
|
|
230
234
|
Building from source also builds the Windows launcher with Zig 0.14.1 or later. Install
|
|
231
235
|
that system tool separately and make `zig` available on `PATH`, or set
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Keiyaku in Pi
|
|
2
|
+
|
|
3
|
+
The existing Pi package entry `plugins/pi/keiyaku.ts` supplies `/keiyaku` (the
|
|
4
|
+
status overlay) and ten native Akuma tools. It imports only the supported
|
|
5
|
+
`@astrosheep/keiyaku` package root. Build the checkout before loading it locally;
|
|
6
|
+
an installed npm package already carries its built SDK.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
# Existing installation path; no separate experimental package
|
|
10
|
+
keiyaku install pi
|
|
11
|
+
# One-invocation local development, after npm run build
|
|
12
|
+
pi --extension ./plugins/pi/keiyaku.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Native tools
|
|
16
|
+
|
|
17
|
+
All tools return the SDK result in `details`, `structuredContent`, and JSON
|
|
18
|
+
text. Typed refusals and partial birth results remain data. An exceptional
|
|
19
|
+
failure retaining a born Akuma also retains its identity in structured error
|
|
20
|
+
data. No native tool shells out to the CLI.
|
|
21
|
+
|
|
22
|
+
| Tool | Parameters |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `akuma_call` | `akuma` definition name, `body`; optional `workdir`, `writableDirectories`, `detach`, `bare`, `wait`, `timeoutMs`, `schema` |
|
|
25
|
+
| `akuma_status` | `akuma`; optional `repo` |
|
|
26
|
+
| `akuma_list` | optional `archetype`, `limit` |
|
|
27
|
+
| `akuma_definitions` | optional `limit` |
|
|
28
|
+
| `akuma_tell` | `akuma`, `body`; optional `interrupt`, `detach`, `repo` |
|
|
29
|
+
| `akuma_ask` | `akuma`, `body`; optional `interrupt`, `detach`, `schema`, `timeoutMs`, `repo` |
|
|
30
|
+
| `akuma_wait` | `akuma` array; optional `completion` (`any` or `all`), `timeoutMs`, `repo` |
|
|
31
|
+
| `akuma_history` | `akuma`; optional `id`, `last`, `before`, `since`, `limit`, `repo` |
|
|
32
|
+
| `akuma_fork` | `akuma`, `at`; optional `detach`, `bare`, `repo` |
|
|
33
|
+
| `akuma_kill` | `akuma` array; optional `repo` |
|
|
34
|
+
|
|
35
|
+
`akuma_call` supports every configured provider, not only Pi. Default
|
|
36
|
+
`detach: false` launches in the calling Pi session's in-process host scope.
|
|
37
|
+
Default `wait: false` returns after birth and first-input admission; these are
|
|
38
|
+
**independent choices**. `wait: true` observes the first answer for up to five
|
|
39
|
+
minutes by default. `timeoutMs` on call requires `wait: true`.
|
|
40
|
+
Call and Fork pass `bare` through to the SDK: it omits only Keiyaku runtime
|
|
41
|
+
instructions and remains frozen through wake and inherited by forks. Providers
|
|
42
|
+
without a verified runtime-instruction channel retain their explicit refusal
|
|
43
|
+
unless the caller opts out.
|
|
44
|
+
|
|
45
|
+
`writableDirectories` grants native tools access to additional existing canonical
|
|
46
|
+
absolute directories at birth (currently Codex only). It preserves the native
|
|
47
|
+
sandbox mode, network policy, and existing writable roots. Other providers
|
|
48
|
+
refuse nonempty grants. These are explicit filesystem grants, not product-action
|
|
49
|
+
permissions; nothing automatically exposes the repository or Home. Forks
|
|
50
|
+
inherit the frozen native recipe; there is no live elevation option.
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{"akuma":"worker","body":"Inspect the parser without changing files."}
|
|
54
|
+
{"akuma":"worker","body":"Run the checks.","detach":true,"wait":true,"timeoutMs":300000}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Tell, Ask and Fork explicitly select future launch hosting through their own
|
|
58
|
+
`detach` option, also defaulting to false. A running Body never moves hosts. A
|
|
59
|
+
sleeping Akuma can be woken in the current session even if another host created
|
|
60
|
+
it. Ordinary CLI defaults and `--wait` semantics are unchanged.
|
|
61
|
+
|
|
62
|
+
World resolves from Pi's `ctx.cwd`. Relative `workdir` and `repo` paths resolve
|
|
63
|
+
there too; `repo` supplies Git association/selector context, not a different
|
|
64
|
+
World. Caller attribution comes from the current Pi session manager, not ambient
|
|
65
|
+
parent Akuma or harness environment declarations. Single targets accept IDs or
|
|
66
|
+
World-local aliases; Wait/Kill also accept globs and Contract selectors (the
|
|
67
|
+
latter need `repo`). History `id` and Fork `at` are exact returned `turn/n`
|
|
68
|
+
identities. Exact history cannot combine with paging or `last`.
|
|
69
|
+
|
|
70
|
+
Ask/Wait are unbounded unless `timeoutMs` is supplied. Deadline or tool abort
|
|
71
|
+
ends observation, not admitted work. Optional `schema` supplies a provider JSON
|
|
72
|
+
answer contract; the result is JSON-decoded, not locally validated against
|
|
73
|
+
arbitrary JSON Schema. Schema input to busy work retains the SDK refusal.
|
|
74
|
+
|
|
75
|
+
## Lifetime and permissions
|
|
76
|
+
|
|
77
|
+
The extension owns one `AkumaHost.inProcess()` scope per Pi session. Orderly
|
|
78
|
+
`session_shutdown` closes only that scope's exact in-process launches, including
|
|
79
|
+
setup and automatic succession. It never kills by AkuId, so an older session
|
|
80
|
+
cannot stop a foreign host's later wake of the same identity. Detached work
|
|
81
|
+
survives. Reload and session replacement follow Pi's shutdown/start lifecycle.
|
|
82
|
+
|
|
83
|
+
The Pi provider supplies the public SDK extension flag
|
|
84
|
+
`keiyaku-request-only: true` to its own sessions. The extension reads that flag
|
|
85
|
+
at native tool execution, before World establishment or SDK operations, and
|
|
86
|
+
refuses local tools there (including nested tool calls). Those child Akumas use
|
|
87
|
+
the existing CLI in their tool environment, with authenticated direct-parent
|
|
88
|
+
routing and frozen permissions. There is no ambient identity guess or duplicate
|
|
89
|
+
permission evaluator. If the optional extension is absent, Pi collects an
|
|
90
|
+
unknown-option diagnostic without failing provider startup.
|
|
91
|
+
|
|
92
|
+
In-process hosting is shared lifetime, not a sandbox: arbitrary extensions can
|
|
93
|
+
block or terminate the runtime. A noncooperative tool/provider can remain
|
|
94
|
+
physically live after the Body records hung and releases its leash. Scope close
|
|
95
|
+
rejects with that exact launch's retained failure; it never reports physical
|
|
96
|
+
retirement based on a later Body's status. Abrupt host death has no graceful
|
|
97
|
+
cleanup guarantee, and no daemon or persisted host policy recovers live objects.
|
|
98
|
+
Child model usage is not rolled into an already-returned parent tool result.
|
|
99
|
+
|
|
100
|
+
## Verification
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
npm run build
|
|
104
|
+
npm test -- tests/akuma-in-process.test.ts tests/pi-akuma-tools.test.ts tests/pi-extension.test.ts tests/package-consumers.test.ts
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The native-tool tests load the built package's actual extension with Pi's public
|
|
108
|
+
loader and execute a deterministic offline model through real Pi tool calls,
|
|
109
|
+
SDK operations, Heart/leash, subprocess tools and detached launches. They do not
|
|
110
|
+
replace the Body engine or depend on model credentials. These are not real-model
|
|
111
|
+
quality or OS sandbox tests.
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import {
|
|
4
|
+
AkumaHost,
|
|
5
|
+
Akumas,
|
|
6
|
+
Repo,
|
|
7
|
+
Schema,
|
|
8
|
+
World,
|
|
9
|
+
listArchetypeDefinitions,
|
|
10
|
+
type CallResult,
|
|
11
|
+
type AkumaHostScope,
|
|
12
|
+
type AkumaCaller,
|
|
13
|
+
type AkumaAddressInput,
|
|
14
|
+
type AkumaAskInput,
|
|
15
|
+
type AkumaHistoryInput,
|
|
16
|
+
type AkumaKillInput,
|
|
17
|
+
type AkumaListInput,
|
|
18
|
+
type AkumaTellInput,
|
|
19
|
+
type AkumaWaitInput,
|
|
20
|
+
type ForkInput,
|
|
21
|
+
type JsonSchemaDocument,
|
|
22
|
+
type WorldRoot,
|
|
23
|
+
} from "@astrosheep/keiyaku";
|
|
24
|
+
|
|
25
|
+
const selector = { type: "string", minLength: 1, description: "Akuma ID or World-local alias" };
|
|
26
|
+
const selectors = {
|
|
27
|
+
type: "array",
|
|
28
|
+
items: selector,
|
|
29
|
+
minItems: 1,
|
|
30
|
+
description: "IDs, aliases or glob selectors; Contract selectors require repo",
|
|
31
|
+
};
|
|
32
|
+
const repo = { type: "string", minLength: 1, description: "Optional Git worktree path, relative to session cwd" };
|
|
33
|
+
const body = { type: "string", minLength: 1, description: "Input to admit; an observation deadline never retracts it" };
|
|
34
|
+
const interrupt = {
|
|
35
|
+
type: "boolean",
|
|
36
|
+
description: "Admit input with a request for current work to yield; admission survives failed retirement",
|
|
37
|
+
};
|
|
38
|
+
const detach = {
|
|
39
|
+
type: "boolean",
|
|
40
|
+
description:
|
|
41
|
+
"Launch future work detached (default false: this Pi session's in-process scope). Never relocates an already running Body.",
|
|
42
|
+
};
|
|
43
|
+
const bare = {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
description: "Omit Keiyaku runtime instructions only; preserved through wake and inherited by forks",
|
|
46
|
+
};
|
|
47
|
+
const timeoutMs = {
|
|
48
|
+
type: "integer",
|
|
49
|
+
minimum: 0,
|
|
50
|
+
description: "Observation limit in milliseconds; omitted is unbounded",
|
|
51
|
+
};
|
|
52
|
+
const limit = { type: "integer", minimum: 1 };
|
|
53
|
+
|
|
54
|
+
type ToolAddress<Input> = Omit<Input, "repo" | "signal"> & { repo?: string };
|
|
55
|
+
type LaunchChoice = { detach?: boolean };
|
|
56
|
+
|
|
57
|
+
function caller(ctx: ExtensionContext): AkumaCaller {
|
|
58
|
+
return { kind: "external", integration: "pi", id: ctx.sessionManager.getSessionId() };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Native adaptation only. Addressing, admission, observation and lifecycle stay with Keiyaku. */
|
|
62
|
+
export function registerAkumaTools(pi: ExtensionAPI): void {
|
|
63
|
+
pi.registerFlag("keiyaku-request-only", {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
default: false,
|
|
66
|
+
description: "Disable local native Akuma tools in provider-owned Pi sessions; use the request-aware CLI",
|
|
67
|
+
});
|
|
68
|
+
type Session = { id: string; host?: AkumaHostScope; closed: boolean };
|
|
69
|
+
let current: Session | undefined;
|
|
70
|
+
const create = (ctx: ExtensionContext): Session => ({
|
|
71
|
+
id: ctx.sessionManager.getSessionId(),
|
|
72
|
+
...(pi.getFlag("keiyaku-request-only") === true ? {} : { host: AkumaHost.inProcess() }),
|
|
73
|
+
closed: false,
|
|
74
|
+
});
|
|
75
|
+
function session(ctx: ExtensionContext): Session {
|
|
76
|
+
if (current === undefined) current = create(ctx);
|
|
77
|
+
if (current.id !== ctx.sessionManager.getSessionId())
|
|
78
|
+
throw new Error("Keiyaku Pi session has changed; wait for session_start");
|
|
79
|
+
if (current.closed) throw new Error("Keiyaku Pi session is shutting down");
|
|
80
|
+
if (current.host === undefined)
|
|
81
|
+
throw new Error(
|
|
82
|
+
"Native Akuma tools require an external Pi session. Inside an Akuma use the request-aware keiyaku CLI (direct-parent permissions apply).",
|
|
83
|
+
);
|
|
84
|
+
return current;
|
|
85
|
+
}
|
|
86
|
+
pi.on("session_start", (_event, ctx) => {
|
|
87
|
+
if (current?.id === ctx.sessionManager.getSessionId() && !current.closed) return;
|
|
88
|
+
if (current !== undefined && !current.closed) throw new Error("Previous Keiyaku Pi session has not shut down");
|
|
89
|
+
current = create(ctx);
|
|
90
|
+
});
|
|
91
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
92
|
+
const owned = current;
|
|
93
|
+
if (owned === undefined || owned.id !== ctx.sessionManager.getSessionId()) return;
|
|
94
|
+
owned.closed = true;
|
|
95
|
+
await owned.host?.close();
|
|
96
|
+
});
|
|
97
|
+
function register<Input>(
|
|
98
|
+
name: string,
|
|
99
|
+
description: string,
|
|
100
|
+
properties: Record<string, unknown>,
|
|
101
|
+
required: string[],
|
|
102
|
+
run: (
|
|
103
|
+
input: Input,
|
|
104
|
+
akumas: Akumas,
|
|
105
|
+
ctx: ExtensionContext,
|
|
106
|
+
signal: AbortSignal | undefined,
|
|
107
|
+
world: WorldRoot,
|
|
108
|
+
) => Promise<object>,
|
|
109
|
+
): void {
|
|
110
|
+
pi.registerTool({
|
|
111
|
+
name,
|
|
112
|
+
label: name,
|
|
113
|
+
description,
|
|
114
|
+
parameters: { type: "object", properties, required, additionalProperties: false },
|
|
115
|
+
outputSchema: { type: "object", additionalProperties: true },
|
|
116
|
+
async execute(_id, raw, signal, _update, ctx) {
|
|
117
|
+
// SDK flag injection happens after the factory. Read here, before World
|
|
118
|
+
// establishment, including when invoked as another tool's nested call.
|
|
119
|
+
if (pi.getFlag("keiyaku-request-only") === true)
|
|
120
|
+
throw new Error(
|
|
121
|
+
"Native Akuma tools require an external Pi session. Inside an Akuma use the request-aware keiyaku CLI (direct-parent permissions apply).",
|
|
122
|
+
);
|
|
123
|
+
const owned = session(ctx);
|
|
124
|
+
signal?.throwIfAborted();
|
|
125
|
+
const world = await (await World.resolve(ctx.cwd)).establish();
|
|
126
|
+
if (owned.closed) throw new Error("Keiyaku Pi session is shutting down");
|
|
127
|
+
const { detach: detached, ...input } = raw as Input & LaunchChoice;
|
|
128
|
+
let result: object;
|
|
129
|
+
try {
|
|
130
|
+
result = await run(
|
|
131
|
+
input as Input,
|
|
132
|
+
Akumas.of(world, detached ? { hosting: "detached" } : owned.host),
|
|
133
|
+
ctx,
|
|
134
|
+
signal,
|
|
135
|
+
world,
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
// SDK exceptions can retain a leading birth. Do not turn that known
|
|
139
|
+
// identity into a text-only error or pretend the birth was rolled back.
|
|
140
|
+
if (!(error instanceof Error) || !("akuma" in error) || typeof error.akuma !== "string") throw error;
|
|
141
|
+
result = { akuma: error.akuma, failure: { diagnostic: error.message } };
|
|
142
|
+
const text = JSON.stringify(result);
|
|
143
|
+
return {
|
|
144
|
+
content: [{ type: "text", text }],
|
|
145
|
+
details: result,
|
|
146
|
+
structuredContent: JSON.parse(text),
|
|
147
|
+
isError: true,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const text = JSON.stringify(result);
|
|
151
|
+
return {
|
|
152
|
+
content: [{ type: "text", text }],
|
|
153
|
+
details: result,
|
|
154
|
+
structuredContent: JSON.parse(text),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function address<Input extends { repo?: string }>(input: Input, ctx: ExtensionContext) {
|
|
161
|
+
const { repo: path, ...rest } = input;
|
|
162
|
+
return { ...rest, ...(path === undefined ? {} : { repo: await Repo.at({ path: resolve(ctx.cwd, path) }) }) };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
register<{
|
|
166
|
+
akuma: string;
|
|
167
|
+
body: string;
|
|
168
|
+
workdir?: string;
|
|
169
|
+
writableDirectories?: readonly string[];
|
|
170
|
+
bare?: boolean;
|
|
171
|
+
wait?: boolean;
|
|
172
|
+
timeoutMs?: number;
|
|
173
|
+
schema?: JsonSchemaDocument;
|
|
174
|
+
}>(
|
|
175
|
+
"akuma_call",
|
|
176
|
+
"Call any configured Akuma provider. By default launch in this Pi session and return birth plus first-Tell receipt immediately. detach selects detached hosting; wait separately observes the first answer (default five-minute bound). Deadlines/abort stop observation, not admitted execution. Session shutdown retires only this session's in-process launches.",
|
|
177
|
+
{
|
|
178
|
+
akuma: {
|
|
179
|
+
type: "string",
|
|
180
|
+
minLength: 1,
|
|
181
|
+
pattern: "\\S",
|
|
182
|
+
description: "Callable definition name (see akuma_definitions)",
|
|
183
|
+
},
|
|
184
|
+
body,
|
|
185
|
+
workdir: { type: "string", minLength: 1, description: "Execution directory relative to Pi cwd (default Pi cwd)" },
|
|
186
|
+
detach,
|
|
187
|
+
writableDirectories: {
|
|
188
|
+
type: "array",
|
|
189
|
+
items: { type: "string", minLength: 1 },
|
|
190
|
+
description:
|
|
191
|
+
"Additional existing canonical absolute writable directories (Codex only); preserves native sandbox/network policy. Separate from product actions.",
|
|
192
|
+
},
|
|
193
|
+
bare,
|
|
194
|
+
wait: { type: "boolean", description: "Observe first answer, independently of host choice (default false)" },
|
|
195
|
+
timeoutMs,
|
|
196
|
+
schema: { type: "object", additionalProperties: true },
|
|
197
|
+
},
|
|
198
|
+
["akuma", "body"],
|
|
199
|
+
async (input, akumas, ctx, signal) => {
|
|
200
|
+
if (input.timeoutMs !== undefined && input.wait !== true) throw new TypeError("timeoutMs requires wait: true");
|
|
201
|
+
const result: CallResult = await akumas.call({
|
|
202
|
+
archetype: input.akuma,
|
|
203
|
+
body: input.body,
|
|
204
|
+
cwd: resolve(ctx.cwd, input.workdir ?? "."),
|
|
205
|
+
...(input.writableDirectories === undefined ? {} : { writableDirectories: input.writableDirectories }),
|
|
206
|
+
mode: input.wait ? "wait" : "detach",
|
|
207
|
+
caller: caller(ctx),
|
|
208
|
+
...(input.bare === undefined ? {} : { bare: input.bare }),
|
|
209
|
+
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
|
|
210
|
+
...(input.schema === undefined ? {} : { schema: Schema.json(input.schema, (value) => value) }),
|
|
211
|
+
...(signal === undefined ? {} : { signal }),
|
|
212
|
+
});
|
|
213
|
+
return result;
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
register<ToolAddress<AkumaAddressInput>>(
|
|
218
|
+
"akuma_status",
|
|
219
|
+
"Observe one Akuma's lifecycle, current work and retained outcome. No mutation.",
|
|
220
|
+
{ akuma: selector, repo },
|
|
221
|
+
["akuma"],
|
|
222
|
+
async (input, akumas, ctx) => akumas.status(await address(input, ctx)),
|
|
223
|
+
);
|
|
224
|
+
register<AkumaListInput>(
|
|
225
|
+
"akuma_list",
|
|
226
|
+
"List a bounded recent-activity roster of Akuma instances, with aliases. Use akuma_definitions for callable names.",
|
|
227
|
+
{ archetype: { type: "string", minLength: 1 }, limit },
|
|
228
|
+
[],
|
|
229
|
+
async (input, akumas) => akumas.list(input),
|
|
230
|
+
);
|
|
231
|
+
register<{ limit?: number }>(
|
|
232
|
+
"akuma_definitions",
|
|
233
|
+
"List visible callable Akuma definitions for all configured providers (not running instances).",
|
|
234
|
+
{ limit },
|
|
235
|
+
[],
|
|
236
|
+
async (input, _akumas, _ctx, _signal, world) => listArchetypeDefinitions({ project: world, ...input }),
|
|
237
|
+
);
|
|
238
|
+
register<ToolAddress<AkumaTellInput> & LaunchChoice>(
|
|
239
|
+
"akuma_tell",
|
|
240
|
+
"Admit input without awaiting its answer. detach explicitly selects future wake hosting (default this Pi session); running work stays in its current host. Optional interrupt admits first, then requests current work to yield.",
|
|
241
|
+
{ akuma: selector, body, interrupt, detach, repo },
|
|
242
|
+
["akuma", "body"],
|
|
243
|
+
async (input, akumas, ctx, signal) =>
|
|
244
|
+
akumas.tell({ ...(await address(input, ctx)), ...(signal === undefined ? {} : { signal }) }),
|
|
245
|
+
);
|
|
246
|
+
register<ToolAddress<Omit<AkumaAskInput, "schema" | "observe">> & LaunchChoice & { schema?: JsonSchemaDocument }>(
|
|
247
|
+
"akuma_ask",
|
|
248
|
+
"Admit input and observe that exact answer. timeoutMs/abort stop observation, never admitted work. detach selects future wake hosting (default this Pi session), never relocates running work. Optional JSON Schema is a provider answer contract; parsed JSON is returned without arbitrary local schema validation.",
|
|
249
|
+
{
|
|
250
|
+
akuma: selector,
|
|
251
|
+
body,
|
|
252
|
+
interrupt,
|
|
253
|
+
detach,
|
|
254
|
+
timeoutMs,
|
|
255
|
+
schema: { type: "object", additionalProperties: true },
|
|
256
|
+
repo,
|
|
257
|
+
},
|
|
258
|
+
["akuma", "body"],
|
|
259
|
+
async (input, akumas, ctx, signal) => {
|
|
260
|
+
const { schema, ...rest } = await address(input, ctx);
|
|
261
|
+
return akumas.ask({
|
|
262
|
+
...rest,
|
|
263
|
+
...(signal === undefined ? {} : { signal }),
|
|
264
|
+
...(schema === undefined ? {} : { schema: Schema.json(schema, (value) => value) }),
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
register<ToolAddress<AkumaWaitInput>>(
|
|
269
|
+
"akuma_wait",
|
|
270
|
+
"Observe existing Akuma-wide work for a frozen selection. Defaults to any; all waits for every member. Completion is not proof of success. Deadline/cancellation does not stop workers.",
|
|
271
|
+
{ akuma: selectors, completion: { type: "string", enum: ["any", "all"] }, timeoutMs, repo },
|
|
272
|
+
["akuma"],
|
|
273
|
+
async (input, akumas, ctx, signal) =>
|
|
274
|
+
akumas.wait({ ...(await address(input, ctx)), ...(signal === undefined ? {} : { signal }) }),
|
|
275
|
+
);
|
|
276
|
+
register<ToolAddress<AkumaHistoryInput>>(
|
|
277
|
+
"akuma_history",
|
|
278
|
+
"Read retained execution history, the last answer, or one exact turn/n outcome. id cannot combine with paging or last. before/since are durable sequence cursors, not timestamps.",
|
|
279
|
+
{
|
|
280
|
+
akuma: selector,
|
|
281
|
+
id: { type: "string", pattern: "^turn/[1-9][0-9]*$" },
|
|
282
|
+
before: { type: "integer", minimum: 1 },
|
|
283
|
+
since: { type: "integer", minimum: 1 },
|
|
284
|
+
limit,
|
|
285
|
+
last: { type: "boolean" },
|
|
286
|
+
repo,
|
|
287
|
+
},
|
|
288
|
+
["akuma"],
|
|
289
|
+
async (input, akumas, ctx) => akumas.history(await address(input, ctx)),
|
|
290
|
+
);
|
|
291
|
+
register<ToolAddress<ForkInput> & LaunchChoice>(
|
|
292
|
+
"akuma_fork",
|
|
293
|
+
"Fork one exact retained answered turn/n into a prompt-free Akuma. detach selects birth hosting (default this Pi session). Source is unchanged. Native refusal preserves unavailable history/provider capability; cancellation cannot undo birth.",
|
|
294
|
+
{ akuma: selector, at: { type: "string", pattern: "^turn/[1-9][0-9]*$" }, detach, bare, repo },
|
|
295
|
+
["akuma", "at"],
|
|
296
|
+
async (input, akumas, ctx) =>
|
|
297
|
+
akumas.fork({
|
|
298
|
+
...(await address(input, ctx)),
|
|
299
|
+
caller: caller(ctx),
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
302
|
+
register<ToolAddress<AkumaKillInput>>(
|
|
303
|
+
"akuma_kill",
|
|
304
|
+
"Stop current work for a frozen Akuma selection without admitting input or deleting history. Returns each owner's honest stop evidence; does not shut down this Pi host.",
|
|
305
|
+
{ akuma: selectors, repo },
|
|
306
|
+
["akuma"],
|
|
307
|
+
async (input, akumas, ctx, signal) =>
|
|
308
|
+
akumas.kill({ ...(await address(input, ctx)), ...(signal === undefined ? {} : { signal }) }),
|
|
309
|
+
);
|
|
310
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { registerAkumaTools } from "./akuma-tools.ts";
|
|
2
3
|
|
|
3
4
|
type ExecResult = Readonly<{ stdout: string; stderr: string; code: number }>;
|
|
4
5
|
|
|
@@ -37,6 +38,7 @@ function overlay(lines: readonly string[], theme: Theme, done: () => void) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export default function keiyakuExtension(pi: ExtensionAPI): void {
|
|
41
|
+
registerAkumaTools(pi);
|
|
40
42
|
pi.registerCommand("keiyaku", {
|
|
41
43
|
description: "Open the Kanshi world status",
|
|
42
44
|
handler: async (_args, ctx) => {
|
|
@@ -2,6 +2,7 @@ import { type AkumaCaller } from "./caller.js";
|
|
|
2
2
|
import type { Settings } from "../settings.js";
|
|
3
3
|
import type { WorldRoot } from "../world.js";
|
|
4
4
|
import { type AllowedAction } from "./allowed.js";
|
|
5
|
+
import type { BodyLauncher } from "./body-launch.js";
|
|
5
6
|
import { type TellResult } from "./body.js";
|
|
6
7
|
import type { CallInitialTell } from "./call-initial-tell.js";
|
|
7
8
|
import { type AllocatedAkuma } from "./identity.js";
|
|
@@ -16,6 +17,8 @@ export type AkumaCallInput = Readonly<{
|
|
|
16
17
|
body?: string;
|
|
17
18
|
cwd?: string;
|
|
18
19
|
allowed?: readonly AllowedAction[];
|
|
20
|
+
writableDirectories?: readonly string[];
|
|
21
|
+
bare?: boolean;
|
|
19
22
|
schema?: Schema<unknown>;
|
|
20
23
|
initiator?: string;
|
|
21
24
|
caller?: AkumaCaller;
|
|
@@ -51,5 +54,5 @@ type AkumaCallLaunchInput = Omit<AkumaCallInput, "body" | "schema"> & Readonly<{
|
|
|
51
54
|
* standalone birth: load the caller Archetype/Settings, freeze allowed and cwd,
|
|
52
55
|
* then invoke the P3 prepared-call executor under local or request custody.
|
|
53
56
|
*/
|
|
54
|
-
export declare function admitAkumaCall(path: WorldRoot, configuration: AkumaConfiguration, input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AdmittedAkumaCall>;
|
|
57
|
+
export declare function admitAkumaCall(path: WorldRoot, configuration: AkumaConfiguration, input: AkumaCallLaunchInput, context: AkumaCallContext, spawn: BodyLauncher): Promise<AdmittedAkumaCall>;
|
|
55
58
|
export {};
|
|
@@ -5,8 +5,8 @@ import { readAkumaBirthCwd } from "./akuma-observe.js";
|
|
|
5
5
|
import { AkumaOwner } from "./akuma-owner.js";
|
|
6
6
|
import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
|
|
7
7
|
import { AkumaArchetypeError, loadPreparedArchetype } from "./archetype.js";
|
|
8
|
-
import {
|
|
9
|
-
import { canonicalBirthCwd } from "./call-input.js";
|
|
8
|
+
import { tellWakeRuntime } from "./body.js";
|
|
9
|
+
import { bareOption, canonicalBirthCwd, writableDirectoriesOption } from "./call-input.js";
|
|
10
10
|
import { requestForwardedAkumaCall } from "./call-request.js";
|
|
11
11
|
import { validateAkumaName } from "../identity/normalize.js";
|
|
12
12
|
import { PreparedCallAdmissionError, executePreparedCall } from "./publication.js";
|
|
@@ -24,6 +24,7 @@ async function admitBodyRequest(input) {
|
|
|
24
24
|
archetype: input.name,
|
|
25
25
|
...(input.call.initialTell === undefined ? {} : { initialTell: input.call.initialTell }),
|
|
26
26
|
...(cwd === undefined ? {} : { cwd }),
|
|
27
|
+
...(input.call.writableDirectories === undefined ? {} : { writableDirectories: input.call.writableDirectories }),
|
|
27
28
|
recipe: input.recipe,
|
|
28
29
|
...(input.call.initiator === undefined ? {} : { birthInitiator: input.call.initiator }),
|
|
29
30
|
...(input.call.contractId === undefined ? {} : { contractId: input.call.contractId }),
|
|
@@ -53,6 +54,7 @@ async function admitDirect(input) {
|
|
|
53
54
|
result = await executePreparedCall({
|
|
54
55
|
archetype: input.archetype.name,
|
|
55
56
|
cwd,
|
|
57
|
+
...(input.call.writableDirectories === undefined ? {} : { writableDirectories: input.call.writableDirectories }),
|
|
56
58
|
...(input.call.caller === undefined ? {} : { caller: input.call.caller }),
|
|
57
59
|
...(input.call.initialTell === undefined ? {} : { initialTell: input.call.initialTell }),
|
|
58
60
|
...(input.call.signal === undefined ? {} : { signal: input.call.signal }),
|
|
@@ -60,14 +62,25 @@ async function admitDirect(input) {
|
|
|
60
62
|
kind: "local",
|
|
61
63
|
world: input.path,
|
|
62
64
|
recipe: input.recipe,
|
|
63
|
-
spawn: async (launch) => await
|
|
64
|
-
|
|
65
|
-
seed: launch.seed,
|
|
65
|
+
spawn: async (launch) => await input.spawn({
|
|
66
|
+
...launch,
|
|
66
67
|
...(input.call.initiator === undefined ? {} : { birthInitiator: input.call.initiator }),
|
|
67
68
|
...(input.call.contractId === undefined ? {} : { completion: { contractId: input.call.contractId } }),
|
|
68
69
|
}),
|
|
69
70
|
admitInitialTell: async ({ id, initialTell, signal }) => await new AkumaOwner(id, input.path).admitInitialTell(initialTell, {
|
|
71
|
+
runtime: tellWakeRuntime(input.spawn),
|
|
70
72
|
...(signal === undefined ? {} : { signal }),
|
|
73
|
+
...(input.call.contractId === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: {
|
|
76
|
+
runtime: {
|
|
77
|
+
spawn: async (paths) => await input.spawn({
|
|
78
|
+
paths,
|
|
79
|
+
refuseIfHeld: true,
|
|
80
|
+
completion: { contractId: input.call.contractId },
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
71
84
|
}),
|
|
72
85
|
},
|
|
73
86
|
});
|
|
@@ -97,7 +110,7 @@ async function admitDirect(input) {
|
|
|
97
110
|
* standalone birth: load the caller Archetype/Settings, freeze allowed and cwd,
|
|
98
111
|
* then invoke the P3 prepared-call executor under local or request custody.
|
|
99
112
|
*/
|
|
100
|
-
export async function admitAkumaCall(path, configuration, input, context) {
|
|
113
|
+
export async function admitAkumaCall(path, configuration, input, context, spawn) {
|
|
101
114
|
decodeAkumaCaller(input.caller);
|
|
102
115
|
const name = validateAkumaName(input.archetype);
|
|
103
116
|
const home = configuration.home === undefined ? {} : { home: configuration.home };
|
|
@@ -106,14 +119,18 @@ export async function admitAkumaCall(path, configuration, input, context) {
|
|
|
106
119
|
const allowed = input.allowed === undefined
|
|
107
120
|
? archetype.allowed
|
|
108
121
|
: unionAllowedActions(archetype.allowed, decodeAllowedActions(input.allowed, "Akuma call allowed"));
|
|
122
|
+
const writableDirectories = await writableDirectoriesOption(input.writableDirectories);
|
|
123
|
+
if (writableDirectories.length > 0 && archetype.provider.kind !== "codex-app-server")
|
|
124
|
+
throw new TypeError(`writableDirectories is unsupported by provider ${archetype.provider.kind}`);
|
|
109
125
|
const execution = executionChannel(configuration.execution);
|
|
110
126
|
const requestRecipe = Object.freeze({
|
|
111
127
|
...(archetype.description === undefined ? {} : { description: archetype.description }),
|
|
112
128
|
provider: archetype.provider,
|
|
113
129
|
options: archetype.options,
|
|
114
130
|
allowed,
|
|
131
|
+
bare: bareOption(input.bare) ?? false,
|
|
115
132
|
});
|
|
116
133
|
if (execution.kind === "body-request")
|
|
117
134
|
return await admitBodyRequest({ call: input, context, path, name, recipe: requestRecipe, execution });
|
|
118
|
-
return await admitDirect({ call: input, context, path, archetype, recipe: requestRecipe });
|
|
135
|
+
return await admitDirect({ call: input, context, path, archetype, recipe: requestRecipe, spawn });
|
|
119
136
|
}
|
|
@@ -6,6 +6,10 @@ import { type InterruptReceipt } from "./akuma-owner.js";
|
|
|
6
6
|
import { type AkumaStatus } from "./akuma.js";
|
|
7
7
|
import type { AllowedAction } from "./allowed.js";
|
|
8
8
|
import { type KillEvidence } from "./heart/index.js";
|
|
9
|
+
import type { ReportFact } from "./heart/facts.js";
|
|
10
|
+
import { type AkumaHostOptions } from "../body-host.js";
|
|
11
|
+
export type { AkumaHostOptions } from "../body-host.js";
|
|
12
|
+
import type { BodyLauncher } from "./body-launch.js";
|
|
9
13
|
import { type AkuId } from "./identity.js";
|
|
10
14
|
import type { ActivityHistory } from "./projection.js";
|
|
11
15
|
import { type Schema, type StandardSchemaV1 } from "./schema.js";
|
|
@@ -26,10 +30,6 @@ export type AkumaHistoryOptions = Readonly<{
|
|
|
26
30
|
export type AkumaSignalOptions = Readonly<{
|
|
27
31
|
signal?: AbortSignal;
|
|
28
32
|
}>;
|
|
29
|
-
/** Observes only plugin diagnostics produced in this host, never remote Body logs. */
|
|
30
|
-
export type AkumaHostOptions = Readonly<{
|
|
31
|
-
reportDiagnostic?: (message: string) => void;
|
|
32
|
-
}>;
|
|
33
33
|
/** Capture the observer capability, not the caller's mutable options object. */
|
|
34
34
|
export declare function captureAkumaHostDiagnostic(host: AkumaHostOptions | undefined): AkumaHostOptions["reportDiagnostic"];
|
|
35
35
|
export type AkumaBirthInput = Readonly<{
|
|
@@ -38,10 +38,23 @@ export type AkumaBirthInput = Readonly<{
|
|
|
38
38
|
home?: string;
|
|
39
39
|
settings?: Settings;
|
|
40
40
|
allowed?: readonly AllowedAction[];
|
|
41
|
+
writableDirectories?: readonly string[];
|
|
42
|
+
bare?: boolean;
|
|
41
43
|
caller?: AkumaCaller;
|
|
42
44
|
initiator?: string;
|
|
43
45
|
host?: AkumaHostOptions;
|
|
44
46
|
}>;
|
|
47
|
+
export type AkumaReportOptions = AkumaSignalOptions & Readonly<{
|
|
48
|
+
bodySequence: number;
|
|
49
|
+
turnSequence: number;
|
|
50
|
+
}>;
|
|
51
|
+
export type AkumaReportResult = Readonly<{
|
|
52
|
+
kind: "recorded";
|
|
53
|
+
report: ReportFact;
|
|
54
|
+
}> | Readonly<{
|
|
55
|
+
kind: "refused";
|
|
56
|
+
reason: "not-born" | "not-current-execution";
|
|
57
|
+
}>;
|
|
45
58
|
export type AkumaTellOptions = AkumaSignalOptions & Readonly<{
|
|
46
59
|
interrupt?: boolean;
|
|
47
60
|
initiator?: string;
|
|
@@ -51,22 +64,29 @@ export type AkumaAskOptions<T> = AkumaTellOptions & Readonly<{
|
|
|
51
64
|
schema?: Schema<T> | StandardSchemaV1<T>;
|
|
52
65
|
}>;
|
|
53
66
|
export type { AkumaAskResult, AkumaTellResult, InterruptReceipt, KillEvidence };
|
|
54
|
-
|
|
67
|
+
declare class AkumaHandle {
|
|
55
68
|
readonly id: AkuId;
|
|
56
69
|
private readonly root;
|
|
57
70
|
private readonly reportDiagnostic;
|
|
58
|
-
private
|
|
71
|
+
private readonly spawn;
|
|
72
|
+
constructor(id: AkuId, root: WorldRoot, reportDiagnostic: (message: string) => void, spawn: BodyLauncher);
|
|
59
73
|
private owner;
|
|
60
|
-
static birth(archetype: string, input: AkumaBirthInput): Promise<Akuma>;
|
|
61
|
-
static select(root: WorldRoot, selector: string, host?: AkumaHostOptions): Akuma;
|
|
62
74
|
tell(text: string, options?: AkumaTellOptions): Promise<AkumaTellResult>;
|
|
63
75
|
ask(text: string, options?: AkumaAskOptions<string>): Promise<AkumaAskResult<string>>;
|
|
64
76
|
ask<T>(text: string, options: AkumaAskOptions<T> & Readonly<{
|
|
65
77
|
schema: Schema<T> | StandardSchemaV1<T>;
|
|
66
78
|
}>): Promise<AkumaAskResult<T>>;
|
|
79
|
+
report(text: string, options: AkumaReportOptions): Promise<AkumaReportResult>;
|
|
67
80
|
status(): Promise<AkumaStatus>;
|
|
68
81
|
idle(options?: AkumaIdleOptions): Promise<AkumaIdleResult>;
|
|
69
82
|
history(options?: AkumaHistoryOptions): Promise<ActivityHistory>;
|
|
70
83
|
stream(options?: AkumaSignalOptions): AsyncIterable<AkumaStreamSnapshot>;
|
|
71
84
|
kill(options?: AkumaSignalOptions): Promise<KillEvidence>;
|
|
72
85
|
}
|
|
86
|
+
export declare class Akuma extends AkumaHandle {
|
|
87
|
+
private constructor();
|
|
88
|
+
static birth(archetype: string, input: AkumaBirthInput): Promise<Akuma>;
|
|
89
|
+
static select(root: WorldRoot, selector: string, host?: AkumaHostOptions): Akuma;
|
|
90
|
+
}
|
|
91
|
+
/** Internal product composition; hosting is captured independently of public operation inputs. */
|
|
92
|
+
export declare function akumaWithLauncher(root: WorldRoot, selector: string, spawn: BodyLauncher, host?: AkumaHostOptions): Akuma;
|