@astrosheep/keiyaku 4.5.15 → 4.5.16
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/skills/keiyaku/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/src/akuma/akuma-errors.d.ts +11 -0
- package/build/src/akuma/akuma-errors.js +20 -0
- package/build/src/akuma/akuma-handle.d.ts +1 -0
- package/build/src/akuma/akuma-handle.js +36 -0
- package/build/src/akuma/akuma-instance.d.ts +31 -0
- package/build/src/akuma/akuma-instance.js +168 -0
- package/build/src/akuma/akuma-probe.d.ts +3 -0
- package/build/src/akuma/akuma-probe.js +6 -0
- package/build/src/akuma/akuma-product.d.ts +1 -0
- package/build/src/akuma/akuma-product.js +2 -0
- package/build/src/akuma/akuma.d.ts +4 -0
- package/build/src/akuma/akuma.js +1 -0
- package/build/src/akuma/body.d.ts +1 -0
- package/build/src/akuma/body.js +3 -1
- package/build/src/akuma/fleet-request.d.ts +35 -2
- package/build/src/akuma/fleet-request.js +57 -3
- package/build/src/akuma/heart/index.js +1 -1
- package/build/src/akuma/heart/tells.js +2 -6
- package/build/src/akuma/index.d.ts +7 -14
- package/build/src/akuma/index.js +3 -9
- package/build/src/akuma/providers/claude/index.js +6 -1
- package/build/src/akuma/providers/pi/index.js +3 -1
- package/build/src/akuma/schema.d.ts +16 -0
- package/build/src/akuma/schema.js +85 -0
- package/build/src/akuma/turn-drive.js +1 -3
- package/build/src/akuma-body.js +6 -0
- package/build/src/cli/commands/akuma-invoke.d.ts +10 -1
- package/build/src/cli/commands/akuma-invoke.js +64 -0
- package/build/src/cli/commands/akuma.d.ts +2 -0
- package/build/src/cli/commands/akuma.js +17 -4
- package/build/src/cli/render/akuma-activity.d.ts +1 -1
- package/build/src/cli/render/akuma-tool.d.ts +1 -1
- package/build/src/cli/render/akuma.js +8 -2
- package/build/src/kanshi/read.js +1 -1
- package/build/src/kanshi/report.d.ts +1 -1
- package/build/src/library/address.js +1 -1
- package/build/src/library/akuma-creation.d.ts +2 -0
- package/build/src/library/akuma-creation.js +2 -0
- package/build/src/library/fleet.d.ts +2 -2
- package/build/src/library/fleet.js +1 -2
- package/build/src/library/keiyaku.d.ts +1 -1
- package/build/src/plugin/runtime.js +1 -4
- package/package.json +2 -2
|
@@ -33,7 +33,7 @@ keiyaku -C <repo> review [<contract>|@<contract>] --satisfied
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--readonly] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
36
|
+
keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--readonly] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
37
37
|
keiyaku -C <repo> wait <akuma-selector>... [--any | --all]
|
|
38
38
|
keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
|
|
39
39
|
```
|
|
@@ -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] [--readonly] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
|
|
16
|
+
keiyaku -C <cwd> call <akuma-name> [--alias @name] [--readonly] [--allowed <product.action>]... [--schema <file>] [--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
|
|
@@ -5,3 +5,14 @@ export declare class AkumaNotBornError extends Error {
|
|
|
5
5
|
readonly kind = "akuma-not-born";
|
|
6
6
|
constructor(id: AkuId);
|
|
7
7
|
}
|
|
8
|
+
export declare class AkumaDecodeError extends Error {
|
|
9
|
+
readonly diagnostic: string;
|
|
10
|
+
readonly answer?: string | undefined;
|
|
11
|
+
readonly kind = "akuma-decode";
|
|
12
|
+
constructor(diagnostic: string, answer?: string | undefined);
|
|
13
|
+
}
|
|
14
|
+
export declare class AkumaProviderError extends Error {
|
|
15
|
+
readonly diagnostic: string;
|
|
16
|
+
readonly kind = "akuma-provider";
|
|
17
|
+
constructor(diagnostic: string);
|
|
18
|
+
}
|
|
@@ -8,3 +8,23 @@ export class AkumaNotBornError extends Error {
|
|
|
8
8
|
this.name = "AkumaNotBornError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
export class AkumaDecodeError extends Error {
|
|
12
|
+
diagnostic;
|
|
13
|
+
answer;
|
|
14
|
+
kind = "akuma-decode";
|
|
15
|
+
constructor(diagnostic, answer) {
|
|
16
|
+
super(diagnostic);
|
|
17
|
+
this.diagnostic = diagnostic;
|
|
18
|
+
this.answer = answer;
|
|
19
|
+
this.name = "AkumaDecodeError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class AkumaProviderError extends Error {
|
|
23
|
+
diagnostic;
|
|
24
|
+
kind = "akuma-provider";
|
|
25
|
+
constructor(diagnostic) {
|
|
26
|
+
super(diagnostic);
|
|
27
|
+
this.diagnostic = diagnostic;
|
|
28
|
+
this.name = "AkumaProviderError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -25,6 +25,7 @@ export declare class AkumaHandle {
|
|
|
25
25
|
}>): Promise<AkumaStatus>;
|
|
26
26
|
tell(body: string): Promise<TellResult>;
|
|
27
27
|
interrupt(body: string): Promise<InterruptReceipt>;
|
|
28
|
+
interruptSchema(body: string, schemaJson: string): Promise<TellResult>;
|
|
28
29
|
fork(input: Readonly<{
|
|
29
30
|
at: string;
|
|
30
31
|
}>): Promise<ForkReceipt>;
|
|
@@ -196,6 +196,42 @@ export class AkumaHandle {
|
|
|
196
196
|
}
|
|
197
197
|
return { kind: "interrupted", putDown, tell: await wakeRecordedTell(this.paths, recorded.tellId) };
|
|
198
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 {
|
|
210
|
+
const settledBody = (await readHeart(this.paths)).latestBody;
|
|
211
|
+
if (settledBody?.sequence === request.body.sequence && settledBody.hung !== undefined) {
|
|
212
|
+
await leash.clearPause(this.paths);
|
|
213
|
+
throw new Error("schema interrupt found a hung Body");
|
|
214
|
+
}
|
|
215
|
+
if (settledBody?.sequence !== request.body.sequence || settledBody.end === undefined) {
|
|
216
|
+
await leash.clearPause(this.paths);
|
|
217
|
+
throw new Error("schema interrupt could not prove Body settlement");
|
|
218
|
+
}
|
|
219
|
+
const admitted = await leash.recordInterruptTell(this.paths, {
|
|
220
|
+
kind: "tell",
|
|
221
|
+
id: randomUUID(),
|
|
222
|
+
body,
|
|
223
|
+
recordedAt: new Date().toISOString(),
|
|
224
|
+
schemaJson,
|
|
225
|
+
});
|
|
226
|
+
if (admitted.kind === "not-born")
|
|
227
|
+
throw new AkumaNotBornError(this.id);
|
|
228
|
+
tellId = admitted.tell.id;
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
leash.release();
|
|
232
|
+
}
|
|
233
|
+
return await wakeRecordedTell(this.paths, tellId);
|
|
234
|
+
}
|
|
199
235
|
async fork(input) {
|
|
200
236
|
const source = await readSoul(this.paths);
|
|
201
237
|
if (source === null)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AllowedAction } from "./allowed.js";
|
|
2
|
+
import { type AkuId } from "./identity.js";
|
|
3
|
+
import { type ActivityHistory } from "./projection.js";
|
|
4
|
+
import type { Settings } from "../settings.js";
|
|
5
|
+
import type { WorldRoot } from "../world.js";
|
|
6
|
+
import type { Schema } from "./schema.js";
|
|
7
|
+
export type AkumaBirthInput = Readonly<{
|
|
8
|
+
root: WorldRoot;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
home?: string;
|
|
11
|
+
settings?: Settings;
|
|
12
|
+
readonly?: true;
|
|
13
|
+
allowed?: readonly AllowedAction[];
|
|
14
|
+
}>;
|
|
15
|
+
export type AkumaTellOptions<T> = Readonly<{
|
|
16
|
+
schema: Schema<T>;
|
|
17
|
+
interrupt?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
export declare class Akuma {
|
|
20
|
+
readonly id: AkuId;
|
|
21
|
+
private readonly root;
|
|
22
|
+
private constructor();
|
|
23
|
+
private get paths();
|
|
24
|
+
static birth(archetype: string, input: AkumaBirthInput): Promise<Akuma>;
|
|
25
|
+
static select(root: WorldRoot, selector: string): Akuma;
|
|
26
|
+
tell(text: string): Promise<string>;
|
|
27
|
+
tell<T>(text: string, options: AkumaTellOptions<T>): Promise<T>;
|
|
28
|
+
idle(): Promise<void>;
|
|
29
|
+
history(): Promise<ActivityHistory>;
|
|
30
|
+
kill(): Promise<void>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { callReadonly, canonicalBirthCwd } from "./call-input.js";
|
|
3
|
+
import { spawnAkumaBody, wakeRecordedTell } from "./body.js";
|
|
4
|
+
import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
|
|
5
|
+
import { AkumaDecodeError, AkumaNotBornError, AkumaProviderError } from "./akuma-errors.js";
|
|
6
|
+
import { AkumaHandle } from "./akuma-handle.js";
|
|
7
|
+
import { POLL_MS, defaultWaitComplete, killAkumaWithRecovery } from "./akuma.js";
|
|
8
|
+
import { bornStatus } from "./akuma-observe.js";
|
|
9
|
+
import { loadArchetype } from "./archetype.js";
|
|
10
|
+
import { activitySlice, readTell, readTurn, recordTell } from "./heart/index.js";
|
|
11
|
+
import { parseAkuId, pathsForAkuId } from "./identity.js";
|
|
12
|
+
import { birthAkuma, launchAkuma } from "./publication.js";
|
|
13
|
+
import { projectTurns, selectHistory } from "./projection.js";
|
|
14
|
+
import { settings as readSettings } from "../settings.js";
|
|
15
|
+
const HISTORY_LIMIT = 12;
|
|
16
|
+
function wait(milliseconds) {
|
|
17
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
18
|
+
}
|
|
19
|
+
async function recordPlainTell(paths, id, body, tellId) {
|
|
20
|
+
const admitted = await recordTell(paths, { kind: "tell", id: tellId, body, recordedAt: new Date().toISOString() });
|
|
21
|
+
if (admitted.kind === "not-born")
|
|
22
|
+
throw new AkumaNotBornError(id);
|
|
23
|
+
return admitted.tell.id;
|
|
24
|
+
}
|
|
25
|
+
async function recordSchemaTell(paths, id, body, tellId, options, root) {
|
|
26
|
+
if (options.interrupt === true) {
|
|
27
|
+
const interrupted = await new AkumaHandle(id, root).interruptSchema(body, options.schema.jsonText);
|
|
28
|
+
return interrupted.admission.tellId;
|
|
29
|
+
}
|
|
30
|
+
const recordedAt = new Date().toISOString();
|
|
31
|
+
const tell = {
|
|
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;
|
|
42
|
+
}
|
|
43
|
+
function outcomeError(outcome) {
|
|
44
|
+
if (outcome.kind === "invalid-output")
|
|
45
|
+
throw new AkumaDecodeError(outcome.diagnostic, outcome.answer);
|
|
46
|
+
if (outcome.kind === "failed")
|
|
47
|
+
throw new AkumaProviderError(outcome.diagnostic);
|
|
48
|
+
throw new AkumaProviderError("Akuma answered without a value");
|
|
49
|
+
}
|
|
50
|
+
async function boundOutcome(paths, tell) {
|
|
51
|
+
if (tell.binding === undefined)
|
|
52
|
+
return null;
|
|
53
|
+
const turn = await readTurn(paths, tell.binding.turnSequence);
|
|
54
|
+
return turn?.end?.outcome ?? null;
|
|
55
|
+
}
|
|
56
|
+
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
|
+
for (;;) {
|
|
61
|
+
const tell = await readTell(paths, tellId);
|
|
62
|
+
if (tell === null)
|
|
63
|
+
throw new AkumaProviderError(`recorded Tell ${tellId} is missing from Heart`);
|
|
64
|
+
const outcome = await boundOutcome(paths, tell);
|
|
65
|
+
if (outcome !== null)
|
|
66
|
+
return outcome;
|
|
67
|
+
if (tell.state === "told" && tell.binding === undefined) {
|
|
68
|
+
throw new AkumaProviderError(`recorded Tell ${tellId} reached a terminal delivery without a Turn binding`);
|
|
69
|
+
}
|
|
70
|
+
await wait(POLL_MS);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export class Akuma {
|
|
74
|
+
id;
|
|
75
|
+
root;
|
|
76
|
+
constructor(id, root) {
|
|
77
|
+
this.id = id;
|
|
78
|
+
this.root = root;
|
|
79
|
+
Object.freeze(this);
|
|
80
|
+
}
|
|
81
|
+
get paths() {
|
|
82
|
+
return pathsForAkuId(this.root, this.id);
|
|
83
|
+
}
|
|
84
|
+
static async birth(archetype, input) {
|
|
85
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
86
|
+
throw new TypeError("Akuma birth input must be an object");
|
|
87
|
+
}
|
|
88
|
+
if (typeof input.root !== "string")
|
|
89
|
+
throw new TypeError("Akuma birth root must be a WorldRoot");
|
|
90
|
+
const name = archetype;
|
|
91
|
+
const home = input.home === undefined ? {} : { home: input.home };
|
|
92
|
+
const settings = input.settings ?? (await readSettings({ root: input.root, ...home }));
|
|
93
|
+
const readonly = callReadonly(input.readonly, "Akuma birth readonly must be true");
|
|
94
|
+
const loaded = await loadArchetype({ name, project: input.root, ...home, settings, ...readonly });
|
|
95
|
+
const allowed = input.allowed === undefined
|
|
96
|
+
? loaded.allowed
|
|
97
|
+
: unionAllowedActions(loaded.allowed, decodeAllowedActions(input.allowed, "Akuma birth allowed"));
|
|
98
|
+
const cwd = input.cwd === undefined ? input.root : await canonicalBirthCwd(input.cwd);
|
|
99
|
+
const allocated = await birthAkuma({ worldPath: input.root, archetype: loaded.name });
|
|
100
|
+
await launchAkuma({
|
|
101
|
+
allocated,
|
|
102
|
+
launch: async (born) => await spawnAkumaBody({
|
|
103
|
+
paths: born.paths,
|
|
104
|
+
seed: {
|
|
105
|
+
id: born.id,
|
|
106
|
+
archetype: born.archetype,
|
|
107
|
+
...(loaded.description === undefined ? {} : { description: loaded.description }),
|
|
108
|
+
provider: loaded.provider,
|
|
109
|
+
options: loaded.options,
|
|
110
|
+
...(loaded.readonly === undefined ? {} : { readonly: loaded.readonly }),
|
|
111
|
+
allowed,
|
|
112
|
+
cwd,
|
|
113
|
+
origin: { kind: "direct" },
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
return new Akuma(allocated.id, input.root);
|
|
118
|
+
}
|
|
119
|
+
static select(root, selector) {
|
|
120
|
+
if (typeof root !== "string")
|
|
121
|
+
throw new TypeError("Akuma.select root must be a WorldRoot");
|
|
122
|
+
return new Akuma(parseAkuId(selector).id, root);
|
|
123
|
+
}
|
|
124
|
+
async tell(text, options) {
|
|
125
|
+
if (typeof text !== "string")
|
|
126
|
+
throw new TypeError("Akuma tell text must be a string");
|
|
127
|
+
const tellId = randomUUID();
|
|
128
|
+
const recorded = options === undefined
|
|
129
|
+
? await recordPlainTell(this.paths, this.id, text, tellId)
|
|
130
|
+
: await recordSchemaTell(this.paths, this.id, text, tellId, options, this.root);
|
|
131
|
+
const outcome = await awaitTellOutcome(this.paths, recorded);
|
|
132
|
+
if (outcome.kind !== "answered")
|
|
133
|
+
outcomeError(outcome);
|
|
134
|
+
if (options === undefined)
|
|
135
|
+
return outcome.answer;
|
|
136
|
+
const raw = outcome.answerJson ?? outcome.answer;
|
|
137
|
+
let parsed;
|
|
138
|
+
try {
|
|
139
|
+
parsed = JSON.parse(raw);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
throw new AkumaDecodeError(error instanceof Error ? error.message : "Answer is not valid JSON", outcome.answer);
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
return options.schema.parse(parsed);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new AkumaDecodeError(error instanceof Error ? error.message : "Answer failed schema decode", outcome.answer);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async idle() {
|
|
152
|
+
for (;;) {
|
|
153
|
+
const observed = await bornStatus(this.paths, this.id, { aperture: "monitoring" });
|
|
154
|
+
if (defaultWaitComplete(observed.status))
|
|
155
|
+
return;
|
|
156
|
+
await wait(POLL_MS);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async history() {
|
|
160
|
+
const slice = await activitySlice(this.paths);
|
|
161
|
+
return selectHistory(projectTurns(slice.rows, { lowestRetained: slice.lowestRetained, highest: slice.highest }), {
|
|
162
|
+
limit: HISTORY_LIMIT,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async kill() {
|
|
166
|
+
await killAkumaWithRecovery(this.paths);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -185,6 +185,7 @@ export class Akuma {
|
|
|
185
185
|
origin: { kind: "direct" },
|
|
186
186
|
},
|
|
187
187
|
initialBody: input.body,
|
|
188
|
+
...(input.schema === undefined ? {} : { initialSchemaJson: input.schema.jsonText }),
|
|
188
189
|
execution: {
|
|
189
190
|
cwd,
|
|
190
191
|
source: input.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
|
|
@@ -201,6 +202,7 @@ export class Akuma {
|
|
|
201
202
|
paths: allocated.paths,
|
|
202
203
|
seed: born.seed,
|
|
203
204
|
initialBody: born.initialBody,
|
|
205
|
+
...(born.initialSchemaJson === undefined ? {} : { initialSchemaJson: born.initialSchemaJson }),
|
|
204
206
|
...(Object.keys(completion).length === 0 ? {} : { completion }),
|
|
205
207
|
}),
|
|
206
208
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type TellResult, type TellWakeRuntime } from "./body.js";
|
|
2
2
|
import { HeldAkumaLeash, type AkumaLife, type KillEvidence, type ResumeCoordinate } from "./heart/index.js";
|
|
3
|
+
export type { KillEvidence };
|
|
3
4
|
import { type AkuId, type AkumaPaths } from "./identity.js";
|
|
4
5
|
import type { Settings } from "../settings.js";
|
|
5
6
|
import type { WorldRoot } from "../world.js";
|
|
@@ -7,6 +8,7 @@ import type { AllowedAction } from "./allowed.js";
|
|
|
7
8
|
import type { ExecutionContext } from "./requests.js";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
import type { BoundedList } from "../bounded-list.js";
|
|
11
|
+
import type { Schema } from "./schema.js";
|
|
10
12
|
export declare const POLL_MS = 100;
|
|
11
13
|
export type AkumaListRow = Readonly<{
|
|
12
14
|
id: AkuId;
|
|
@@ -436,6 +438,7 @@ export type AkumaCallInput = Readonly<{
|
|
|
436
438
|
cwd?: string;
|
|
437
439
|
readonly?: true;
|
|
438
440
|
allowed?: readonly AllowedAction[];
|
|
441
|
+
schema?: Schema<unknown>;
|
|
439
442
|
}>;
|
|
440
443
|
export type AkumaCallContext = Readonly<{
|
|
441
444
|
initiatorCwd?: string;
|
|
@@ -492,3 +495,4 @@ export type AkumaConfiguration = Readonly<{
|
|
|
492
495
|
}>;
|
|
493
496
|
export { AkumaHandle, akumaCallExecution, type LastAnswer } from "./akuma-handle.js";
|
|
494
497
|
export { Akuma } from "./akuma-product.js";
|
|
498
|
+
export { AkumaBusyError, AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
package/build/src/akuma/akuma.js
CHANGED
|
@@ -107,3 +107,4 @@ export async function killAkumaWithRecovery(paths, recover = handoffPendingTells
|
|
|
107
107
|
export { bornStatus, fleetListRow, readAkumaBirthCwd, readBudgetedStatus, } from "./akuma-observe.js";
|
|
108
108
|
export { AkumaHandle, akumaCallExecution } from "./akuma-handle.js";
|
|
109
109
|
export { Akuma } from "./akuma-product.js";
|
|
110
|
+
export { AkumaBusyError, AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
package/build/src/akuma/body.js
CHANGED
|
@@ -250,7 +250,9 @@ async function runBodyTurns(input) {
|
|
|
250
250
|
...(initial === undefined ? {} : { call: initial }),
|
|
251
251
|
launchTells,
|
|
252
252
|
...(launchTells.find((tell) => tell.schemaJson !== undefined)?.schemaJson === undefined
|
|
253
|
-
?
|
|
253
|
+
? launch.initialSchemaJson === undefined
|
|
254
|
+
? {}
|
|
255
|
+
: { schemaJson: launch.initialSchemaJson }
|
|
254
256
|
: { schemaJson: launchTells.find((tell) => tell.schemaJson !== undefined).schemaJson }),
|
|
255
257
|
world: runtime.world,
|
|
256
258
|
externalCommands: runtime.externalCommands,
|
|
@@ -3,6 +3,7 @@ import { type ErasedRequestCommand, type RequestProtocol, type ServiceRequestCom
|
|
|
3
3
|
import type { AkumaStatus } from "./akuma.js";
|
|
4
4
|
import { type AkumaKillResult, type AkumaTellResult, type AkumaWaitResult } from "./fleet-observation.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import type { Schema } from "./schema.js";
|
|
6
7
|
declare const waitRequestSchema: z.ZodPipe<z.ZodObject<{
|
|
7
8
|
targets: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<import("./identity.js").AkuId, string>>>;
|
|
8
9
|
completion: z.ZodEnum<{
|
|
@@ -31,6 +32,23 @@ declare const tellRequestSchema: z.ZodPipe<z.ZodObject<{
|
|
|
31
32
|
target: import("./identity.js").AkuId;
|
|
32
33
|
body: string;
|
|
33
34
|
}>>;
|
|
35
|
+
declare const tellAnswerRequestSchema: z.ZodPipe<z.ZodObject<{
|
|
36
|
+
target: z.ZodPipe<z.ZodString, z.ZodTransform<import("./identity.js").AkuId, string>>;
|
|
37
|
+
body: z.ZodString;
|
|
38
|
+
schemaJson: z.ZodString;
|
|
39
|
+
interrupt: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
41
|
+
target: import("./identity.js").AkuId;
|
|
42
|
+
body: string;
|
|
43
|
+
schemaJson: string;
|
|
44
|
+
interrupt?: boolean | undefined;
|
|
45
|
+
action: "akuma.tell-answer";
|
|
46
|
+
}, {
|
|
47
|
+
target: import("./identity.js").AkuId;
|
|
48
|
+
body: string;
|
|
49
|
+
schemaJson: string;
|
|
50
|
+
interrupt?: boolean | undefined;
|
|
51
|
+
}>>;
|
|
34
52
|
declare const killRequestSchema: z.ZodPipe<z.ZodObject<{
|
|
35
53
|
targets: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<import("./identity.js").AkuId, string>>>;
|
|
36
54
|
}, z.core.$strict>, z.ZodTransform<{
|
|
@@ -63,7 +81,7 @@ declare const killServiceSchema: z.ZodObject<{
|
|
|
63
81
|
}, z.core.$strict>;
|
|
64
82
|
export type FleetRequest = (Omit<z.infer<typeof waitRequestSchema>, "targets"> & Readonly<{
|
|
65
83
|
targets: readonly AkumaStatus["id"][];
|
|
66
|
-
}>) | z.infer<typeof tellRequestSchema> | (Omit<z.infer<typeof killRequestSchema>, "targets"> & Readonly<{
|
|
84
|
+
}>) | z.infer<typeof tellRequestSchema> | z.infer<typeof tellAnswerRequestSchema> | (Omit<z.infer<typeof killRequestSchema>, "targets"> & Readonly<{
|
|
67
85
|
targets: readonly AkumaStatus["id"][];
|
|
68
86
|
}>);
|
|
69
87
|
export type FleetService = z.infer<typeof waitServiceSchema> | z.infer<typeof tellServiceSchema> | (Omit<z.infer<typeof killServiceSchema>, "results"> & Readonly<{
|
|
@@ -86,6 +104,13 @@ export type FleetRequestPort = Readonly<{
|
|
|
86
104
|
recordedAt: string;
|
|
87
105
|
signal: AbortSignal;
|
|
88
106
|
}>): Promise<AkumaTellResult>;
|
|
107
|
+
tellAnswer?(input: Readonly<{
|
|
108
|
+
target: AkumaStatus["id"];
|
|
109
|
+
body: string;
|
|
110
|
+
schemaJson: string;
|
|
111
|
+
interrupt?: boolean;
|
|
112
|
+
signal: AbortSignal;
|
|
113
|
+
}>): Promise<unknown>;
|
|
89
114
|
kill(input: Readonly<{
|
|
90
115
|
targets: readonly AkumaStatus["id"][];
|
|
91
116
|
signal: AbortSignal;
|
|
@@ -100,7 +125,15 @@ export type FleetRequestPort = Readonly<{
|
|
|
100
125
|
/** Akuma owns Body Request payload, live result, and durable service codecs for wait/tell/kill. */
|
|
101
126
|
export declare function fleetRequestProtocol(action: FleetRequest["action"]): RequestProtocol<FleetRequest, unknown, FleetService>;
|
|
102
127
|
export declare function fleetRequestCommand(action: FleetRequest["action"], port: FleetRequestPort): ServiceRequestCommand<FleetRequest, unknown, FleetService, FleetService>;
|
|
103
|
-
export declare function fleetRequestCommands(port: FleetRequestPort): Readonly<Record<"akuma.wait" | "akuma.tell" | "akuma.kill", ErasedRequestCommand>>;
|
|
128
|
+
export declare function fleetRequestCommands(port: FleetRequestPort): Readonly<Record<"akuma.wait" | "akuma.tell" | "akuma.tell-answer" | "akuma.kill", ErasedRequestCommand>>;
|
|
129
|
+
export declare function requestForwardedFleetTellAnswer(input: Readonly<{
|
|
130
|
+
directory: string;
|
|
131
|
+
target: AkumaStatus["id"];
|
|
132
|
+
body: string;
|
|
133
|
+
schema: Schema<unknown>;
|
|
134
|
+
interrupt?: boolean;
|
|
135
|
+
signal?: AbortSignal;
|
|
136
|
+
}>): Promise<unknown>;
|
|
104
137
|
export declare function requestForwardedFleetWait(input: Readonly<{
|
|
105
138
|
directory: string;
|
|
106
139
|
targets: readonly AkumaStatus["id"][];
|
|
@@ -27,6 +27,15 @@ const tellRequestSchema = z
|
|
|
27
27
|
.object({ target: akumaIdSchema, body: z.string() })
|
|
28
28
|
.strict()
|
|
29
29
|
.transform((request) => ({ action: "akuma.tell", ...request }));
|
|
30
|
+
const tellAnswerRequestSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
target: akumaIdSchema,
|
|
33
|
+
body: z.string(),
|
|
34
|
+
schemaJson: nonblankTextSchema,
|
|
35
|
+
interrupt: z.boolean().optional(),
|
|
36
|
+
})
|
|
37
|
+
.strict()
|
|
38
|
+
.transform((request) => ({ action: "akuma.tell-answer", ...request }));
|
|
30
39
|
const killRequestSchema = z
|
|
31
40
|
.object({ targets: fleetTargetsSchema })
|
|
32
41
|
.strict()
|
|
@@ -42,18 +51,30 @@ const killServiceSchema = z
|
|
|
42
51
|
})
|
|
43
52
|
.strict();
|
|
44
53
|
function decodeFleetRequest(action, value) {
|
|
45
|
-
const schema = action === "akuma.wait"
|
|
54
|
+
const schema = action === "akuma.wait"
|
|
55
|
+
? waitRequestSchema
|
|
56
|
+
: action === "akuma.tell"
|
|
57
|
+
? tellRequestSchema
|
|
58
|
+
: action === "akuma.tell-answer"
|
|
59
|
+
? tellAnswerRequestSchema
|
|
60
|
+
: killRequestSchema;
|
|
46
61
|
const parsed = schema.safeParse(value);
|
|
47
62
|
return parsed.success ? parsed.data : null;
|
|
48
63
|
}
|
|
49
64
|
function decodeFleetService(action, value) {
|
|
50
|
-
const schema = action === "akuma.wait"
|
|
65
|
+
const schema = action === "akuma.wait"
|
|
66
|
+
? waitServiceSchema
|
|
67
|
+
: action === "akuma.tell" || action === "akuma.tell-answer"
|
|
68
|
+
? tellServiceSchema
|
|
69
|
+
: killServiceSchema;
|
|
51
70
|
const parsed = schema.safeParse(value);
|
|
52
71
|
if (!parsed.success)
|
|
53
72
|
throw new Error("malformed stored Fleet service evidence");
|
|
54
73
|
return parsed.data;
|
|
55
74
|
}
|
|
56
75
|
function decodedFleetResult(action, value) {
|
|
76
|
+
if (action === "akuma.tell-answer")
|
|
77
|
+
return value;
|
|
57
78
|
const schema = action === "akuma.wait"
|
|
58
79
|
? fleetResultSchemas.wait
|
|
59
80
|
: action === "akuma.tell"
|
|
@@ -76,7 +97,8 @@ export function fleetRequestProtocol(action) {
|
|
|
76
97
|
encodeResult: (result) => result,
|
|
77
98
|
decodeResult: (result) => decodedFleetResult(action, result),
|
|
78
99
|
decodeReference: (reference) => decodeFleetService(action, reference),
|
|
79
|
-
isPermitted: (allowed) => action === "akuma.wait" ||
|
|
100
|
+
isPermitted: (allowed) => action === "akuma.wait" ||
|
|
101
|
+
((action === "akuma.tell" || action === "akuma.tell-answer") && allowed.includes("akuma.tell")),
|
|
80
102
|
};
|
|
81
103
|
}
|
|
82
104
|
export function fleetRequestCommand(action, port) {
|
|
@@ -105,6 +127,20 @@ export function fleetRequestCommand(action, port) {
|
|
|
105
127
|
service: { action: request.action, target: request.target, tellId: facts.id },
|
|
106
128
|
};
|
|
107
129
|
}
|
|
130
|
+
if (request.action === "akuma.tell-answer") {
|
|
131
|
+
if (port.tellAnswer === undefined)
|
|
132
|
+
throw new Error("schema answer Fleet port is unavailable");
|
|
133
|
+
return {
|
|
134
|
+
result: await port.tellAnswer({
|
|
135
|
+
target: request.target,
|
|
136
|
+
body: request.body,
|
|
137
|
+
schemaJson: request.schemaJson,
|
|
138
|
+
...(request.interrupt === undefined ? {} : { interrupt: request.interrupt }),
|
|
139
|
+
signal: facts.signal,
|
|
140
|
+
}),
|
|
141
|
+
service: { action: "akuma.tell", target: request.target, tellId: facts.id },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
108
144
|
const result = await port.kill({ targets: request.targets, signal: facts.signal });
|
|
109
145
|
if ("result" in result)
|
|
110
146
|
return { result: result.result, service: { action: request.action, results: result.service } };
|
|
@@ -119,9 +155,27 @@ export function fleetRequestCommands(port) {
|
|
|
119
155
|
return {
|
|
120
156
|
"akuma.wait": eraseRequestCommand(fleetRequestCommand("akuma.wait", port)),
|
|
121
157
|
"akuma.tell": eraseRequestCommand(fleetRequestCommand("akuma.tell", port)),
|
|
158
|
+
"akuma.tell-answer": eraseRequestCommand(fleetRequestCommand("akuma.tell-answer", port)),
|
|
122
159
|
"akuma.kill": eraseRequestCommand(fleetRequestCommand("akuma.kill", port)),
|
|
123
160
|
};
|
|
124
161
|
}
|
|
162
|
+
export async function requestForwardedFleetTellAnswer(input) {
|
|
163
|
+
const response = await requestBodyCommand({
|
|
164
|
+
directory: input.directory,
|
|
165
|
+
command: fleetRequestProtocol("akuma.tell-answer"),
|
|
166
|
+
value: {
|
|
167
|
+
action: "akuma.tell-answer",
|
|
168
|
+
target: input.target,
|
|
169
|
+
body: input.body,
|
|
170
|
+
schemaJson: input.schema.jsonText,
|
|
171
|
+
...(input.interrupt === undefined ? {} : { interrupt: input.interrupt }),
|
|
172
|
+
},
|
|
173
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
174
|
+
});
|
|
175
|
+
if (response.kind !== "returned")
|
|
176
|
+
throw new Error("Akuma body request terminal schema answer cannot reproduce an expired live result");
|
|
177
|
+
return response.result;
|
|
178
|
+
}
|
|
125
179
|
function forwardedFleetCommandResult(response, action) {
|
|
126
180
|
if (response.kind === "returned") {
|
|
127
181
|
if (action === "akuma.wait" && isWaitResult(response.result))
|
|
@@ -54,7 +54,7 @@ export async function activitySlice(paths) {
|
|
|
54
54
|
return await withHeart(paths, (heart) => readTransaction(heart, () => activityFactSlice(heart)));
|
|
55
55
|
}
|
|
56
56
|
function sameTellInput(existing, tell) {
|
|
57
|
-
return existing.body === tell.body && existing.recordedAt === tell.recordedAt && existing.schemaJson === tell.schemaJson;
|
|
57
|
+
return (existing.body === tell.body && existing.recordedAt === tell.recordedAt && existing.schemaJson === tell.schemaJson);
|
|
58
58
|
}
|
|
59
59
|
export async function recordTell(paths, tell, options = {}) {
|
|
60
60
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
@@ -25,9 +25,7 @@ function tellDeliveries(database, id) {
|
|
|
25
25
|
: { turnSequence: row.turn_sequence, route: row.route, receipt: row.receipt, deliveredAt: row.delivered_at });
|
|
26
26
|
}
|
|
27
27
|
function tellBinding(database, id) {
|
|
28
|
-
const row = database
|
|
29
|
-
.prepare("SELECT turn_sequence, bound_at FROM tell_bindings WHERE tell_id = ?")
|
|
30
|
-
.get(id);
|
|
28
|
+
const row = database.prepare("SELECT turn_sequence, bound_at FROM tell_bindings WHERE tell_id = ?").get(id);
|
|
31
29
|
return row === undefined ? undefined : { turnSequence: row.turn_sequence, boundAt: row.bound_at };
|
|
32
30
|
}
|
|
33
31
|
function decodeTellRow(database, row) {
|
|
@@ -195,9 +193,7 @@ export function insertTellBindingFact(database, input) {
|
|
|
195
193
|
const result = database
|
|
196
194
|
.prepare("INSERT OR IGNORE INTO tell_bindings(tell_id, turn_sequence, bound_at) VALUES (?, ?, ?)")
|
|
197
195
|
.run(input.tellId, input.turnSequence, input.boundAt);
|
|
198
|
-
const row = database
|
|
199
|
-
.prepare("SELECT turn_sequence FROM tell_bindings WHERE tell_id = ?")
|
|
200
|
-
.get(input.tellId);
|
|
196
|
+
const row = database.prepare("SELECT turn_sequence FROM tell_bindings WHERE tell_id = ?").get(input.tellId);
|
|
201
197
|
if (row === undefined || (result.changes === 0 && row.turn_sequence !== input.turnSequence)) {
|
|
202
198
|
throw new Error(`tell ${input.tellId} is already bound to a different Turn`);
|
|
203
199
|
}
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
1
|
+
export { Akuma } from "./akuma-instance.js";
|
|
2
|
+
export type { AkumaBirthInput, AkumaTellOptions } from "./akuma-instance.js";
|
|
3
|
+
export { Schema, JsonSchema } from "./schema.js";
|
|
4
|
+
export type { JsonSchemaDocument } from "./schema.js";
|
|
5
|
+
export type { AkuId } from "./identity.js";
|
|
6
|
+
export type { ActivityHistory, ActivityRow } from "./projection.js";
|
|
5
7
|
export { ALLOWED_ACTIONS } from "./allowed.js";
|
|
6
8
|
export type { AllowedAction, AllowedActions } from "./allowed.js";
|
|
7
9
|
export type { WorldRoot } from "../world.js";
|
|
8
|
-
export
|
|
9
|
-
export { AkumaArchetypeError, listArchetypeDefinitions, listArchetypes } from "./archetype.js";
|
|
10
|
-
export type { ArchetypeCatalogRow } from "./archetype.js";
|
|
11
|
-
export { AkumaBodyRequestError } from "./requests.js";
|
|
12
|
-
export type { AkumaList, AkumaListInput, AkumaListRow, AkumaStatus, ActivityHistory, ExactHistory, ActivityRow, ActivitySnapshot, ActivitySnapshotEntry, ForkReceipt, InterruptReceipt, TellResult, TellWake, UnbornAkumaListRow, } from "./akuma.js";
|
|
13
|
-
export type { ActiveToolRow, ClosedTurn, ClosedTurnRow, CompletedToolRow, HistoryCursor, HistoryPage, IdleSnapshotRow, OpenSnapshotRow, OpenTurn, OpenTurnRow, OutcomeRow, ReportedFileChange, RetainedWindow, Snapshot, SnapshotRow, TellRow, TurnLedger, TurnOutcome, TurnStartRow, UnsettledToolRow, } from "./akuma.js";
|
|
14
|
-
export type { AgentEvent, ToolCall, ToolEvent, ToolResult } from "./provider.js";
|
|
15
|
-
export type { AkuId } from "./identity.js";
|
|
16
|
-
export type { KillEvidence, ResumeCoordinate, TurnFact } from "./heart/index.js";
|
|
17
|
-
export type { ProviderOptions, ReadonlyRestraint } from "./provider-recipe.js";
|
|
10
|
+
export { AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError } from "./akuma-errors.js";
|
package/build/src/akuma/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export { Akuma, AkumaHandle, AkumaNotBornError, akumaIdSchema, akumaStatusSchema, defaultWaitComplete, parseAkumaStatus, } from "./akuma.js";
|
|
1
|
+
export { Akuma } from "./akuma-instance.js";
|
|
2
|
+
export { Schema, JsonSchema } from "./schema.js";
|
|
4
3
|
export { ALLOWED_ACTIONS } from "./allowed.js";
|
|
5
|
-
export
|
|
6
|
-
const soul = await readSoul(pathsForAkuId(worldPath, id));
|
|
7
|
-
return soul !== null;
|
|
8
|
-
}
|
|
9
|
-
export { AkumaArchetypeError, listArchetypeDefinitions, listArchetypes } from "./archetype.js";
|
|
10
|
-
export { AkumaBodyRequestError } from "./requests.js";
|
|
4
|
+
export { AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError } from "./akuma-errors.js";
|
|
@@ -88,7 +88,12 @@ function claudeQueryOptions(input, execution, abortController) {
|
|
|
88
88
|
...(input.session.kind === "fresh" ? {} : { resume: claudeSessionId(input.session.coordinate) }),
|
|
89
89
|
...(input.schemaJson === undefined
|
|
90
90
|
? {}
|
|
91
|
-
: {
|
|
91
|
+
: {
|
|
92
|
+
outputFormat: {
|
|
93
|
+
type: "json_schema",
|
|
94
|
+
schema: JSON.parse(input.schemaJson),
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
92
97
|
};
|
|
93
98
|
}
|
|
94
99
|
async function forkClaude(load, execution, input) {
|
|
@@ -79,7 +79,9 @@ async function createPiSession(sdk, execution, input) {
|
|
|
79
79
|
}
|
|
80
80
|
async function runPiPrompt(native, input, events, state, settle) {
|
|
81
81
|
try {
|
|
82
|
-
const prompt = [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
82
|
+
const prompt = [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
83
|
+
.filter((part) => part.length > 0)
|
|
84
|
+
.join("\n\n");
|
|
83
85
|
const schemaPrompt = input.schemaJson === undefined
|
|
84
86
|
? prompt
|
|
85
87
|
: [prompt, "Respond with JSON matching this JSON Schema and no other text:", input.schemaJson]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ZodType } from "zod";
|
|
2
|
+
export type JsonSchemaDocument = Readonly<{
|
|
3
|
+
readonly [key: string]: unknown;
|
|
4
|
+
}>;
|
|
5
|
+
export declare class Schema<T> {
|
|
6
|
+
readonly jsonSchema: JsonSchemaDocument;
|
|
7
|
+
readonly jsonText: string;
|
|
8
|
+
readonly decode: (value: unknown) => T;
|
|
9
|
+
private constructor();
|
|
10
|
+
get json(): JsonSchemaDocument;
|
|
11
|
+
static zod<Output>(schema: ZodType<Output>): Schema<Output>;
|
|
12
|
+
static json(schema: unknown): Schema<unknown>;
|
|
13
|
+
static json<Output>(schema: unknown, decode: (value: unknown) => Output): Schema<Output>;
|
|
14
|
+
parse(value: unknown): T;
|
|
15
|
+
}
|
|
16
|
+
export declare function JsonSchema(schema: unknown): Schema<unknown>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { fromJSONSchema, toJSONSchema } from "zod";
|
|
2
|
+
const SCHEMA_JSON_MAX_BYTES = 65_536;
|
|
3
|
+
function isPlainObject(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function sortValue(value) {
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map(sortValue);
|
|
9
|
+
if (!isPlainObject(value))
|
|
10
|
+
return value;
|
|
11
|
+
const sorted = {};
|
|
12
|
+
for (const key of Object.keys(value).sort()) {
|
|
13
|
+
if (key === "~standard")
|
|
14
|
+
continue;
|
|
15
|
+
sorted[key] = sortValue(value[key]);
|
|
16
|
+
}
|
|
17
|
+
return sorted;
|
|
18
|
+
}
|
|
19
|
+
function freezeValue(value) {
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
for (const entry of value)
|
|
22
|
+
freezeValue(entry);
|
|
23
|
+
return Object.freeze(value);
|
|
24
|
+
}
|
|
25
|
+
if (!isPlainObject(value))
|
|
26
|
+
return value;
|
|
27
|
+
for (const key of Object.keys(value))
|
|
28
|
+
freezeValue(value[key]);
|
|
29
|
+
return Object.freeze(value);
|
|
30
|
+
}
|
|
31
|
+
function canonicalDocument(value, label) {
|
|
32
|
+
const sorted = sortValue(value);
|
|
33
|
+
if (!isPlainObject(sorted))
|
|
34
|
+
throw new TypeError(`${label} must be a JSON object`);
|
|
35
|
+
const jsonText = JSON.stringify(sorted);
|
|
36
|
+
if (new TextEncoder().encode(jsonText).byteLength > SCHEMA_JSON_MAX_BYTES) {
|
|
37
|
+
throw new TypeError(`${label} exceeds the ${SCHEMA_JSON_MAX_BYTES}-byte bound`);
|
|
38
|
+
}
|
|
39
|
+
return { json: freezeValue(JSON.parse(jsonText)), jsonText };
|
|
40
|
+
}
|
|
41
|
+
function decodeJsonDocument(schema) {
|
|
42
|
+
if (typeof schema === "string") {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(schema);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new TypeError(error instanceof Error ? error.message : "JSON Schema is not valid JSON");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (isPlainObject(schema) || Array.isArray(schema))
|
|
51
|
+
return schema;
|
|
52
|
+
throw new TypeError("JSON Schema must be a JSON object or JSON text");
|
|
53
|
+
}
|
|
54
|
+
export class Schema {
|
|
55
|
+
jsonSchema;
|
|
56
|
+
jsonText;
|
|
57
|
+
decode;
|
|
58
|
+
constructor(jsonSchema, jsonText, decode) {
|
|
59
|
+
this.jsonSchema = jsonSchema;
|
|
60
|
+
this.jsonText = jsonText;
|
|
61
|
+
this.decode = decode;
|
|
62
|
+
Object.freeze(this);
|
|
63
|
+
}
|
|
64
|
+
get json() {
|
|
65
|
+
return this.jsonSchema;
|
|
66
|
+
}
|
|
67
|
+
static zod(schema) {
|
|
68
|
+
const payload = toJSONSchema(schema, { target: "draft-07", unrepresentable: "throw", cycles: "throw" });
|
|
69
|
+
const canonical = canonicalDocument(payload, "Zod JSON Schema");
|
|
70
|
+
return new Schema(canonical.json, canonical.jsonText, (value) => schema.parse(value));
|
|
71
|
+
}
|
|
72
|
+
static json(schema, decode) {
|
|
73
|
+
const document = decodeJsonDocument(schema);
|
|
74
|
+
const canonical = canonicalDocument(document, "JSON Schema");
|
|
75
|
+
const decoder = decode ??
|
|
76
|
+
((value) => fromJSONSchema(JSON.parse(canonical.jsonText)).parse(value));
|
|
77
|
+
return new Schema(canonical.json, canonical.jsonText, decoder);
|
|
78
|
+
}
|
|
79
|
+
parse(value) {
|
|
80
|
+
return this.decode(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function JsonSchema(schema) {
|
|
84
|
+
return Schema.json(schema);
|
|
85
|
+
}
|
|
@@ -275,9 +275,7 @@ async function stopActiveDrive(input, active) {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
function hasUnattemptedTell(liveTells, tellPump, pending, attempted) {
|
|
278
|
-
return (liveTells &&
|
|
279
|
-
tellPump === null &&
|
|
280
|
-
pending.some((tell) => !attempted.has(tell.id) && tell.schemaJson === undefined));
|
|
278
|
+
return (liveTells && tellPump === null && pending.some((tell) => !attempted.has(tell.id) && tell.schemaJson === undefined));
|
|
281
279
|
}
|
|
282
280
|
async function settleCompletion(result, session, active) {
|
|
283
281
|
try {
|
package/build/src/akuma-body.js
CHANGED
|
@@ -2,6 +2,8 @@ import { LEASH_HELD_EXIT, runAkumaBody } from "./akuma/body.js";
|
|
|
2
2
|
import { worldRootForAkumaPaths } from "./akuma/identity.js";
|
|
3
3
|
import { World } from "./world.js";
|
|
4
4
|
import { executeKillAkuma, executeTellAkuma, executeWaitAkuma } from "./akuma/fleet-execution.js";
|
|
5
|
+
import { Akuma as PublicAkuma } from "./akuma/akuma-instance.js";
|
|
6
|
+
import { JsonSchema } from "./akuma/schema.js";
|
|
5
7
|
import { fleetRequestCommands } from "./akuma/fleet-request.js";
|
|
6
8
|
import { worktreeHooksFrom } from "./git/hooks.js";
|
|
7
9
|
import { contractRequestCommands, executeForwardedAudit, executeForwardedDeliver, executeForwardedReview, } from "./library/contract-operations.js";
|
|
@@ -82,6 +84,10 @@ function fleetRequestPort(world) {
|
|
|
82
84
|
recordedAt: input.recordedAt,
|
|
83
85
|
signal: input.signal,
|
|
84
86
|
}),
|
|
87
|
+
tellAnswer: async (input) => await PublicAkuma.select(world, input.target).tell(input.body, {
|
|
88
|
+
schema: JsonSchema(input.schemaJson),
|
|
89
|
+
...(input.interrupt === undefined ? {} : { interrupt: input.interrupt }),
|
|
90
|
+
}),
|
|
85
91
|
kill: async (input) => {
|
|
86
92
|
const result = await executeKillAkuma({ path: world, ids: input.targets, signal: input.signal });
|
|
87
93
|
return result;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type AkuId
|
|
1
|
+
import { type AkuId } from "../../akuma/identity.js";
|
|
2
|
+
import { type ActivityHistory } from "../../akuma/akuma.js";
|
|
2
3
|
import { Keiyaku, type AkumaKillResult, type AkumaHistoryResult, type AkumaObservation, type AkumaTellResult, type AkumaWaitResult, type CallResult, type ForkResult, type Keiyaku as KeiyakuContract, type Repo } from "../../index.js";
|
|
3
4
|
import type { Settings } from "../../settings.js";
|
|
4
5
|
import type { WorldRoot } from "../../world.js";
|
|
@@ -10,6 +11,7 @@ export type AkumaInvocationResult = Readonly<{
|
|
|
10
11
|
action: "call";
|
|
11
12
|
result: CallResult;
|
|
12
13
|
world: WorldRoot;
|
|
14
|
+
schemaAnswer?: unknown;
|
|
13
15
|
}> | Readonly<{
|
|
14
16
|
kind: "akuma";
|
|
15
17
|
action: "status";
|
|
@@ -27,6 +29,13 @@ export type AkumaInvocationResult = Readonly<{
|
|
|
27
29
|
result: AkumaTellResult;
|
|
28
30
|
body: string;
|
|
29
31
|
alias?: string;
|
|
32
|
+
}> | Readonly<{
|
|
33
|
+
kind: "akuma";
|
|
34
|
+
action: "tell";
|
|
35
|
+
mode: "schema";
|
|
36
|
+
result: unknown;
|
|
37
|
+
body: string;
|
|
38
|
+
alias?: string;
|
|
30
39
|
}> | Readonly<{
|
|
31
40
|
kind: "akuma";
|
|
32
41
|
action: "tell";
|
|
@@ -1,7 +1,35 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
1
2
|
import { Keiyaku, } from "../../index.js";
|
|
2
3
|
import { beginCall, finishCall } from "../../library/akuma-creation.js";
|
|
3
4
|
import { killAkuma, tellAkuma, waitAkuma } from "../../library/fleet.js";
|
|
4
5
|
import { localExecutionContext } from "../../akuma/requests.js";
|
|
6
|
+
import { Akuma, JsonSchema } from "../../akuma/index.js";
|
|
7
|
+
import { addressAkuma } from "../../library/address.js";
|
|
8
|
+
import { executionChannel } from "../../akuma/requests.js";
|
|
9
|
+
import { requestForwardedFleetTellAnswer } from "../../akuma/fleet-request.js";
|
|
10
|
+
async function schemaFromFile(path) {
|
|
11
|
+
try {
|
|
12
|
+
return JsonSchema(await readFile(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
throw new Error(`cannot read JSON Schema file ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function firstSchemaAnswer(id, world, schema) {
|
|
19
|
+
const history = await Akuma.select(world, id).history();
|
|
20
|
+
const outcome = history.rows.find((row) => row.kind === "outcome" && row.outcome.kind === "answered");
|
|
21
|
+
if (outcome === undefined || outcome.kind !== "outcome" || outcome.outcome.kind !== "answered") {
|
|
22
|
+
throw new Error("schema call completed without an answered Turn");
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(outcome.outcome.answer);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new Error(`schema answer is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
}
|
|
31
|
+
return schema.decode(parsed);
|
|
32
|
+
}
|
|
5
33
|
function inputAlias(selector) {
|
|
6
34
|
return selector.startsWith("@") ? selector : undefined;
|
|
7
35
|
}
|
|
@@ -25,6 +53,36 @@ async function invokeWait(command, input) {
|
|
|
25
53
|
}
|
|
26
54
|
async function invokeTell(command, input) {
|
|
27
55
|
const body = await promptBody(command, input);
|
|
56
|
+
if (command.schema !== undefined) {
|
|
57
|
+
const schema = await schemaFromFile(command.schema);
|
|
58
|
+
const addressed = await addressAkuma({
|
|
59
|
+
path: input.path,
|
|
60
|
+
akuma: command.akuma,
|
|
61
|
+
...(input.repo === undefined ? {} : { repo: input.repo }),
|
|
62
|
+
});
|
|
63
|
+
const channel = executionChannel(input.execution);
|
|
64
|
+
const answer = channel.kind === "body-request"
|
|
65
|
+
? await requestForwardedFleetTellAnswer({
|
|
66
|
+
directory: channel.directory,
|
|
67
|
+
target: addressed.id,
|
|
68
|
+
body,
|
|
69
|
+
schema,
|
|
70
|
+
interrupt: command.interrupt,
|
|
71
|
+
})
|
|
72
|
+
: await Akuma.select(addressed.path, addressed.id).tell(body, {
|
|
73
|
+
schema,
|
|
74
|
+
...(command.interrupt ? { interrupt: true } : {}),
|
|
75
|
+
});
|
|
76
|
+
const alias = inputAlias(command.akuma);
|
|
77
|
+
return {
|
|
78
|
+
kind: "akuma",
|
|
79
|
+
action: "tell",
|
|
80
|
+
mode: "schema",
|
|
81
|
+
result: answer,
|
|
82
|
+
body,
|
|
83
|
+
...(alias === undefined ? {} : { alias }),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
28
86
|
if (command.interrupt) {
|
|
29
87
|
const result = await Keiyaku.interrupt({
|
|
30
88
|
path: input.path,
|
|
@@ -106,6 +164,7 @@ export async function invokeAkuma(command, input) {
|
|
|
106
164
|
switch (command.command) {
|
|
107
165
|
case "call": {
|
|
108
166
|
const body = await promptBody(command, input);
|
|
167
|
+
const schema = command.schema === undefined ? undefined : await schemaFromFile(command.schema);
|
|
109
168
|
const born = await beginCall({
|
|
110
169
|
path: input.path,
|
|
111
170
|
archetype: command.archetype,
|
|
@@ -119,8 +178,13 @@ export async function invokeAkuma(command, input) {
|
|
|
119
178
|
...(input.contract === undefined ? {} : { contract: input.contract }),
|
|
120
179
|
...(command.alias === undefined ? {} : { alias: command.alias }),
|
|
121
180
|
...(command.allowed === undefined ? {} : { allowed: command.allowed }),
|
|
181
|
+
...(schema === undefined ? {} : { schema }),
|
|
122
182
|
}, input.execution ?? localExecutionContext());
|
|
123
183
|
const result = await (input.finishCall ?? finishCall)(born);
|
|
184
|
+
if (schema !== undefined && command.mode === "wait" && result.observation.kind === "observed") {
|
|
185
|
+
const answer = await firstSchemaAnswer(result.akuma, input.path, schema);
|
|
186
|
+
return { kind: "akuma", action: "call", result, world: input.path, schemaAnswer: answer };
|
|
187
|
+
}
|
|
124
188
|
return { kind: "akuma", action: "call", result, world: input.path };
|
|
125
189
|
}
|
|
126
190
|
case "wait":
|
|
@@ -24,6 +24,7 @@ export type ParsedAkumaCommand = Output & ((Readonly<{
|
|
|
24
24
|
timeoutMs?: number;
|
|
25
25
|
readonly?: true;
|
|
26
26
|
allowed?: AllowedActions;
|
|
27
|
+
schema?: string;
|
|
27
28
|
}> & Prompted) | Readonly<{
|
|
28
29
|
command: "kill";
|
|
29
30
|
akuma: readonly string[];
|
|
@@ -35,6 +36,7 @@ export type ParsedAkumaCommand = Output & ((Readonly<{
|
|
|
35
36
|
}> | (Readonly<{
|
|
36
37
|
command: "tell";
|
|
37
38
|
interrupt: boolean;
|
|
39
|
+
schema?: string;
|
|
38
40
|
}> & Addressed & Prompted) | (Readonly<{
|
|
39
41
|
command: "history";
|
|
40
42
|
last: boolean;
|
|
@@ -16,8 +16,9 @@ const AKUMA_COMMAND_SPECS = {
|
|
|
16
16
|
readonly: "boolean",
|
|
17
17
|
allowed: "repeatable",
|
|
18
18
|
json: "boolean",
|
|
19
|
+
schema: "value",
|
|
19
20
|
},
|
|
20
|
-
usage: "call <akuma-name> [--contract <kei/...>] [--alias @name] [--readonly] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)",
|
|
21
|
+
usage: "call <akuma-name> [--contract <kei/...>] [--alias @name] [--readonly] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)",
|
|
21
22
|
purpose: "Birth an Akuma from <akuma-name> with one prompt.",
|
|
22
23
|
details: [
|
|
23
24
|
"Give <prompt> as one argument, or use - to read stdin.",
|
|
@@ -26,6 +27,7 @@ const AKUMA_COMMAND_SPECS = {
|
|
|
26
27
|
"--alias assigns the world-local @name selector to the born Akuma.",
|
|
27
28
|
"--readonly adds the one-way read-only birth restriction.",
|
|
28
29
|
"Repeated --allowed adds actions to the selected Akuma's defaults.",
|
|
30
|
+
"--schema reads a JSON Schema file for the answer contract; stdin remains the prompt source.",
|
|
29
31
|
"With --contract, Dispatch succeeds first. If @name exists, the alias then moves.",
|
|
30
32
|
].join("\n"),
|
|
31
33
|
},
|
|
@@ -39,12 +41,13 @@ const AKUMA_COMMAND_SPECS = {
|
|
|
39
41
|
tell: {
|
|
40
42
|
arity: 1,
|
|
41
43
|
stdin: true,
|
|
42
|
-
flags: { interrupt: "boolean", json: "boolean" },
|
|
43
|
-
usage: "tell <aku/...|@alias> [--interrupt] [--json] (<prompt> | -)",
|
|
44
|
+
flags: { interrupt: "boolean", schema: "value", json: "boolean" },
|
|
45
|
+
usage: "tell <aku/...|@alias> [--interrupt] [--schema <file>] [--json] (<prompt> | -)",
|
|
44
46
|
purpose: "Send one prompt to an existing Akuma and wake it.",
|
|
45
47
|
details: [
|
|
46
48
|
"Give <prompt> as one argument, or use - to read stdin.",
|
|
47
49
|
"--interrupt ends the current Body before recording the prompt and waking its successor.",
|
|
50
|
+
"--schema reads a JSON Schema file for the answer contract; stdin remains the prompt source.",
|
|
48
51
|
].join("\n"),
|
|
49
52
|
},
|
|
50
53
|
history: {
|
|
@@ -265,7 +268,15 @@ function parsePrompted(action, positionals, stdin, fail) {
|
|
|
265
268
|
};
|
|
266
269
|
}
|
|
267
270
|
function parseTell(flags, subject, prompt, output, fail) {
|
|
268
|
-
|
|
271
|
+
const schema = flags.schema === undefined ? undefined : stringFlag(flags.schema, "tell --schema requires a file", fail);
|
|
272
|
+
return {
|
|
273
|
+
command: "tell",
|
|
274
|
+
akuma: validateDirect(subject, fail),
|
|
275
|
+
interrupt: flags.interrupt === true,
|
|
276
|
+
...(schema === undefined ? {} : { schema }),
|
|
277
|
+
prompt,
|
|
278
|
+
output,
|
|
279
|
+
};
|
|
269
280
|
}
|
|
270
281
|
function parseAllowedFlag(raw, fail) {
|
|
271
282
|
if (raw === undefined)
|
|
@@ -295,6 +306,7 @@ function parseCall(flags, archetype, prompt, output, fail) {
|
|
|
295
306
|
const mode = flags.detach === true ? "detach" : "wait";
|
|
296
307
|
const timeoutMs = flags.wait === undefined ? undefined : parseDuration(flags.wait, "--wait", fail);
|
|
297
308
|
const allowed = parseAllowedFlag(flags.allowed, fail);
|
|
309
|
+
const schema = flags.schema === undefined ? undefined : stringFlag(flags.schema, "call --schema requires a file", fail);
|
|
298
310
|
return {
|
|
299
311
|
command: "call",
|
|
300
312
|
archetype,
|
|
@@ -304,6 +316,7 @@ function parseCall(flags, archetype, prompt, output, fail) {
|
|
|
304
316
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
305
317
|
...(flags.readonly === true ? { readonly: true } : {}),
|
|
306
318
|
...(allowed === undefined ? {} : { allowed }),
|
|
319
|
+
...(schema === undefined ? {} : { schema }),
|
|
307
320
|
prompt,
|
|
308
321
|
output,
|
|
309
322
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KillEvidence } from "../../akuma/
|
|
1
|
+
import type { KillEvidence } from "../../akuma/akuma.js";
|
|
2
2
|
import type { AkumaObservation, AkumaObservationStage, CreatedTaskObservation, DispatchAssociation } from "../../index.js";
|
|
3
3
|
import type { AkumaInvocationResult } from "../commands/akuma-invoke.js";
|
|
4
4
|
import type { ParsedCommand } from "../parse.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivityRow, SnapshotRow } from "../../akuma/
|
|
1
|
+
import type { ActivityRow, SnapshotRow } from "../../akuma/akuma.js";
|
|
2
2
|
import type { AkumaObservation } from "../../index.js";
|
|
3
3
|
type FleetTimelineRow = Extract<AkumaObservation["status"]["timeline"]["entries"][number], {
|
|
4
4
|
kind: "row";
|
|
@@ -19,6 +19,8 @@ function posixShellArgument(value) {
|
|
|
19
19
|
return `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
20
20
|
}
|
|
21
21
|
function callText(result, context) {
|
|
22
|
+
if (result.schemaAnswer !== undefined)
|
|
23
|
+
return JSON.stringify(result.schemaAnswer);
|
|
22
24
|
const alias = result.result.alias.kind === "aliased" ? result.result.alias.alias.alias : undefined;
|
|
23
25
|
const contractId = result.result.dispatch.kind === "dispatched" ? result.result.dispatch.dispatch.contractId : undefined;
|
|
24
26
|
const facts = [...dispatchLines(result.result.dispatch)];
|
|
@@ -63,6 +65,8 @@ export function renderAkumaText(command, result, context = DEFAULT_CONTEXT) {
|
|
|
63
65
|
const cwd = executionCwdLine(result.result);
|
|
64
66
|
return cwd.length === 0 ? answer : `${cwd.join("\n")}\n${answer}`;
|
|
65
67
|
}
|
|
68
|
+
if (result.action === "tell" && result.mode === "schema")
|
|
69
|
+
return JSON.stringify(result.result);
|
|
66
70
|
switch (result.action) {
|
|
67
71
|
case "call":
|
|
68
72
|
return callText(result, context);
|
|
@@ -111,6 +115,8 @@ function killExitCode(result) {
|
|
|
111
115
|
: 0;
|
|
112
116
|
}
|
|
113
117
|
function tellExitCode(result) {
|
|
118
|
+
if (result.mode === "schema")
|
|
119
|
+
return 0;
|
|
114
120
|
return result.mode === "ordinary"
|
|
115
121
|
? result.result.tell.wake.kind === "failed"
|
|
116
122
|
? 2
|
|
@@ -145,7 +151,7 @@ export function akumaExitCode(result) {
|
|
|
145
151
|
}
|
|
146
152
|
export function akumaJsonValue(result) {
|
|
147
153
|
if (result.action === "call")
|
|
148
|
-
return result.result;
|
|
154
|
+
return result.schemaAnswer === undefined ? result.result : result.schemaAnswer;
|
|
149
155
|
if (result.action === "fork")
|
|
150
156
|
return result.receipt;
|
|
151
157
|
if (result.action === "status")
|
|
@@ -153,7 +159,7 @@ export function akumaJsonValue(result) {
|
|
|
153
159
|
if (result.action === "wait")
|
|
154
160
|
return result.result;
|
|
155
161
|
if (result.action === "tell")
|
|
156
|
-
return result.result;
|
|
162
|
+
return result.mode === "schema" ? result.result : result.result;
|
|
157
163
|
if (result.action === "kill")
|
|
158
164
|
return result.result;
|
|
159
165
|
return result.historyResult;
|
package/build/src/kanshi/read.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Repo } from "../library/repo.js";
|
|
|
2
2
|
import { scopeForRepo } from "../library/repo.js";
|
|
3
3
|
import { observeTaskBoard } from "../task/operations.js";
|
|
4
4
|
import { contractNamespace } from "../task/identity.js";
|
|
5
|
-
import { Akuma } from "../akuma/
|
|
5
|
+
import { Akuma } from "../akuma/akuma.js";
|
|
6
6
|
import { readAliases } from "../alias/index.js";
|
|
7
7
|
import { readDispatchesAt } from "../dispatch/index.js";
|
|
8
8
|
import { readTaskHolderProjectionAt } from "../settlement/holder.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContractBoard, ContractDisposition, ContractPhase } from "../library/contract.js";
|
|
2
2
|
import type { TaskId, TaskRef, TaskRow } from "../task/index.js";
|
|
3
|
-
import type { AkumaList, AkumaListRow, UnbornAkumaListRow, ActivitySnapshot } from "../akuma/
|
|
3
|
+
import type { AkumaList, AkumaListRow, UnbornAkumaListRow, ActivitySnapshot } from "../akuma/akuma.js";
|
|
4
4
|
import type { AkumaAlias } from "../identity/selector.js";
|
|
5
5
|
import type { WorldRoot } from "../world.js";
|
|
6
6
|
import type { ContractId } from "../library/contract.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @architectureCompositionRoot */
|
|
2
2
|
import { readAliases } from "../alias/index.js";
|
|
3
3
|
import { Akuma } from "../akuma/akuma.js";
|
|
4
|
-
import { probeBornAkuma } from "../akuma/
|
|
4
|
+
import { probeBornAkuma } from "../akuma/akuma-probe.js";
|
|
5
5
|
import { parseAkuId } from "../akuma/identity.js";
|
|
6
6
|
import { contractId } from "../core/facts/types.js";
|
|
7
7
|
import { readDispatches } from "../dispatch/index.js";
|
|
@@ -8,6 +8,7 @@ import { type AkumaAlias } from "../identity/selector.js";
|
|
|
8
8
|
import type { Settings } from "../settings.js";
|
|
9
9
|
import { type WorldRoot } from "../world.js";
|
|
10
10
|
import type { AllowedAction } from "../akuma/allowed.js";
|
|
11
|
+
import type { Schema } from "../akuma/schema.js";
|
|
11
12
|
import { type ExecutionContext } from "../akuma/requests.js";
|
|
12
13
|
import { type Keiyaku } from "./contract.js";
|
|
13
14
|
import { type Repo } from "./repo.js";
|
|
@@ -52,6 +53,7 @@ export type CallInput = Readonly<{
|
|
|
52
53
|
contract?: Keiyaku;
|
|
53
54
|
alias?: AkumaAlias;
|
|
54
55
|
allowed?: readonly AllowedAction[];
|
|
56
|
+
schema?: Schema<unknown>;
|
|
55
57
|
}>;
|
|
56
58
|
export type CallObservation = Readonly<{
|
|
57
59
|
kind: "detached";
|
|
@@ -242,6 +242,7 @@ export async function beginCall(input, context) {
|
|
|
242
242
|
"contract",
|
|
243
243
|
"alias",
|
|
244
244
|
"allowed",
|
|
245
|
+
"schema",
|
|
245
246
|
], "Keiyaku.call input");
|
|
246
247
|
const path = await World.prove(nonblank(values.path, "path"));
|
|
247
248
|
const archetype = nonblank(values.archetype, "archetype");
|
|
@@ -265,6 +266,7 @@ export async function beginCall(input, context) {
|
|
|
265
266
|
body,
|
|
266
267
|
...(readonlyRequested === undefined ? {} : { readonly: readonlyRequested }),
|
|
267
268
|
...(values.allowed === undefined ? {} : { allowed: values.allowed }),
|
|
269
|
+
...(values.schema === undefined ? {} : { schema: values.schema }),
|
|
268
270
|
...(execution === undefined ? {} : { cwd: execution.cwd }),
|
|
269
271
|
};
|
|
270
272
|
const { born, akuma } = await admitCall({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @architectureCompositionRoot */
|
|
2
|
-
import { type ActivityHistory, type OutcomeRow, type AkumaStatus, type InterruptReceipt } from "../akuma/
|
|
2
|
+
import { type ActivityHistory, type OutcomeRow, type AkumaStatus, type InterruptReceipt } from "../akuma/akuma.js";
|
|
3
3
|
import { type ExecutionContext } from "../akuma/requests.js";
|
|
4
4
|
import { type DispatchAssociation } from "../dispatch/index.js";
|
|
5
5
|
import { type AkumaAddressInput, type AkumaSetAddressInput } from "./address.js";
|
|
@@ -11,7 +11,7 @@ export type AkumaWaitInput = AkumaSetAddressInput & Readonly<{
|
|
|
11
11
|
export type AkumaTellInput = AkumaAddressInput & Readonly<{
|
|
12
12
|
body: string;
|
|
13
13
|
}>;
|
|
14
|
-
export type { TellResult, TellWake } from "../akuma/
|
|
14
|
+
export type { TellResult, TellWake } from "../akuma/akuma.js";
|
|
15
15
|
export type AkumaInterruptInput = AkumaAddressInput & Readonly<{
|
|
16
16
|
body: string;
|
|
17
17
|
}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** @architectureCompositionRoot */
|
|
2
|
-
import { Akuma, AkumaNotBornError, } from "../akuma/
|
|
3
|
-
import { readBudgetedStatus } from "../akuma/akuma.js";
|
|
2
|
+
import { Akuma, AkumaNotBornError, readBudgetedStatus, } from "../akuma/akuma.js";
|
|
4
3
|
import { executionChannel, localExecutionContext } from "../akuma/requests.js";
|
|
5
4
|
import { requestForwardedFleetKill, requestForwardedFleetTell, requestForwardedFleetWait, } from "../akuma/fleet-request.js";
|
|
6
5
|
import { executeKillAkuma, executeTellAkuma, executeWaitAkuma } from "../akuma/fleet-execution.js";
|
|
@@ -22,7 +22,7 @@ export type { Dispatch, DispatchFailure } from "../dispatch/index.js";
|
|
|
22
22
|
export type { AkumaAlias } from "../identity/selector.js";
|
|
23
23
|
export type { AkumaGlob } from "../identity/selector.js";
|
|
24
24
|
export type { AkuId } from "../akuma/identity.js";
|
|
25
|
-
export type { TellResult, TellWake } from "../akuma/
|
|
25
|
+
export type { TellResult, TellWake } from "../akuma/akuma.js";
|
|
26
26
|
export type { AllowedAction, AllowedActions } from "../akuma/allowed.js";
|
|
27
27
|
export type { Catalog, CatalogInput, CatalogQuery } from "./catalog.js";
|
|
28
28
|
export type { NukeInput, NukeResult } from "./nuke.js";
|
|
@@ -374,10 +374,7 @@ async function createPluginRuntime(input) {
|
|
|
374
374
|
while (inFlight.size > 0)
|
|
375
375
|
await Promise.all([...inFlight]);
|
|
376
376
|
};
|
|
377
|
-
await Promise.race([
|
|
378
|
-
settle(),
|
|
379
|
-
new Promise((resolve) => setTimeout(resolve, PLUGIN_DRAIN_TIMEOUT_MS)),
|
|
380
|
-
]);
|
|
377
|
+
await Promise.race([settle(), new Promise((resolve) => setTimeout(resolve, PLUGIN_DRAIN_TIMEOUT_MS))]);
|
|
381
378
|
},
|
|
382
379
|
});
|
|
383
380
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/keiyaku",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.16",
|
|
4
4
|
"workspaces": [
|
|
5
5
|
"plugins/*"
|
|
6
6
|
],
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
79
79
|
"@anthropic-ai/claude-agent-sdk": "^0.3.226",
|
|
80
|
-
"@astrosheep/keiyaku-plugin-square": "0.1.
|
|
80
|
+
"@astrosheep/keiyaku-plugin-square": "0.1.4",
|
|
81
81
|
"@earendil-works/pi-coding-agent": "0.80.10",
|
|
82
82
|
"@opencode-ai/sdk": "1.18.3",
|
|
83
83
|
"cross-spawn": "^7.0.6",
|