@astrosheep/keiyaku 4.5.25 → 4.5.27
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-akuma/SKILL.md +5 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +16 -14
- package/build/src/akuma/akuma-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +11 -0
- package/build/src/akuma/akuma-handle.d.ts +53 -1
- package/build/src/akuma/akuma-handle.js +75 -7
- package/build/src/akuma/akuma-instance.js +6 -22
- package/build/src/akuma/akuma-probe.d.ts +20 -1
- package/build/src/akuma/akuma-probe.js +20 -3
- package/build/src/akuma/akuma.d.ts +12 -0
- package/build/src/akuma/allowed.d.ts +2 -1
- package/build/src/akuma/allowed.js +9 -4
- package/build/src/akuma/archetype.js +3 -3
- package/build/src/akuma/body.js +8 -3
- package/build/src/akuma/fleet-execution.d.ts +6 -1
- package/build/src/akuma/fleet-execution.js +44 -0
- package/build/src/akuma/fleet-observation.d.ts +238 -0
- package/build/src/akuma/fleet-observation.js +13 -0
- package/build/src/akuma/fleet-owner-port.d.ts +12 -0
- package/build/src/akuma/fleet-owner-port.js +46 -0
- package/build/src/akuma/fleet-request.d.ts +55 -4
- package/build/src/akuma/fleet-request.js +101 -6
- package/build/src/akuma/heart/index.d.ts +1 -0
- package/build/src/akuma/heart/index.js +4 -1
- package/build/src/akuma/heart/rows.d.ts +1 -0
- package/build/src/akuma/heart/rows.js +8 -0
- package/build/src/akuma/heart/soul.js +2 -2
- package/build/src/akuma/projection-read.js +5 -0
- package/build/src/akuma/projection.d.ts +36 -0
- package/build/src/akuma/projection.js +2 -1
- package/build/src/akuma/provider.d.ts +14 -0
- package/build/src/akuma/provider.js +66 -4
- package/build/src/akuma/providers/acp/events.js +16 -8
- package/build/src/akuma/providers/claude/events.js +5 -7
- package/build/src/akuma/providers/opencode-sdk/events.js +9 -5
- package/build/src/akuma/providers/pi/events.js +4 -3
- package/build/src/akuma/request-lifecycle.js +10 -1
- package/build/src/akuma-body.js +1 -33
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -0
- package/build/src/cli/commands/akuma-invoke.js +58 -6
- package/build/src/cli/commands/akuma.d.ts +1 -0
- package/build/src/cli/commands/akuma.js +10 -15
- package/build/src/cli/commands/contract-help.js +8 -5
- package/build/src/cli/render/akuma-activity.d.ts +7 -1
- package/build/src/cli/render/akuma-activity.js +171 -56
- package/build/src/cli/render/akuma-tool.d.ts +5 -0
- package/build/src/cli/render/akuma-tool.js +122 -16
- package/build/src/cli/render/akuma.d.ts +3 -1
- package/build/src/cli/render/akuma.js +34 -2
- package/build/src/cli/render/catalog.js +2 -3
- package/build/src/cli/render/contract-observation.d.ts +1 -0
- package/build/src/cli/render/contract-observation.js +20 -1
- package/build/src/cli/render/contract.js +4 -4
- package/build/src/cli/render/execution-progress.d.ts +1 -0
- package/build/src/cli/render/execution-progress.js +77 -3
- package/build/src/cli/render/kanshi.js +5 -19
- package/build/src/cli/render/terminal.js +3 -1
- package/build/src/cli/runtime.d.ts +6 -0
- package/build/src/cli/runtime.js +35 -17
- package/build/src/core/facts/offer.d.ts +1 -2
- package/build/src/core/verbs/abandon.js +1 -1
- package/build/src/core/verbs/amend.js +1 -1
- package/build/src/core/verbs/arc.js +1 -1
- package/build/src/core/verbs/attestation.js +1 -1
- package/build/src/core/verbs/bind.js +1 -1
- package/build/src/core/verbs/deliver.js +1 -1
- package/build/src/core/verbs/placement.js +1 -1
- package/build/src/core/verbs/reintegrate.js +1 -1
- package/build/src/git/admission.js +18 -7
- package/build/src/identity/coordinates.js +1 -1
- package/build/src/library/address.d.ts +33 -3
- package/build/src/library/address.js +89 -28
- package/build/src/library/akuma-creation.js +1 -1
- package/build/src/library/composition.d.ts +12 -0
- package/build/src/library/fleet.d.ts +11 -1
- package/build/src/library/fleet.js +138 -48
- package/build/src/library/keiyaku.d.ts +12 -0
- package/build/src/plugin/public.d.ts +2 -0
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/package.json +3 -3
|
@@ -44,10 +44,11 @@ to that invocation directory. Without `--workdir`, an unassociated call uses
|
|
|
44
44
|
the invocation cwd, while a `--contract` call uses its appointed worktree.
|
|
45
45
|
|
|
46
46
|
Repeated `--allowed` values add actions to the selected Akuma's defaults; they
|
|
47
|
-
never narrow them. An omitted Archetype
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
never narrow them. An omitted Archetype permits `akuma.*`, every `task.*`,
|
|
48
|
+
`contract.audit`, and `contract.deliver`; `contract.review` requires an explicit
|
|
49
|
+
Archetype or call-time grant. An explicit empty default permits none. A nested
|
|
50
|
+
call can use only actions permitted by its direct parent Soul. Use `status
|
|
51
|
+
<aku/...|@alias>` to inspect the born worker's frozen effective actions.
|
|
51
52
|
|
|
52
53
|
## Answer Schemas
|
|
53
54
|
|
|
@@ -19,10 +19,10 @@ resolve.
|
|
|
19
19
|
|
|
20
20
|
The author pins every public, high-level fact: surfaces, semantics, persisted
|
|
21
21
|
shapes, the observable acceptance boundary. Private decomposition, helper
|
|
22
|
-
names, and control flow belong to the Deliverer — and that freedom
|
|
23
|
-
the author genuinely not caring, never from the author not deciding.
|
|
24
|
-
are observable accept/reject observations a Reviewer can judge without
|
|
25
|
-
the author anything further.
|
|
22
|
+
names, and equivalent control flow belong to the Deliverer — and that freedom
|
|
23
|
+
comes from the author genuinely not caring, never from the author not deciding.
|
|
24
|
+
Criteria are observable accept/reject observations a Reviewer can judge without
|
|
25
|
+
asking the author anything further.
|
|
26
26
|
|
|
27
27
|
## Author And Bind
|
|
28
28
|
|
|
@@ -47,22 +47,24 @@ fulfillment.>
|
|
|
47
47
|
|
|
48
48
|
## Design
|
|
49
49
|
<The closed decisions. A statement belongs here exactly when a test-green
|
|
50
|
-
candidate could still violate it:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
candidate could still violate it: existing owner modules and entry points,
|
|
51
|
+
what is reused or changed, the implementation approach, critical ordering, the
|
|
52
|
+
exact public surface — each type with its fields, each verb with its success,
|
|
53
|
+
refusal, and error arms and their reason words; the persisted format; which way
|
|
54
|
+
data flows and where it commits or refuses; which parallel shapes are
|
|
55
|
+
forbidden. Unresolved architectural choices are settled before binding. Private
|
|
56
|
+
helper names and equivalent control flow remain the Deliverer's freedom.>
|
|
56
57
|
|
|
57
58
|
```text
|
|
58
59
|
<pseudocode — only where ordering matters>
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
## Region
|
|
62
|
-
<one intended write pattern per line —
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
<one intended write pattern per line — the narrowest justified intended
|
|
64
|
+
writes for this approach, not every potentially involved file. Planning evidence
|
|
65
|
+
for overlap detection, never ownership or the exact diff. No broad directory
|
|
66
|
+
fallback or redundant parent/child patterns; directory patterns end with `/`.
|
|
67
|
+
Fenced lines, list items, and bare lines are equivalent and union.>
|
|
66
68
|
|
|
67
69
|
## Criteria
|
|
68
70
|
### <one observable condition>
|
|
@@ -5,6 +5,12 @@ export declare class AkumaNotBornError extends Error {
|
|
|
5
5
|
readonly kind = "akuma-not-born";
|
|
6
6
|
constructor(id: AkuId);
|
|
7
7
|
}
|
|
8
|
+
export declare class AkumaObservationError extends Error {
|
|
9
|
+
readonly id: AkuId;
|
|
10
|
+
readonly diagnostic: string;
|
|
11
|
+
readonly kind = "akuma-observation";
|
|
12
|
+
constructor(id: AkuId, diagnostic: string);
|
|
13
|
+
}
|
|
8
14
|
export declare class AkumaDecodeError extends Error {
|
|
9
15
|
readonly diagnostic: string;
|
|
10
16
|
readonly answer?: string | undefined;
|
|
@@ -8,6 +8,17 @@ export class AkumaNotBornError extends Error {
|
|
|
8
8
|
this.name = "AkumaNotBornError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
export class AkumaObservationError extends Error {
|
|
12
|
+
id;
|
|
13
|
+
diagnostic;
|
|
14
|
+
kind = "akuma-observation";
|
|
15
|
+
constructor(id, diagnostic) {
|
|
16
|
+
super(`Akuma ${id} observation failed: ${diagnostic}`);
|
|
17
|
+
this.id = id;
|
|
18
|
+
this.diagnostic = diagnostic;
|
|
19
|
+
this.name = "AkumaObservationError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
11
22
|
export class AkumaDecodeError extends Error {
|
|
12
23
|
diagnostic;
|
|
13
24
|
answer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TellResult, type TellWakeRuntime } from "./body.js";
|
|
2
|
-
import { HeldAkumaLeash, type KillEvidence } from "./heart/index.js";
|
|
2
|
+
import { HeldAkumaLeash, type KillEvidence, type TellFact, type TurnOutcome } from "./heart/index.js";
|
|
3
3
|
import { type AkuId, type AkumaPaths } from "./identity.js";
|
|
4
4
|
import { type ActivityHistory, type ExactHistory } from "./projection.js";
|
|
5
5
|
import { type WaitReason } from "./akuma-observe.js";
|
|
@@ -11,6 +11,22 @@ export declare function settleAkumaKill(paths: AkumaPaths, signal?: AbortSignal,
|
|
|
11
11
|
leash?: HeldAkumaLeash;
|
|
12
12
|
}>>;
|
|
13
13
|
export declare function killAkumaWithRecovery(paths: AkumaPaths, recover?: (paths: AkumaPaths) => Promise<void>, signal?: AbortSignal): Promise<KillEvidence>;
|
|
14
|
+
export type TellAdmission = Readonly<{
|
|
15
|
+
kind: "not-born";
|
|
16
|
+
}> | Readonly<{
|
|
17
|
+
kind: "admitted";
|
|
18
|
+
tell: TellFact;
|
|
19
|
+
wake: Promise<TellResult>;
|
|
20
|
+
}>;
|
|
21
|
+
export type InterruptAdmission = Readonly<{
|
|
22
|
+
kind: "unavailable";
|
|
23
|
+
evidence: "hung" | "untidy" | "unavailable";
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
kind: "admitted";
|
|
26
|
+
tell: TellFact;
|
|
27
|
+
putDown: "was-idle" | "self-aborted";
|
|
28
|
+
wake: Promise<TellResult>;
|
|
29
|
+
}>;
|
|
14
30
|
export declare class AkumaHandle {
|
|
15
31
|
readonly id: AkuId;
|
|
16
32
|
private readonly worldPath;
|
|
@@ -42,6 +58,31 @@ export declare class AkumaHandle {
|
|
|
42
58
|
initiator?: string;
|
|
43
59
|
signal?: AbortSignal;
|
|
44
60
|
}>): Promise<TellResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Record one Tell and start its wake without waiting for delivery. A bounded
|
|
63
|
+
* caller starts its own deadline at this admission boundary while the wake
|
|
64
|
+
* continues in the background; the unbounded public Tell awaits `wake`.
|
|
65
|
+
*/
|
|
66
|
+
admitTell(body: string, tellId?: string, recordedAt?: string, runtime?: TellWakeRuntime, options?: Readonly<{
|
|
67
|
+
schemaJson?: string;
|
|
68
|
+
initiator?: string;
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
}>): Promise<TellAdmission>;
|
|
71
|
+
/**
|
|
72
|
+
* The admitted Tell's receipt as it stands now. A bounded caller that stops
|
|
73
|
+
* waiting before the wake settles reports this honest instant rather than a
|
|
74
|
+
* delivery that has not happened yet.
|
|
75
|
+
*/
|
|
76
|
+
admittedReceipt(tellId: string): Promise<TellResult>;
|
|
77
|
+
/** Observe the exact admitted Tell's bound Turn without substituting Akuma-wide idleness. */
|
|
78
|
+
tellOutcome(tellId: string, options?: Readonly<{
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
signal?: AbortSignal;
|
|
81
|
+
}>): Promise<Readonly<{
|
|
82
|
+
reason: WaitReason;
|
|
83
|
+
outcome: TurnOutcome | null;
|
|
84
|
+
}>>;
|
|
85
|
+
private boundTellOutcome;
|
|
45
86
|
interrupt(body: string, options?: Readonly<{
|
|
46
87
|
tellId?: string;
|
|
47
88
|
schemaJson?: string;
|
|
@@ -49,6 +90,17 @@ export declare class AkumaHandle {
|
|
|
49
90
|
signal?: AbortSignal;
|
|
50
91
|
runtime?: TellWakeRuntime;
|
|
51
92
|
}>): Promise<InterruptReceipt>;
|
|
93
|
+
/**
|
|
94
|
+
* Settle the predecessor and record the interrupt Tell, returning at the
|
|
95
|
+
* admission boundary so a bounded caller owns the wake wait.
|
|
96
|
+
*/
|
|
97
|
+
admitInterrupt(body: string, options?: Readonly<{
|
|
98
|
+
tellId?: string;
|
|
99
|
+
schemaJson?: string;
|
|
100
|
+
initiator?: string;
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
runtime?: TellWakeRuntime;
|
|
103
|
+
}>): Promise<InterruptAdmission>;
|
|
52
104
|
fork(input: Readonly<{
|
|
53
105
|
at: string;
|
|
54
106
|
}>): Promise<ForkReceipt>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { handoffPendingTells, wakeRecordedTell } from "./body.js";
|
|
3
|
-
import { HeldAkumaLeash, activitySlice, readForkPoint, readHeart, readKill, readLastAnsweredTurn, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
|
|
3
|
+
import { HeldAkumaLeash, activitySlice, readForkPoint, readHeart, readKill, readLastAnsweredTurn, readSoul, readTell, readTurn, recordTell, requestPause, requestStop, projectTell, } from "./heart/index.js";
|
|
4
4
|
import { acquireLeash } from "./control.js";
|
|
5
5
|
import { parsePublicHistoryId, pathsForAkuId } from "./identity.js";
|
|
6
6
|
import { projectTurns, selectHistory, selectExactHistory, } from "./projection.js";
|
|
7
7
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
8
8
|
import { publishAkuma } from "./publication.js";
|
|
9
9
|
import { spawnAkumaBody } from "./body.js";
|
|
10
|
-
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
10
|
+
import { AkumaNotBornError, AkumaProviderError } from "./akuma-errors.js";
|
|
11
11
|
import { bornStatus, defaultWaitComplete, readWaitComplete, waitForObservation, } from "./akuma-observe.js";
|
|
12
12
|
const CALL_EXECUTION = Symbol("akuma-call-execution");
|
|
13
13
|
function diagnostic(error) {
|
|
@@ -151,6 +151,17 @@ export class AkumaHandle {
|
|
|
151
151
|
return (await this.waitReceipt(predicate, options)).status;
|
|
152
152
|
}
|
|
153
153
|
async tell(body, tellId = randomUUID(), recordedAt = new Date().toISOString(), runtime, options = {}) {
|
|
154
|
+
const admitted = await this.admitTell(body, tellId, recordedAt, runtime, options);
|
|
155
|
+
if (admitted.kind === "not-born")
|
|
156
|
+
throw new AkumaNotBornError(this.id);
|
|
157
|
+
return await admitted.wake;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Record one Tell and start its wake without waiting for delivery. A bounded
|
|
161
|
+
* caller starts its own deadline at this admission boundary while the wake
|
|
162
|
+
* continues in the background; the unbounded public Tell awaits `wake`.
|
|
163
|
+
*/
|
|
164
|
+
async admitTell(body, tellId = randomUUID(), recordedAt = new Date().toISOString(), runtime, options = {}) {
|
|
154
165
|
const { schemaJson, initiator, signal } = options;
|
|
155
166
|
const admitted = await recordTell(this.paths, {
|
|
156
167
|
kind: "tell",
|
|
@@ -161,10 +172,66 @@ export class AkumaHandle {
|
|
|
161
172
|
...(schemaJson === undefined ? {} : { schemaJson }),
|
|
162
173
|
});
|
|
163
174
|
if (admitted.kind === "not-born")
|
|
164
|
-
|
|
165
|
-
return
|
|
175
|
+
return admitted;
|
|
176
|
+
return {
|
|
177
|
+
kind: "admitted",
|
|
178
|
+
tell: admitted.tell,
|
|
179
|
+
wake: wakeRecordedTell(this.paths, admitted.tell.id, runtime, signal),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The admitted Tell's receipt as it stands now. A bounded caller that stops
|
|
184
|
+
* waiting before the wake settles reports this honest instant rather than a
|
|
185
|
+
* delivery that has not happened yet.
|
|
186
|
+
*/
|
|
187
|
+
async admittedReceipt(tellId) {
|
|
188
|
+
const tell = await readTell(this.paths, tellId);
|
|
189
|
+
if (tell === null)
|
|
190
|
+
throw new AkumaProviderError(`recorded Tell ${tellId} is missing from Heart`);
|
|
191
|
+
const latestBody = (await readHeart(this.paths)).latestBody;
|
|
192
|
+
return {
|
|
193
|
+
admission: { tellId, fact: "recorded" },
|
|
194
|
+
row: projectTell(tell),
|
|
195
|
+
wake: tell.state === "told" || tell.binding !== undefined
|
|
196
|
+
? { kind: "told" }
|
|
197
|
+
: latestBody !== null && latestBody.end === undefined && latestBody.hung === undefined
|
|
198
|
+
? { kind: "pursuing", bodySequence: latestBody.sequence }
|
|
199
|
+
: { kind: "held" },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/** Observe the exact admitted Tell's bound Turn without substituting Akuma-wide idleness. */
|
|
203
|
+
async tellOutcome(tellId, options = {}) {
|
|
204
|
+
const waited = await waitForObservation({
|
|
205
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
206
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
207
|
+
observe: async () => {
|
|
208
|
+
const tell = await readTell(this.paths, tellId);
|
|
209
|
+
if (tell === null)
|
|
210
|
+
throw new AkumaProviderError(`recorded Tell ${tellId} is missing from Heart`);
|
|
211
|
+
const outcome = await this.boundTellOutcome(tell);
|
|
212
|
+
return { outcome, terminalWithoutTurn: tell.state === "told" && tell.binding === undefined };
|
|
213
|
+
},
|
|
214
|
+
complete: (observed) => observed.outcome !== null || observed.terminalWithoutTurn,
|
|
215
|
+
});
|
|
216
|
+
return { reason: waited.reason, outcome: waited.value.outcome };
|
|
217
|
+
}
|
|
218
|
+
async boundTellOutcome(tell) {
|
|
219
|
+
if (tell.binding === undefined)
|
|
220
|
+
return null;
|
|
221
|
+
const turn = await readTurn(this.paths, tell.binding.turnSequence);
|
|
222
|
+
return turn?.end?.outcome ?? null;
|
|
166
223
|
}
|
|
167
224
|
async interrupt(body, options = {}) {
|
|
225
|
+
const admitted = await this.admitInterrupt(body, options);
|
|
226
|
+
if (admitted.kind === "unavailable")
|
|
227
|
+
return admitted;
|
|
228
|
+
return { kind: "interrupted", putDown: admitted.putDown, tell: await admitted.wake };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Settle the predecessor and record the interrupt Tell, returning at the
|
|
232
|
+
* admission boundary so a bounded caller owns the wake wait.
|
|
233
|
+
*/
|
|
234
|
+
async admitInterrupt(body, options = {}) {
|
|
168
235
|
const request = await requestPause(this.paths, new Date().toISOString(), options.signal);
|
|
169
236
|
if (request.kind === "not-born") {
|
|
170
237
|
throw new AkumaNotBornError(this.id);
|
|
@@ -204,15 +271,16 @@ export class AkumaHandle {
|
|
|
204
271
|
});
|
|
205
272
|
if (admitted.kind === "not-born")
|
|
206
273
|
throw new AkumaNotBornError(this.id);
|
|
207
|
-
recorded =
|
|
274
|
+
recorded = admitted.tell;
|
|
208
275
|
}
|
|
209
276
|
finally {
|
|
210
277
|
leash.release();
|
|
211
278
|
}
|
|
212
279
|
return {
|
|
213
|
-
kind: "
|
|
280
|
+
kind: "admitted",
|
|
281
|
+
tell: recorded,
|
|
214
282
|
putDown,
|
|
215
|
-
|
|
283
|
+
wake: wakeRecordedTell(this.paths, recorded.id, options.runtime, options.signal),
|
|
216
284
|
};
|
|
217
285
|
}
|
|
218
286
|
async fork(input) {
|
|
@@ -6,7 +6,7 @@ import { AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
|
|
6
6
|
import { AkumaHandle } from "./akuma-handle.js";
|
|
7
7
|
import { bornStatus, defaultWaitComplete, waitForObservation } from "./akuma-observe.js";
|
|
8
8
|
import { loadArchetype } from "./archetype.js";
|
|
9
|
-
import { activitySlice
|
|
9
|
+
import { activitySlice } from "./heart/index.js";
|
|
10
10
|
import { parseAkuId, pathsForAkuId } from "./identity.js";
|
|
11
11
|
import { birthAkuma, launchAkuma } from "./publication.js";
|
|
12
12
|
import { projectTurns, selectHistory } from "./projection.js";
|
|
@@ -72,26 +72,10 @@ function outcomeError(outcome) {
|
|
|
72
72
|
throw new AkumaProviderError(outcome.diagnostic);
|
|
73
73
|
throw new AkumaProviderError("Akuma answered without a value");
|
|
74
74
|
}
|
|
75
|
-
async function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return turn?.end?.outcome ?? null;
|
|
80
|
-
}
|
|
81
|
-
async function awaitTellOutcome(paths, tellId, signal) {
|
|
82
|
-
const waited = await waitForObservation({
|
|
83
|
-
...(signal === undefined ? {} : { signal }),
|
|
84
|
-
observe: async () => {
|
|
85
|
-
const tell = await readTell(paths, tellId);
|
|
86
|
-
if (tell === null)
|
|
87
|
-
throw new AkumaProviderError(`recorded Tell ${tellId} is missing from Heart`);
|
|
88
|
-
const outcome = await boundOutcome(paths, tell);
|
|
89
|
-
return { outcome, terminalWithoutTurn: tell.state === "told" && tell.binding === undefined };
|
|
90
|
-
},
|
|
91
|
-
complete: (observed) => observed.outcome !== null || observed.terminalWithoutTurn,
|
|
92
|
-
});
|
|
93
|
-
if (waited.value.outcome !== null)
|
|
94
|
-
return waited.value.outcome;
|
|
75
|
+
async function awaitTellOutcome(handle, tellId, signal) {
|
|
76
|
+
const observed = await handle.tellOutcome(tellId, signal === undefined ? {} : { signal });
|
|
77
|
+
if (observed.outcome !== null)
|
|
78
|
+
return observed.outcome;
|
|
95
79
|
throw new AkumaProviderError(`recorded Tell ${tellId} reached a terminal delivery without a Turn binding`);
|
|
96
80
|
}
|
|
97
81
|
export class Akuma {
|
|
@@ -172,7 +156,7 @@ export class Akuma {
|
|
|
172
156
|
...(schemaOptions.initiator === undefined ? {} : { initiator: schemaOptions.initiator }),
|
|
173
157
|
...(signal === undefined ? {} : { signal }),
|
|
174
158
|
});
|
|
175
|
-
const outcome = await awaitTellOutcome(this.
|
|
159
|
+
const outcome = await awaitTellOutcome(new AkumaHandle(this.id, this.root), recorded.tellId, signal);
|
|
176
160
|
if (outcome.kind !== "answered")
|
|
177
161
|
outcomeError(outcome);
|
|
178
162
|
if (schema === undefined)
|
|
@@ -1,3 +1,22 @@
|
|
|
1
1
|
import { type AkuId } from "./identity.js";
|
|
2
2
|
import type { WorldRoot } from "../world.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* How one Akuma coordinate stands in a World, decided in the single place that
|
|
5
|
+
* reads a Heart for addressing. Absence and unreadability are different facts,
|
|
6
|
+
* and each consumer below maps them to its own caller terms.
|
|
7
|
+
*/
|
|
8
|
+
export type AkumaAddressability = Readonly<{
|
|
9
|
+
kind: "born";
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: "absent";
|
|
12
|
+
}> | Readonly<{
|
|
13
|
+
kind: "unreadable";
|
|
14
|
+
reason: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function akumaAddressability(worldPath: WorldRoot, id: AkuId): Promise<AkumaAddressability>;
|
|
17
|
+
/**
|
|
18
|
+
* The one normalization of addressability into caller terms: an Akuma whose
|
|
19
|
+
* Heart is absent was never born, and one whose Heart cannot be read is an
|
|
20
|
+
* observation failure that keeps its reason with the identity.
|
|
21
|
+
*/
|
|
22
|
+
export declare function requireBornAkuma(worldPath: WorldRoot, id: AkuId): Promise<void>;
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
import { AkumaNotBornError, AkumaObservationError } from "./akuma-errors.js";
|
|
1
2
|
import { readSoul } from "./heart/index.js";
|
|
2
3
|
import { pathsForAkuId } from "./identity.js";
|
|
3
|
-
export async function
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
export async function akumaAddressability(worldPath, id) {
|
|
5
|
+
try {
|
|
6
|
+
return (await readSoul(pathsForAkuId(worldPath, id))) === null ? { kind: "absent" } : { kind: "born" };
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
return { kind: "unreadable", reason: error instanceof Error ? error.message : String(error) };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The one normalization of addressability into caller terms: an Akuma whose
|
|
14
|
+
* Heart is absent was never born, and one whose Heart cannot be read is an
|
|
15
|
+
* observation failure that keeps its reason with the identity.
|
|
16
|
+
*/
|
|
17
|
+
export async function requireBornAkuma(worldPath, id) {
|
|
18
|
+
const addressability = await akumaAddressability(worldPath, id);
|
|
19
|
+
if (addressability.kind === "absent")
|
|
20
|
+
throw new AkumaNotBornError(id);
|
|
21
|
+
if (addressability.kind === "unreadable")
|
|
22
|
+
throw new AkumaObservationError(id, addressability.reason);
|
|
6
23
|
}
|
|
@@ -207,6 +207,10 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
207
207
|
}, z.core.$strict>, z.ZodObject<{
|
|
208
208
|
kind: z.ZodLiteral<"other">;
|
|
209
209
|
display: z.ZodString;
|
|
210
|
+
input: z.ZodOptional<z.ZodObject<{
|
|
211
|
+
json: z.ZodString;
|
|
212
|
+
truncated: z.ZodBoolean;
|
|
213
|
+
}, z.core.$strict>>;
|
|
210
214
|
}, z.core.$strict>]>;
|
|
211
215
|
state: z.ZodLiteral<"active">;
|
|
212
216
|
truncated: z.ZodOptional<z.ZodLiteral<true>>;
|
|
@@ -254,6 +258,10 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
254
258
|
}, z.core.$strict>, z.ZodObject<{
|
|
255
259
|
kind: z.ZodLiteral<"other">;
|
|
256
260
|
display: z.ZodString;
|
|
261
|
+
input: z.ZodOptional<z.ZodObject<{
|
|
262
|
+
json: z.ZodString;
|
|
263
|
+
truncated: z.ZodBoolean;
|
|
264
|
+
}, z.core.$strict>>;
|
|
257
265
|
}, z.core.$strict>]>;
|
|
258
266
|
state: z.ZodObject<{
|
|
259
267
|
status: z.ZodEnum<{
|
|
@@ -409,6 +417,10 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
409
417
|
}, z.core.$strict>, z.ZodObject<{
|
|
410
418
|
kind: z.ZodLiteral<"other">;
|
|
411
419
|
display: z.ZodString;
|
|
420
|
+
input: z.ZodOptional<z.ZodObject<{
|
|
421
|
+
json: z.ZodString;
|
|
422
|
+
truncated: z.ZodBoolean;
|
|
423
|
+
}, z.core.$strict>>;
|
|
412
424
|
}, z.core.$strict>]>;
|
|
413
425
|
state: z.ZodObject<{
|
|
414
426
|
status: z.ZodEnum<{
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.audit", "contract.deliver", "contract.review", "task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
|
|
3
3
|
export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
|
|
4
4
|
export type AllowedActions = readonly AllowedAction[];
|
|
5
|
+
export declare const DEFAULT_ALLOWED_ACTIONS: AllowedActions;
|
|
5
6
|
export declare function isAllowedAction(value: unknown): value is AllowedAction;
|
|
6
7
|
export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
|
|
7
8
|
/** Public codecs retain the Soul's canonical, duplicate-free action membership. */
|
|
@@ -23,6 +24,6 @@ export declare const allowedActionsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
|
23
24
|
"contract.deliver": "contract.deliver";
|
|
24
25
|
"contract.review": "contract.review";
|
|
25
26
|
}>>, z.ZodTransform<AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
|
|
26
|
-
export declare function
|
|
27
|
+
export declare function historicalAllowedActions(value: unknown): AllowedActions;
|
|
27
28
|
export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
|
|
28
29
|
export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { TASK_MUTATION_ACTIONS } from "../task/mutation.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
const AKUMA_MUTATION_ACTIONS = Object.freeze(["akuma.call", "akuma.kill", "akuma.tell"]);
|
|
3
4
|
export const ALLOWED_ACTIONS = Object.freeze([
|
|
4
|
-
|
|
5
|
-
"akuma.kill",
|
|
6
|
-
"akuma.tell",
|
|
5
|
+
...AKUMA_MUTATION_ACTIONS,
|
|
7
6
|
"contract.audit",
|
|
8
7
|
"contract.deliver",
|
|
9
8
|
"contract.review",
|
|
10
9
|
...TASK_MUTATION_ACTIONS,
|
|
11
10
|
]);
|
|
11
|
+
export const DEFAULT_ALLOWED_ACTIONS = Object.freeze([
|
|
12
|
+
...AKUMA_MUTATION_ACTIONS,
|
|
13
|
+
"contract.audit",
|
|
14
|
+
"contract.deliver",
|
|
15
|
+
...TASK_MUTATION_ACTIONS,
|
|
16
|
+
]);
|
|
12
17
|
const ALLOWED_ACTION_SET = new Set(ALLOWED_ACTIONS);
|
|
13
18
|
export function isAllowedAction(value) {
|
|
14
19
|
return typeof value === "string" && ALLOWED_ACTION_SET.has(value);
|
|
@@ -36,7 +41,7 @@ export const allowedActionsSchema = z.array(z.enum(ALLOWED_ACTIONS)).transform((
|
|
|
36
41
|
return z.NEVER;
|
|
37
42
|
}
|
|
38
43
|
});
|
|
39
|
-
export function
|
|
44
|
+
export function historicalAllowedActions(value) {
|
|
40
45
|
return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
|
|
41
46
|
}
|
|
42
47
|
export function unionAllowedActions(base, additions) {
|
|
@@ -5,7 +5,7 @@ import { parseDocument } from "yaml";
|
|
|
5
5
|
import { archetypeName } from "./identity.js";
|
|
6
6
|
import { decodeProviderOptions, decodeProviderRecipe, } from "./provider-recipe.js";
|
|
7
7
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
8
|
-
import {
|
|
8
|
+
import { DEFAULT_ALLOWED_ACTIONS, decodeAllowedActions } from "./allowed.js";
|
|
9
9
|
export class AkumaArchetypeError extends Error {
|
|
10
10
|
archetype;
|
|
11
11
|
searched;
|
|
@@ -135,7 +135,7 @@ function decodeArchetypeFields(values) {
|
|
|
135
135
|
const sandbox = archetypeEnum(values, "sandbox", ["full-access"]);
|
|
136
136
|
const description = archetypeField(values, "description");
|
|
137
137
|
const allowedPresent = "allowed" in values;
|
|
138
|
-
const allowed = allowedPresent ?
|
|
138
|
+
const allowed = allowedPresent ? decodeAllowedActions(values.allowed) : undefined;
|
|
139
139
|
const systemPromptMode = archetypeEnum(values, "systemPromptMode", ["append", "replace"]);
|
|
140
140
|
return {
|
|
141
141
|
...(base === undefined ? {} : { base }),
|
|
@@ -186,7 +186,7 @@ function mergeArchetype(base, local) {
|
|
|
186
186
|
if (provider === undefined)
|
|
187
187
|
throw new TypeError("Akuma provider must be a nonblank string");
|
|
188
188
|
const options = decodeProviderOptions({ ...(base?.options ?? {}), ...local.options });
|
|
189
|
-
const allowed = local.allowedPresent ? local.allowed : (base?.allowed ??
|
|
189
|
+
const allowed = local.allowedPresent ? local.allowed : (base?.allowed ?? DEFAULT_ALLOWED_ACTIONS);
|
|
190
190
|
return Object.freeze({
|
|
191
191
|
name: local.name,
|
|
192
192
|
path: local.path,
|
package/build/src/akuma/body.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { abortable, abortableDelay } from "./abort.js";
|
|
5
5
|
import { BodySupervisor } from "./body-supervisor.js";
|
|
6
6
|
import { driveTurn, turnRecipe } from "./turn-drive.js";
|
|
7
|
-
import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, readHeart, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readNonterminalRequests, resolvePendingTellDisposition, } from "./heart/index.js";
|
|
7
|
+
import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, readHeart, readLatestTurnForBody, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readNonterminalRequests, resolvePendingTellDisposition, } from "./heart/index.js";
|
|
8
8
|
import { worldRootForAkumaPaths } from "./identity.js";
|
|
9
9
|
import { akumaExecutionEnvironment } from "./providers/execution-environment.js";
|
|
10
10
|
import { pluginRuntime } from "../plugin/runtime.js";
|
|
@@ -291,7 +291,9 @@ function turnOutcomeEmitter(launch, soul) {
|
|
|
291
291
|
return {
|
|
292
292
|
async emit(turnSequence, outcome) {
|
|
293
293
|
try {
|
|
294
|
-
const
|
|
294
|
+
const turn = await readTurn(launch.paths, turnSequence);
|
|
295
|
+
if (turn === null)
|
|
296
|
+
throw new Error(`Committed Turn ${turnSequence} is missing from Heart`);
|
|
295
297
|
plugins ??= pluginRuntime({
|
|
296
298
|
world: await World.at(worldRootForAkumaPaths(launch.paths)),
|
|
297
299
|
reportDiagnostic,
|
|
@@ -299,8 +301,9 @@ function turnOutcomeEmitter(launch, soul) {
|
|
|
299
301
|
await (await plugins).emit({
|
|
300
302
|
kind: "akuma.turn-outcome",
|
|
301
303
|
akumaId: soul.id,
|
|
304
|
+
bodySequence: turn.bodySequence,
|
|
302
305
|
turnSequence,
|
|
303
|
-
...(initiator === undefined ? {} : { initiator }),
|
|
306
|
+
...(turn.initiator === undefined ? {} : { initiator: turn.initiator }),
|
|
304
307
|
outcome: outcome.outcome === "answered"
|
|
305
308
|
? { kind: "answered", text: outcome.answer }
|
|
306
309
|
: { kind: "failed", reason: outcome.diagnostic },
|
|
@@ -320,6 +323,7 @@ function bodyEndEmitter(launch, soul) {
|
|
|
320
323
|
};
|
|
321
324
|
return (bodySequence, end, diagnostic) => (async () => {
|
|
322
325
|
try {
|
|
326
|
+
const initiator = (await readLatestTurnForBody(launch.paths, bodySequence))?.initiator;
|
|
323
327
|
plugins ??= pluginRuntime({
|
|
324
328
|
world: await World.at(worldRootForAkumaPaths(launch.paths)),
|
|
325
329
|
reportDiagnostic,
|
|
@@ -330,6 +334,7 @@ function bodyEndEmitter(launch, soul) {
|
|
|
330
334
|
akumaId: soul.id,
|
|
331
335
|
bodySequence,
|
|
332
336
|
end,
|
|
337
|
+
...(initiator === undefined ? {} : { initiator }),
|
|
333
338
|
...(diagnostic === undefined ? {} : { diagnostic }),
|
|
334
339
|
...(launch.completion?.contractId === undefined ? {} : { contractId: launch.completion.contractId }),
|
|
335
340
|
}, reportDiagnostic);
|
|
@@ -3,7 +3,7 @@ import type { ActivityRow } from "./projection.js";
|
|
|
3
3
|
import { type DispatchAssociation } from "./dispatch-association.js";
|
|
4
4
|
import type { AkumaAlias } from "../identity/selector.js";
|
|
5
5
|
import type { WorldRoot } from "../world.js";
|
|
6
|
-
import { type AkumaKillResult, type AkumaTellResult, type AkumaWaitResult } from "./fleet-observation.js";
|
|
6
|
+
import { type AkumaKillResult, type AkumaTellResult, type AkumaTellWaitResult, type AkumaWaitResult } from "./fleet-observation.js";
|
|
7
7
|
export type WaitIdentityFacts = Readonly<{
|
|
8
8
|
/** The alias currently addressing this Akuma in its world, when one is bound to it. */
|
|
9
9
|
alias?: AkumaAlias;
|
|
@@ -50,6 +50,11 @@ export type TellExecutionInput = Readonly<{
|
|
|
50
50
|
signal?: AbortSignal;
|
|
51
51
|
}>;
|
|
52
52
|
export declare function executeTellAkuma(input: TellExecutionInput): Promise<AkumaTellResult>;
|
|
53
|
+
export declare function executeTellWaitAkuma(input: TellExecutionInput & Readonly<{
|
|
54
|
+
timeoutMs: number;
|
|
55
|
+
schemaJson?: string;
|
|
56
|
+
interrupt?: boolean;
|
|
57
|
+
}>): Promise<AkumaTellWaitResult>;
|
|
53
58
|
export type KillExecutionInput = Readonly<{
|
|
54
59
|
path: WorldRoot;
|
|
55
60
|
ids: readonly AkumaStatus["id"][];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
|
1
2
|
import { AkumaNotBornError, defaultWaitComplete } from "./akuma.js";
|
|
2
3
|
import { createAkumaProduct } from "./akuma-product.js";
|
|
3
4
|
import { readLiveStatus, waitForObservation } from "./akuma-observe.js";
|
|
@@ -125,6 +126,49 @@ export async function executeTellAkuma(input) {
|
|
|
125
126
|
tell,
|
|
126
127
|
});
|
|
127
128
|
}
|
|
129
|
+
export async function executeTellWaitAkuma(input) {
|
|
130
|
+
input.signal?.throwIfAborted();
|
|
131
|
+
const handle = source(input.path).selectHandle({ id: input.id });
|
|
132
|
+
const admission = input.interrupt === true
|
|
133
|
+
? await handle.admitInterrupt(input.body, {
|
|
134
|
+
...(input.tellId === undefined ? {} : { tellId: input.tellId }),
|
|
135
|
+
...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
|
|
136
|
+
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
137
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
138
|
+
})
|
|
139
|
+
: await handle.admitTell(input.body, input.tellId, input.recordedAt, undefined, {
|
|
140
|
+
...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
|
|
141
|
+
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
142
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
143
|
+
});
|
|
144
|
+
if (admission.kind === "unavailable")
|
|
145
|
+
throw new AkumaProviderError(`Tell interrupt unavailable: ${admission.evidence}`);
|
|
146
|
+
if (admission.kind === "not-born")
|
|
147
|
+
throw new AkumaNotBornError(input.id);
|
|
148
|
+
// The wake runs behind the window; a settled wake is preferred, otherwise the
|
|
149
|
+
// receipt reports the admitted Tell as it stands when the window closes.
|
|
150
|
+
let settled;
|
|
151
|
+
void admission.wake.then((receipt) => {
|
|
152
|
+
settled = receipt;
|
|
153
|
+
}, () => undefined);
|
|
154
|
+
const observed = await handle.tellOutcome(admission.tell.id, {
|
|
155
|
+
timeoutMs: input.timeoutMs,
|
|
156
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
157
|
+
});
|
|
158
|
+
const observation = observed.outcome === null
|
|
159
|
+
? observed.reason === "deadline"
|
|
160
|
+
? { reason: "deadline" }
|
|
161
|
+
: { reason: "unanswered" }
|
|
162
|
+
: observed.outcome.kind === "answered"
|
|
163
|
+
? { reason: "answered", answer: observed.outcome.answerJson ?? observed.outcome.answer }
|
|
164
|
+
: observed.outcome.kind === "failed"
|
|
165
|
+
? { reason: "failed", diagnostic: observed.outcome.diagnostic }
|
|
166
|
+
: (() => {
|
|
167
|
+
throw new AkumaDecodeError(observed.outcome.diagnostic, observed.outcome.answer);
|
|
168
|
+
})();
|
|
169
|
+
const tell = settled ?? (await handle.admittedReceipt(admission.tell.id));
|
|
170
|
+
return fleetResultSchemas.tellWait.parse({ akuma: input.id, tell, observation });
|
|
171
|
+
}
|
|
128
172
|
export async function executeKillAkuma(input) {
|
|
129
173
|
input.signal?.throwIfAborted();
|
|
130
174
|
const handles = input.ids.map((id) => source(input.path).selectHandle({ id }));
|