@astrosheep/keiyaku 4.5.24 → 4.5.25
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/src/akuma/akuma-handle.d.ts +10 -0
- package/build/src/akuma/akuma-handle.js +18 -18
- package/build/src/akuma/akuma-instance.d.ts +5 -10
- package/build/src/akuma/akuma-instance.js +55 -40
- package/build/src/akuma/akuma-observe.d.ts +40 -7
- package/build/src/akuma/akuma-observe.js +51 -16
- package/build/src/akuma/akuma-product.d.ts +1 -2
- package/build/src/akuma/akuma-product.js +8 -5
- package/build/src/akuma/akuma.d.ts +2 -0
- package/build/src/akuma/body.d.ts +1 -1
- package/build/src/akuma/body.js +48 -30
- package/build/src/akuma/call-request.d.ts +0 -1
- package/build/src/akuma/call-request.js +1 -2
- package/build/src/akuma/fleet-execution.d.ts +3 -1
- package/build/src/akuma/fleet-execution.js +30 -71
- package/build/src/akuma/fleet-observation.d.ts +13 -2
- package/build/src/akuma/fleet-observation.js +2 -1
- package/build/src/akuma/fleet-request.js +3 -2
- package/build/src/akuma/heart/timeline.d.ts +1 -1
- package/build/src/akuma/heart/timeline.js +5 -5
- package/build/src/akuma/projection-read.js +32 -7
- package/build/src/akuma/projection.d.ts +1 -0
- package/build/src/akuma/projection.js +24 -9
- package/build/src/akuma/providers/pi/index.d.ts +2 -1
- package/build/src/akuma/providers/pi/index.js +2 -1
- package/build/src/akuma/publication.js +7 -4
- package/build/src/akuma-body.js +1 -0
- package/build/src/cli/commands/akuma-invoke.d.ts +1 -0
- package/build/src/cli/commands/akuma-invoke.js +16 -3
- package/build/src/cli/invoke.js +1 -0
- package/build/src/cli/render/akuma-activity.d.ts +13 -5
- package/build/src/cli/render/akuma-activity.js +105 -105
- package/build/src/library/address.d.ts +1 -1
- package/build/src/library/address.js +10 -8
- package/build/src/library/akuma-creation.d.ts +3 -0
- package/build/src/library/akuma-creation.js +111 -46
- package/build/src/library/composition.d.ts +3 -1
- package/build/src/library/fleet.d.ts +7 -1
- package/build/src/library/fleet.js +35 -9
- package/build/src/library/keiyaku.d.ts +3 -1
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { type TellResult, type TellWakeRuntime } from "./body.js";
|
|
|
2
2
|
import { HeldAkumaLeash, type KillEvidence } 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
|
+
import { type WaitReason } from "./akuma-observe.js";
|
|
5
6
|
import type { AkumaCallExecution, AkumaStatus, ForkReceipt, InterruptReceipt } from "./akuma.js";
|
|
6
7
|
import type { WorldRoot } from "../world.js";
|
|
7
8
|
declare const CALL_EXECUTION: unique symbol;
|
|
@@ -25,12 +26,21 @@ export declare class AkumaHandle {
|
|
|
25
26
|
since?: number;
|
|
26
27
|
limit?: number;
|
|
27
28
|
}>): Promise<ActivityHistory | ExactHistory>;
|
|
29
|
+
waitReceipt(predicate?: (status: AkumaStatus) => boolean, options?: Readonly<{
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}>): Promise<Readonly<{
|
|
33
|
+
reason: WaitReason;
|
|
34
|
+
status: AkumaStatus;
|
|
35
|
+
}>>;
|
|
28
36
|
wait(predicate?: (status: AkumaStatus) => boolean, options?: Readonly<{
|
|
29
37
|
timeoutMs?: number;
|
|
38
|
+
signal?: AbortSignal;
|
|
30
39
|
}>): Promise<AkumaStatus>;
|
|
31
40
|
tell(body: string, tellId?: string, recordedAt?: string, runtime?: TellWakeRuntime, options?: Readonly<{
|
|
32
41
|
schemaJson?: string;
|
|
33
42
|
initiator?: string;
|
|
43
|
+
signal?: AbortSignal;
|
|
34
44
|
}>): Promise<TellResult>;
|
|
35
45
|
interrupt(body: string, options?: Readonly<{
|
|
36
46
|
tellId?: string;
|
|
@@ -8,10 +8,8 @@ import { resolveProviderExecution } from "./providers/index.js";
|
|
|
8
8
|
import { publishAkuma } from "./publication.js";
|
|
9
9
|
import { spawnAkumaBody } from "./body.js";
|
|
10
10
|
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
11
|
-
import { bornStatus, defaultWaitComplete, readWaitComplete } from "./akuma-observe.js";
|
|
11
|
+
import { bornStatus, defaultWaitComplete, readWaitComplete, waitForObservation, } from "./akuma-observe.js";
|
|
12
12
|
const CALL_EXECUTION = Symbol("akuma-call-execution");
|
|
13
|
-
const POLL_MS = 100;
|
|
14
|
-
const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
15
13
|
function diagnostic(error) {
|
|
16
14
|
return error instanceof Error ? error.message : String(error);
|
|
17
15
|
}
|
|
@@ -134,24 +132,26 @@ export class AkumaHandle {
|
|
|
134
132
|
limit,
|
|
135
133
|
});
|
|
136
134
|
}
|
|
137
|
-
async
|
|
135
|
+
async waitReceipt(predicate = defaultWaitComplete, options = {}) {
|
|
138
136
|
if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
|
|
139
137
|
throw new TypeError("Akuma wait timeoutMs must be a nonnegative finite millisecond duration");
|
|
140
138
|
}
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
(await readWaitComplete(this.worldPath, this.id)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
const waited = await waitForObservation({
|
|
140
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
141
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
142
|
+
...(predicate === defaultWaitComplete
|
|
143
|
+
? { probe: async () => await readWaitComplete(this.worldPath, this.id) }
|
|
144
|
+
: {}),
|
|
145
|
+
observe: async () => await this.status(),
|
|
146
|
+
complete: predicate,
|
|
147
|
+
});
|
|
148
|
+
return { reason: waited.reason, status: waited.value };
|
|
149
|
+
}
|
|
150
|
+
async wait(predicate = defaultWaitComplete, options = {}) {
|
|
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 { schemaJson, initiator } = options;
|
|
154
|
+
const { schemaJson, initiator, signal } = options;
|
|
155
155
|
const admitted = await recordTell(this.paths, {
|
|
156
156
|
kind: "tell",
|
|
157
157
|
id: tellId,
|
|
@@ -162,7 +162,7 @@ export class AkumaHandle {
|
|
|
162
162
|
});
|
|
163
163
|
if (admitted.kind === "not-born")
|
|
164
164
|
throw new AkumaNotBornError(this.id);
|
|
165
|
-
return await wakeRecordedTell(this.paths, admitted.tell.id, runtime);
|
|
165
|
+
return await wakeRecordedTell(this.paths, admitted.tell.id, runtime, signal);
|
|
166
166
|
}
|
|
167
167
|
async interrupt(body, options = {}) {
|
|
168
168
|
const request = await requestPause(this.paths, new Date().toISOString(), options.signal);
|
|
@@ -212,7 +212,7 @@ export class AkumaHandle {
|
|
|
212
212
|
return {
|
|
213
213
|
kind: "interrupted",
|
|
214
214
|
putDown,
|
|
215
|
-
tell: await wakeRecordedTell(this.paths, recorded.tellId, options.runtime),
|
|
215
|
+
tell: await wakeRecordedTell(this.paths, recorded.tellId, options.runtime, options.signal),
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
218
|
async fork(input) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AllowedAction } from "./allowed.js";
|
|
2
2
|
import type { AkumaStatus } from "./akuma.js";
|
|
3
3
|
import type { InterruptReceipt, KillEvidence } from "./akuma.js";
|
|
4
|
+
import { type WaitReason } from "./akuma-observe.js";
|
|
4
5
|
import { type AkuId } from "./identity.js";
|
|
5
6
|
import { type ActivityHistory } from "./projection.js";
|
|
6
7
|
import type { Settings } from "../settings.js";
|
|
@@ -8,18 +9,11 @@ import type { WorldRoot } from "../world.js";
|
|
|
8
9
|
import { type Schema, type StandardSchemaV1 } from "./schema.js";
|
|
9
10
|
export type AkumaIdleOptions = Readonly<{
|
|
10
11
|
timeoutMs?: number;
|
|
12
|
+
signal?: AbortSignal;
|
|
11
13
|
}>;
|
|
12
14
|
export type AkumaIdleResult = Readonly<{
|
|
13
|
-
|
|
14
|
-
status: AkumaStatus;
|
|
15
|
-
reason: Exclude<AkumaStatus["life"], "running">;
|
|
16
|
-
}> | Readonly<{
|
|
17
|
-
kind: "timeout";
|
|
15
|
+
reason: WaitReason;
|
|
18
16
|
status: AkumaStatus;
|
|
19
|
-
reason: Readonly<{
|
|
20
|
-
running: boolean;
|
|
21
|
-
pendingTell: boolean;
|
|
22
|
-
}>;
|
|
23
17
|
}>;
|
|
24
18
|
export type AkumaHistoryOptions = Readonly<{
|
|
25
19
|
before?: number;
|
|
@@ -51,8 +45,9 @@ export declare class Akuma {
|
|
|
51
45
|
static select(root: WorldRoot, selector: string): Akuma;
|
|
52
46
|
tell(text: string, options?: Readonly<{
|
|
53
47
|
initiator?: string;
|
|
48
|
+
signal?: AbortSignal;
|
|
54
49
|
}>): Promise<string>;
|
|
55
|
-
tell<T>(text: string, options: AkumaTellOptions<T>): Promise<T>;
|
|
50
|
+
tell<T>(text: string, options: AkumaTellOptions<T> & AkumaSignalOptions): Promise<T>;
|
|
56
51
|
status(): Promise<AkumaStatus>;
|
|
57
52
|
interrupt(text: string, options?: AkumaSignalOptions & Readonly<{
|
|
58
53
|
initiator?: string;
|
|
@@ -4,8 +4,7 @@ import { spawnAkumaBody } from "./body.js";
|
|
|
4
4
|
import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
|
|
5
5
|
import { AkumaDecodeError, AkumaProviderError } from "./akuma-errors.js";
|
|
6
6
|
import { AkumaHandle } from "./akuma-handle.js";
|
|
7
|
-
import {
|
|
8
|
-
import { bornStatus } from "./akuma-observe.js";
|
|
7
|
+
import { bornStatus, defaultWaitComplete, waitForObservation } from "./akuma-observe.js";
|
|
9
8
|
import { loadArchetype } from "./archetype.js";
|
|
10
9
|
import { activitySlice, readTell, readTurn } from "./heart/index.js";
|
|
11
10
|
import { parseAkuId, pathsForAkuId } from "./identity.js";
|
|
@@ -22,27 +21,29 @@ function signalOption(value) {
|
|
|
22
21
|
throw new TypeError("signal must be an AbortSignal");
|
|
23
22
|
return value;
|
|
24
23
|
}
|
|
25
|
-
function wait(milliseconds) {
|
|
26
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
27
|
-
}
|
|
28
|
-
function pendingTellOf(status) {
|
|
29
|
-
return status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending");
|
|
30
|
-
}
|
|
31
|
-
function idleResult(status) {
|
|
32
|
-
const pendingTell = pendingTellOf(status);
|
|
33
|
-
if (status.life !== "running" && !pendingTell)
|
|
34
|
-
return { kind: "idle", status, reason: status.life };
|
|
35
|
-
return { kind: "timeout", status, reason: { running: status.life === "running", pendingTell } };
|
|
36
|
-
}
|
|
37
24
|
function recordedTell(result) {
|
|
38
25
|
if (result.wake.kind === "failed")
|
|
39
26
|
throw new AkumaProviderError(result.wake.diagnostic);
|
|
40
27
|
return { tellId: result.admission.tellId };
|
|
41
28
|
}
|
|
42
|
-
async function recordPlainTell(
|
|
43
|
-
const
|
|
29
|
+
async function recordPlainTell(input) {
|
|
30
|
+
const { id, root, body, tellId, initiator, signal } = input;
|
|
31
|
+
const admitted = await new AkumaHandle(id, root).tell(body, tellId, undefined, undefined, {
|
|
32
|
+
...(initiator === undefined ? {} : { initiator }),
|
|
33
|
+
...(signal === undefined ? {} : { signal }),
|
|
34
|
+
});
|
|
44
35
|
return recordedTell(admitted);
|
|
45
36
|
}
|
|
37
|
+
function plainTellInput(input) {
|
|
38
|
+
return {
|
|
39
|
+
id: input.id,
|
|
40
|
+
root: input.root,
|
|
41
|
+
body: input.body,
|
|
42
|
+
tellId: input.tellId,
|
|
43
|
+
...(input.options?.initiator === undefined ? {} : { initiator: input.options.initiator }),
|
|
44
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
46
47
|
async function recordSchemaTell(input) {
|
|
47
48
|
const { id, body, tellId, schema, root } = input;
|
|
48
49
|
if (input.interrupt === true) {
|
|
@@ -50,6 +51,7 @@ async function recordSchemaTell(input) {
|
|
|
50
51
|
tellId,
|
|
51
52
|
schemaJson: schemaJsonText(schema),
|
|
52
53
|
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
54
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
53
55
|
});
|
|
54
56
|
if (interrupted.kind === "unavailable") {
|
|
55
57
|
throw new AkumaProviderError(`schema interrupt unavailable: ${interrupted.evidence}`);
|
|
@@ -59,6 +61,7 @@ async function recordSchemaTell(input) {
|
|
|
59
61
|
const admitted = await new AkumaHandle(id, root).tell(body, tellId, undefined, undefined, {
|
|
60
62
|
schemaJson: schemaJsonText(schema),
|
|
61
63
|
...(input.initiator === undefined ? {} : { initiator: input.initiator }),
|
|
64
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
62
65
|
});
|
|
63
66
|
return recordedTell(admitted);
|
|
64
67
|
}
|
|
@@ -75,19 +78,21 @@ async function boundOutcome(paths, tell) {
|
|
|
75
78
|
const turn = await readTurn(paths, tell.binding.turnSequence);
|
|
76
79
|
return turn?.end?.outcome ?? null;
|
|
77
80
|
}
|
|
78
|
-
async function awaitTellOutcome(paths, tellId) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
95
|
+
throw new AkumaProviderError(`recorded Tell ${tellId} reached a terminal delivery without a Turn binding`);
|
|
91
96
|
}
|
|
92
97
|
export class Akuma {
|
|
93
98
|
id;
|
|
@@ -143,11 +148,20 @@ export class Akuma {
|
|
|
143
148
|
async tell(text, options) {
|
|
144
149
|
if (typeof text !== "string")
|
|
145
150
|
throw new TypeError("Akuma tell text must be a string");
|
|
151
|
+
const signal = signalOption(options?.signal);
|
|
152
|
+
signal?.throwIfAborted();
|
|
146
153
|
const tellId = randomUUID();
|
|
147
154
|
const schemaOptions = options !== undefined && "schema" in options ? options : undefined;
|
|
148
155
|
const schema = schemaOptions === undefined ? undefined : schemaFromStandard(schemaOptions.schema);
|
|
149
156
|
const recorded = schemaOptions === undefined || schema === undefined
|
|
150
|
-
? await recordPlainTell(
|
|
157
|
+
? await recordPlainTell(plainTellInput({
|
|
158
|
+
id: this.id,
|
|
159
|
+
root: this.root,
|
|
160
|
+
body: text,
|
|
161
|
+
tellId,
|
|
162
|
+
options,
|
|
163
|
+
signal,
|
|
164
|
+
}))
|
|
151
165
|
: await recordSchemaTell({
|
|
152
166
|
id: this.id,
|
|
153
167
|
body: text,
|
|
@@ -156,8 +170,9 @@ export class Akuma {
|
|
|
156
170
|
root: this.root,
|
|
157
171
|
...(schemaOptions.interrupt === undefined ? {} : { interrupt: schemaOptions.interrupt }),
|
|
158
172
|
...(schemaOptions.initiator === undefined ? {} : { initiator: schemaOptions.initiator }),
|
|
173
|
+
...(signal === undefined ? {} : { signal }),
|
|
159
174
|
});
|
|
160
|
-
const outcome = await awaitTellOutcome(this.paths, recorded.tellId);
|
|
175
|
+
const outcome = await awaitTellOutcome(this.paths, recorded.tellId, signal);
|
|
161
176
|
if (outcome.kind !== "answered")
|
|
162
177
|
outcomeError(outcome);
|
|
163
178
|
if (schema === undefined)
|
|
@@ -198,20 +213,20 @@ export class Akuma {
|
|
|
198
213
|
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
199
214
|
throw new TypeError("Akuma idle options must be an object");
|
|
200
215
|
}
|
|
201
|
-
const unknown = Object.keys(options).find((key) => key !== "timeoutMs");
|
|
216
|
+
const unknown = Object.keys(options).find((key) => key !== "timeoutMs" && key !== "signal");
|
|
202
217
|
if (unknown !== undefined)
|
|
203
218
|
throw new TypeError(`Akuma idle options has unknown field: ${unknown}`);
|
|
204
219
|
if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
|
|
205
220
|
throw new TypeError("Akuma idle timeoutMs must be a nonnegative finite millisecond duration");
|
|
206
221
|
}
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
222
|
+
const signal = signalOption(options.signal);
|
|
223
|
+
const waited = await waitForObservation({
|
|
224
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
225
|
+
...(signal === undefined ? {} : { signal }),
|
|
226
|
+
observe: async () => (await bornStatus(this.paths, this.id, { aperture: "monitoring" })).status,
|
|
227
|
+
complete: defaultWaitComplete,
|
|
228
|
+
});
|
|
229
|
+
return { reason: waited.reason, status: waited.value };
|
|
215
230
|
}
|
|
216
231
|
async history(options = {}) {
|
|
217
232
|
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AkuId, type AkumaPaths } from "./identity.js";
|
|
2
|
-
import { selectHistory, type ActivityHistory, type ActivitySnapshot } from "./projection.js";
|
|
2
|
+
import { selectHistory, type ActivityHistory, type ActivityRow, type ActivitySnapshot } from "./projection.js";
|
|
3
3
|
import type { WorldRoot } from "../world.js";
|
|
4
4
|
import type { AkumaListRow, AkumaStatus, UnbornAkumaListRow } from "./akuma.js";
|
|
5
5
|
export declare function fleetListRow(paths: AkumaPaths, expected: AkuId): Promise<AkumaListRow | UnbornAkumaListRow>;
|
|
@@ -7,6 +7,14 @@ export type BudgetedStatusObservation = Readonly<{
|
|
|
7
7
|
status: AkumaStatus;
|
|
8
8
|
ordinarySelected: number;
|
|
9
9
|
}>;
|
|
10
|
+
/**
|
|
11
|
+
* The internal live-observation companion for one status frontier. It is not
|
|
12
|
+
* part of status or any returned facade result: viewers receive it only while
|
|
13
|
+
* an observation loop is still streaming.
|
|
14
|
+
*/
|
|
15
|
+
export type LiveStatusObservation = BudgetedStatusObservation & Readonly<{
|
|
16
|
+
rows: readonly ActivityRow[];
|
|
17
|
+
}>;
|
|
10
18
|
export declare function bornStatus(paths: AkumaPaths, expected: AkuId, input: Readonly<{
|
|
11
19
|
aperture: "monitoring" | "receipt";
|
|
12
20
|
ordinaryBudget?: number;
|
|
@@ -17,18 +25,43 @@ export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, inpu
|
|
|
17
25
|
ordinaryBudget?: number;
|
|
18
26
|
admittedTellId?: string;
|
|
19
27
|
}>): Promise<BudgetedStatusObservation>;
|
|
28
|
+
export declare function readLiveStatus(worldPath: WorldRoot, id: AkuId, input: Readonly<{
|
|
29
|
+
aperture: "monitoring" | "receipt";
|
|
30
|
+
ordinaryBudget?: number;
|
|
31
|
+
admittedTellId?: string;
|
|
32
|
+
}>): Promise<LiveStatusObservation>;
|
|
20
33
|
export declare function defaultWaitComplete(status: AkumaStatus): boolean;
|
|
21
34
|
export declare function readWaitComplete(worldPath: WorldRoot, id: AkuId): Promise<boolean>;
|
|
35
|
+
export type WaitReason = "completed" | "deadline";
|
|
36
|
+
/**
|
|
37
|
+
* The one deadline-aware observation loop for public Akuma waiting. It always
|
|
38
|
+
* reads once before judging a passed deadline, so a zero-duration wait remains
|
|
39
|
+
* an honest final snapshot and completion wins at the deadline edge.
|
|
40
|
+
*/
|
|
41
|
+
export declare function waitForObservation<T>(input: Readonly<{
|
|
42
|
+
timeoutMs?: number;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
probe?(): Promise<boolean>;
|
|
45
|
+
observe(): Promise<T>;
|
|
46
|
+
complete(value: T): boolean;
|
|
47
|
+
onObserve?(value: T): void | Promise<void>;
|
|
48
|
+
}>): Promise<Readonly<{
|
|
49
|
+
reason: WaitReason;
|
|
50
|
+
value: T;
|
|
51
|
+
}>>;
|
|
22
52
|
/**
|
|
23
53
|
* Successive status observations of one already born Akuma until its
|
|
24
|
-
* observation window closes. Every
|
|
25
|
-
*
|
|
26
|
-
* already
|
|
27
|
-
* nothing and returns the current status.
|
|
54
|
+
* observation window closes. Every acquired observation is reported to
|
|
55
|
+
* `observe` before completion or deadline judgment, including the first
|
|
56
|
+
* already-complete observation; the last status is returned.
|
|
28
57
|
*/
|
|
29
58
|
export declare function observeAkumaStatus(worldPath: WorldRoot, expected: AkuId, input: Readonly<{
|
|
30
59
|
timeoutMs: number;
|
|
31
|
-
|
|
32
|
-
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
observe: (status: LiveStatusObservation) => void;
|
|
62
|
+
}>): Promise<Readonly<{
|
|
63
|
+
reason: WaitReason;
|
|
64
|
+
status: AkumaStatus;
|
|
65
|
+
}>>;
|
|
33
66
|
export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
|
|
34
67
|
export { selectHistory, type ActivityHistory, type ActivitySnapshot };
|
|
@@ -2,6 +2,7 @@ import { readStatusFacts, readLifeSnapshot, HeldAkumaLeash, isHeartAbsent, life,
|
|
|
2
2
|
import { pathsForAkuId } from "./identity.js";
|
|
3
3
|
import { ordinarySnapshotBudget, projectTurns, selectHistory, selectSnapshot, } from "./projection.js";
|
|
4
4
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
5
|
+
import { abortableDelay } from "./abort.js";
|
|
5
6
|
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
6
7
|
export async function fleetListRow(paths, expected) {
|
|
7
8
|
const snapshot = await readHeart(paths);
|
|
@@ -55,7 +56,7 @@ async function bornObservation(paths, expected, read, snapshot) {
|
|
|
55
56
|
claim?.release();
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
async function bornLiveStatus(paths, expected, input) {
|
|
59
60
|
if (input.ordinaryBudget !== undefined && (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0))
|
|
60
61
|
throw new TypeError("ordinary budget must be a nonnegative safe integer");
|
|
61
62
|
const { snapshot, soul, currentLife } = await bornObservation(paths, expected, () => readLifeSnapshot(paths));
|
|
@@ -63,7 +64,8 @@ export async function bornStatus(paths, expected, input) {
|
|
|
63
64
|
snapshot.latestSession?.provider === soul.provider.name &&
|
|
64
65
|
(await resolveProviderExecution(soul.provider)).adapter.resume === undefined;
|
|
65
66
|
const facts = await readStatusFacts(paths, input);
|
|
66
|
-
const
|
|
67
|
+
const ledger = projectTurns(facts);
|
|
68
|
+
const selected = selectSnapshot(ledger, {
|
|
67
69
|
aperture: input.aperture,
|
|
68
70
|
budget: ordinarySnapshotBudget(input.ordinaryBudget),
|
|
69
71
|
...(input.admittedTellId === undefined ? {} : { admittedTellId: input.admittedTellId }),
|
|
@@ -78,11 +80,19 @@ export async function bornStatus(paths, expected, input) {
|
|
|
78
80
|
timeline: selected.snapshot,
|
|
79
81
|
},
|
|
80
82
|
ordinarySelected: selected.ordinaryCount,
|
|
83
|
+
rows: ledger.rows,
|
|
81
84
|
};
|
|
82
85
|
}
|
|
86
|
+
export async function bornStatus(paths, expected, input) {
|
|
87
|
+
const { rows: _rows, ...status } = await bornLiveStatus(paths, expected, input);
|
|
88
|
+
return status;
|
|
89
|
+
}
|
|
83
90
|
export async function readBudgetedStatus(worldPath, id, input) {
|
|
84
91
|
return await bornStatus(pathsForAkuId(worldPath, id), id, input);
|
|
85
92
|
}
|
|
93
|
+
export async function readLiveStatus(worldPath, id, input) {
|
|
94
|
+
return await bornLiveStatus(pathsForAkuId(worldPath, id), id, input);
|
|
95
|
+
}
|
|
86
96
|
function complete(life, pending) {
|
|
87
97
|
return life !== "running" && !pending;
|
|
88
98
|
}
|
|
@@ -95,26 +105,51 @@ export async function readWaitComplete(worldPath, id) {
|
|
|
95
105
|
return complete(observed.currentLife, observed.snapshot.hasPendingTell);
|
|
96
106
|
}
|
|
97
107
|
const OBSERVATION_POLL_MS = 100;
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The one deadline-aware observation loop for public Akuma waiting. It always
|
|
110
|
+
* reads once before judging a passed deadline, so a zero-duration wait remains
|
|
111
|
+
* an honest final snapshot and completion wins at the deadline edge.
|
|
112
|
+
*/
|
|
113
|
+
export async function waitForObservation(input) {
|
|
114
|
+
const deadline = input.timeoutMs === undefined ? undefined : performance.now() + input.timeoutMs;
|
|
115
|
+
for (;;) {
|
|
116
|
+
input.signal?.throwIfAborted();
|
|
117
|
+
const ready = input.probe === undefined || (await input.probe());
|
|
118
|
+
input.signal?.throwIfAborted();
|
|
119
|
+
if (!ready && (deadline === undefined || performance.now() < deadline)) {
|
|
120
|
+
await abortableDelay(deadline === undefined
|
|
121
|
+
? OBSERVATION_POLL_MS
|
|
122
|
+
: Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - performance.now())), input.signal);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const value = await input.observe();
|
|
126
|
+
input.signal?.throwIfAborted();
|
|
127
|
+
await input.onObserve?.(value);
|
|
128
|
+
if (input.complete(value))
|
|
129
|
+
return { reason: "completed", value };
|
|
130
|
+
if (deadline !== undefined && performance.now() >= deadline)
|
|
131
|
+
return { reason: "deadline", value };
|
|
132
|
+
await abortableDelay(deadline === undefined
|
|
133
|
+
? OBSERVATION_POLL_MS
|
|
134
|
+
: Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - performance.now())), input.signal);
|
|
135
|
+
}
|
|
100
136
|
}
|
|
101
137
|
/**
|
|
102
138
|
* Successive status observations of one already born Akuma until its
|
|
103
|
-
* observation window closes. Every
|
|
104
|
-
*
|
|
105
|
-
* already
|
|
106
|
-
* nothing and returns the current status.
|
|
139
|
+
* observation window closes. Every acquired observation is reported to
|
|
140
|
+
* `observe` before completion or deadline judgment, including the first
|
|
141
|
+
* already-complete observation; the last status is returned.
|
|
107
142
|
*/
|
|
108
143
|
export async function observeAkumaStatus(worldPath, expected, input) {
|
|
109
144
|
const paths = pathsForAkuId(worldPath, expected);
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
input.observe
|
|
116
|
-
|
|
117
|
-
}
|
|
145
|
+
const waited = await waitForObservation({
|
|
146
|
+
timeoutMs: input.timeoutMs,
|
|
147
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
148
|
+
observe: async () => await bornLiveStatus(paths, expected, { aperture: "monitoring" }),
|
|
149
|
+
complete: (observation) => defaultWaitComplete(observation.status),
|
|
150
|
+
onObserve: input.observe,
|
|
151
|
+
});
|
|
152
|
+
return { reason: waited.reason, status: waited.value.status };
|
|
118
153
|
}
|
|
119
154
|
export async function readAkumaBirthCwd(worldPath, id) {
|
|
120
155
|
const soul = await readSoul(pathsForAkuId(worldPath, id));
|
|
@@ -23,7 +23,6 @@ export type BornAkumaCall = Readonly<{
|
|
|
23
23
|
initialBody?: string;
|
|
24
24
|
initiator?: string;
|
|
25
25
|
initialSchemaJson?: string;
|
|
26
|
-
awaitAsleep?: true;
|
|
27
26
|
execution: BornExecution;
|
|
28
27
|
}>;
|
|
29
28
|
export type RequestedAkumaCall = Readonly<{
|
|
@@ -51,7 +50,7 @@ declare class AkumaProduct {
|
|
|
51
50
|
admit(input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaBornCall>;
|
|
52
51
|
publish(born: AkumaBornCall, completion?: Readonly<{
|
|
53
52
|
contractId?: string;
|
|
54
|
-
}
|
|
53
|
+
}>, signal?: AbortSignal): Promise<AkumaHandle>;
|
|
55
54
|
[CALL_WITH_CONTEXT](input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaHandle>;
|
|
56
55
|
listComplete(input?: Readonly<{
|
|
57
56
|
archetype?: string;
|
|
@@ -27,9 +27,9 @@ async function admitBodyRequest(input) {
|
|
|
27
27
|
archetype: input.name,
|
|
28
28
|
...(input.call.body === undefined ? {} : { body: input.call.body }),
|
|
29
29
|
...(input.call.initiator === undefined ? {} : { initiator: input.call.initiator }),
|
|
30
|
-
...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
|
|
31
30
|
...(cwd === undefined ? {} : { cwd }),
|
|
32
31
|
recipe: input.recipe,
|
|
32
|
+
...(input.call.signal === undefined ? {} : { signal: input.call.signal }),
|
|
33
33
|
});
|
|
34
34
|
const bornCwd = await readAkumaBirthCwd(input.path, child);
|
|
35
35
|
return {
|
|
@@ -45,7 +45,11 @@ async function admitDirect(input) {
|
|
|
45
45
|
const cwd = input.call.cwd !== undefined && input.context.cwdCanonical === true
|
|
46
46
|
? input.call.cwd
|
|
47
47
|
: await canonicalBirthCwd(selectedCwd);
|
|
48
|
-
const allocated = await birthAkuma({
|
|
48
|
+
const allocated = await birthAkuma({
|
|
49
|
+
worldPath: input.path,
|
|
50
|
+
archetype: input.archetype.name,
|
|
51
|
+
...(input.call.signal === undefined ? {} : { signal: input.call.signal }),
|
|
52
|
+
});
|
|
49
53
|
return {
|
|
50
54
|
kind: "born",
|
|
51
55
|
allocated,
|
|
@@ -59,7 +63,6 @@ async function admitDirect(input) {
|
|
|
59
63
|
...(input.call.body === undefined ? {} : { initialBody: input.call.body }),
|
|
60
64
|
...(input.call.initiator === undefined ? {} : { initiator: input.call.initiator }),
|
|
61
65
|
...(input.call.schema === undefined ? {} : { initialSchemaJson: schemaJsonText(input.call.schema) }),
|
|
62
|
-
...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
|
|
63
66
|
execution: {
|
|
64
67
|
cwd,
|
|
65
68
|
source: input.call.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
|
|
@@ -204,13 +207,13 @@ class AkumaProduct {
|
|
|
204
207
|
return await admitBodyRequest({ call: input, context, path: this.path, name, recipe: requestRecipe, execution });
|
|
205
208
|
return await admitDirect({ call: input, context, path: this.path, archetype, recipe: requestRecipe });
|
|
206
209
|
}
|
|
207
|
-
async publish(born, completion = {}) {
|
|
210
|
+
async publish(born, completion = {}, signal) {
|
|
208
211
|
if (born.kind === "requested") {
|
|
209
212
|
return new AkumaHandle(born.id, this.path, { cwd: born.cwd, source: born.execution.source });
|
|
210
213
|
}
|
|
211
214
|
const published = await launchAkuma({
|
|
212
215
|
allocated: born.allocated,
|
|
213
|
-
...(
|
|
216
|
+
...(signal === undefined ? {} : { signal }),
|
|
214
217
|
launch: async (allocated) => await spawnAkumaBody({
|
|
215
218
|
paths: allocated.paths,
|
|
216
219
|
seed: born.seed,
|
|
@@ -267,6 +267,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
|
|
|
267
267
|
}, z.core.$strict>]>;
|
|
268
268
|
}, z.core.$strict>]>>>;
|
|
269
269
|
omitted: z.ZodNumber;
|
|
270
|
+
openingSequence: z.ZodOptional<z.ZodNumber>;
|
|
270
271
|
}, z.core.$strict>, z.ZodObject<{
|
|
271
272
|
reportedChanges: z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
272
273
|
sequence: z.ZodNumber;
|
|
@@ -459,6 +460,7 @@ export type AkumaCallInput = Readonly<{
|
|
|
459
460
|
allowed?: readonly AllowedAction[];
|
|
460
461
|
schema?: Schema<unknown>;
|
|
461
462
|
initiator?: string;
|
|
463
|
+
signal?: AbortSignal;
|
|
462
464
|
}>;
|
|
463
465
|
export type AkumaCallContext = Readonly<{
|
|
464
466
|
initiatorCwd?: string;
|
|
@@ -85,4 +85,4 @@ export declare function handoffPendingTells(paths: AkumaPaths, spawn?: (launch:
|
|
|
85
85
|
export declare function driveAkumaBody(launch: BodyLaunch, adapter?: ProviderAdapter, runtimeInput?: Partial<BodyRuntime>): Promise<"held" | void>;
|
|
86
86
|
export declare function spawnAkumaBody(launch: BodyLaunch, runtime?: BodyLaunchRuntime): Promise<OwnedProcess>;
|
|
87
87
|
export declare function runAkumaBody(launch: BodyLaunch, world: WorldRoot, externalCommands: Readonly<Record<string, ErasedRequestCommand>>): Promise<"held" | void>;
|
|
88
|
-
export declare function wakeRecordedTell(paths: AkumaPaths, tellId: string, runtime?: TellWakeRuntime): Promise<TellResult>;
|
|
88
|
+
export declare function wakeRecordedTell(paths: AkumaPaths, tellId: string, runtime?: TellWakeRuntime, signal?: AbortSignal): Promise<TellResult>;
|