@astrosheep/keiyaku 4.1.1 → 4.1.2

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.
Files changed (58) hide show
  1. package/build/src/akuma/akuma-errors.d.ts +6 -0
  2. package/build/src/akuma/akuma-errors.js +9 -0
  3. package/build/src/akuma/akuma-handle.d.ts +39 -0
  4. package/build/src/akuma/akuma-handle.js +263 -0
  5. package/build/src/akuma/akuma-observe.d.ts +19 -0
  6. package/build/src/akuma/akuma-observe.js +74 -0
  7. package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
  8. package/build/src/akuma/akuma-product-symbols.js +1 -0
  9. package/build/src/akuma/akuma-product.d.ts +18 -0
  10. package/build/src/akuma/akuma-product.js +154 -0
  11. package/build/src/akuma/akuma.d.ts +23 -70
  12. package/build/src/akuma/akuma.js +17 -433
  13. package/build/src/akuma/body-supervisor.d.ts +26 -0
  14. package/build/src/akuma/body-supervisor.js +89 -0
  15. package/build/src/akuma/body.d.ts +1 -1
  16. package/build/src/akuma/body.js +3 -2
  17. package/build/src/akuma/projection-read.d.ts +17 -0
  18. package/build/src/akuma/projection-read.js +135 -0
  19. package/build/src/akuma/projection.d.ts +1 -18
  20. package/build/src/akuma/projection.js +21 -155
  21. package/build/src/akuma/request-execution.d.ts +6 -0
  22. package/build/src/akuma/request-execution.js +86 -0
  23. package/build/src/akuma/request-serve.d.ts +12 -1
  24. package/build/src/akuma/request-serve.js +2 -102
  25. package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
  26. package/build/src/akuma/{body-turn.js → turn-drive.js} +4 -99
  27. package/build/src/cli/commands/akuma.d.ts +0 -6
  28. package/build/src/cli/commands/akuma.js +1 -11
  29. package/build/src/cli/commands/contract-invoke.d.ts +25 -0
  30. package/build/src/cli/commands/contract-invoke.js +193 -0
  31. package/build/src/cli/commands/contract.d.ts +12 -0
  32. package/build/src/cli/commands/contract.js +229 -0
  33. package/build/src/cli/invoke.js +24 -184
  34. package/build/src/cli/parse.d.ts +1 -1
  35. package/build/src/cli/parse.js +8 -222
  36. package/build/src/cli/render/akuma-activity.d.ts +38 -0
  37. package/build/src/cli/render/akuma-activity.js +362 -0
  38. package/build/src/cli/render/akuma.d.ts +1 -1
  39. package/build/src/cli/render/akuma.js +14 -371
  40. package/build/src/cli/render/contract-history.d.ts +2 -0
  41. package/build/src/cli/render/contract-history.js +83 -0
  42. package/build/src/cli/render/contract.d.ts +1 -2
  43. package/build/src/cli/render/contract.js +1 -83
  44. package/build/src/git/reconcile.d.ts +8 -1
  45. package/build/src/git/reconcile.js +13 -301
  46. package/build/src/git/scratch.d.ts +8 -5
  47. package/build/src/git/scratch.js +29 -2
  48. package/build/src/git/terminal-reconcile.d.ts +21 -0
  49. package/build/src/git/terminal-reconcile.js +278 -0
  50. package/build/src/library/contract-bind.d.ts +26 -0
  51. package/build/src/library/contract-bind.js +106 -0
  52. package/build/src/library/contract-operations.d.ts +77 -0
  53. package/build/src/library/contract-operations.js +130 -0
  54. package/build/src/library/contract-types.d.ts +95 -0
  55. package/build/src/library/contract-types.js +1 -0
  56. package/build/src/library/contract.d.ts +11 -136
  57. package/build/src/library/contract.js +9 -222
  58. package/package.json +1 -1
@@ -1,10 +1,15 @@
1
1
  import { type TellResult, type TellWakeRuntime } from "./body.js";
2
- import { type AkumaLife, type KillEvidence, type ResumeCoordinate, type Soul } from "./heart/index.js";
2
+ import { HeldAkumaLeash, type AkumaLife, type KillEvidence, type ResumeCoordinate, type Soul } from "./heart/index.js";
3
3
  import { type AkuId, type AkumaPaths } from "./identity.js";
4
- import { type ActivityHistory, type ActivitySnapshot } from "./projection.js";
5
- import { type Settings } from "../settings.js";
4
+ import type { ActivitySnapshot } from "./projection.js";
5
+ import type { Settings } from "../settings.js";
6
6
  import type { WorldRoot } from "../world.js";
7
- import { type AllowedAction } from "./allowed.js";
7
+ import type { AllowedAction } from "./allowed.js";
8
+ export declare const POLL_MS = 100;
9
+ export declare function canonicalBirthCwd(input: string): Promise<string>;
10
+ export declare function callReadonly(value: unknown): Readonly<{
11
+ readonly?: true;
12
+ }>;
8
13
  export type AkumaListRow = Readonly<{
9
14
  id: AkuId;
10
15
  archetype: string;
@@ -44,8 +49,7 @@ export type AkumaCallExecution = Readonly<{
44
49
  cwd: string;
45
50
  source: "input" | "caller" | "process" | "world";
46
51
  }>;
47
- declare const CALL_EXECUTION: unique symbol;
48
- declare const CALL_WITH_CONTEXT: unique symbol;
52
+ export { CALL_WITH_CONTEXT } from "./akuma-product-symbols.js";
49
53
  export type AkumaCallInput = Readonly<{
50
54
  archetype: string;
51
55
  body: string;
@@ -53,7 +57,7 @@ export type AkumaCallInput = Readonly<{
53
57
  readonly?: true;
54
58
  allowed?: readonly AllowedAction[];
55
59
  }>;
56
- type AkumaCallContext = Readonly<{
60
+ export type AkumaCallContext = Readonly<{
57
61
  initiatorCwd?: string;
58
62
  cwdCanonical?: true;
59
63
  }>;
@@ -83,11 +87,13 @@ export type ForkReceipt = Readonly<{
83
87
  childSession: ResumeCoordinate;
84
88
  diagnostic: string;
85
89
  }>;
86
- export declare class AkumaNotBornError extends Error {
87
- readonly id: AkuId;
88
- readonly kind = "akuma-not-born";
89
- constructor(id: AkuId);
90
- }
90
+ export { AkumaNotBornError } from "./akuma-errors.js";
91
+ export declare function wait(milliseconds: number): Promise<void>;
92
+ export declare function takeLeashUntil(paths: AkumaPaths, deadline: number): Promise<HeldAkumaLeash | null>;
93
+ export declare function recordTellBody(paths: AkumaPaths, akuma: AkuId, body: string, id?: string, recordedAt?: string): Promise<Readonly<{
94
+ kind: "recorded";
95
+ tellId: string;
96
+ }>>;
91
97
  export declare function tellAkumaWithId(input: Readonly<{
92
98
  worldPath: WorldRoot;
93
99
  id: AkuId;
@@ -96,65 +102,12 @@ export declare function tellAkumaWithId(input: Readonly<{
96
102
  recordedAt?: string;
97
103
  runtime?: TellWakeRuntime;
98
104
  }>): Promise<TellResult>;
105
+ export declare function diagnostic(error: unknown): string;
99
106
  export declare function killAkumaWithRecovery(paths: AkumaPaths, recover?: (paths: AkumaPaths) => Promise<void>): Promise<KillEvidence>;
100
- export type BudgetedStatusObservation = Readonly<{
101
- status: AkumaStatus;
102
- ordinarySelected: number;
103
- }>;
104
- /** Package-internal observation; Fleet chooses the aperture and ordinary budget. */
105
- export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, input: Readonly<{
106
- aperture: "monitoring" | "receipt";
107
- ordinaryBudget?: number;
108
- }>): Promise<BudgetedStatusObservation>;
109
- /** Package-internal birth projection used by the composed Library call result. */
110
- export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
111
- export declare class AkumaHandle {
112
- readonly id: AkuId;
113
- private readonly worldPath;
114
- readonly [CALL_EXECUTION]?: AkumaCallExecution;
115
- constructor(id: AkuId, worldPath: WorldRoot, execution?: AkumaCallExecution);
116
- private get paths();
117
- status(): Promise<AkumaStatus>;
118
- history(input?: Readonly<{
119
- before?: number;
120
- since?: number;
121
- limit?: number;
122
- }>): Promise<ActivityHistory>;
123
- wait(predicate?: (status: AkumaStatus) => boolean, options?: Readonly<{
124
- timeoutMs?: number;
125
- }>): Promise<AkumaStatus>;
126
- tell(body: string): Promise<TellResult>;
127
- interrupt(body: string): Promise<InterruptReceipt>;
128
- fork(input: Readonly<{
129
- at: string;
130
- }>): Promise<ForkReceipt>;
131
- kill(): Promise<KillEvidence>;
132
- lastAnswer(): Promise<LastAnswer>;
133
- }
134
- /** Package-internal provenance retained only by the handle returned from call. */
135
- export declare function akumaCallExecution(handle: AkumaHandle): AkumaCallExecution | undefined;
136
- export type LastAnswer = Readonly<{
137
- kind: "answer";
138
- answer: string;
139
- }> | Readonly<{
140
- kind: "no-answer";
141
- }>;
142
- type AkumaConfiguration = Readonly<{
107
+ export { bornStatus, fleetListRow, readAkumaBirthCwd, readBudgetedStatus, type BudgetedStatusObservation, } from "./akuma-observe.js";
108
+ export type AkumaConfiguration = Readonly<{
143
109
  home?: string;
144
110
  settings?: Settings;
145
111
  }>;
146
- export declare class Akuma {
147
- private readonly path;
148
- private readonly configuration;
149
- private constructor();
150
- static of(root: WorldRoot, input?: AkumaConfiguration): Akuma;
151
- of(input: Readonly<{
152
- id: string;
153
- }>): AkumaHandle;
154
- listArchetypes(): Promise<readonly string[]>;
155
- call(input: AkumaCallInput): Promise<AkumaHandle>;
156
- [CALL_WITH_CONTEXT](input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
157
- list(input?: AkumaListInput): Promise<AkumaList>;
158
- }
159
- /** Package-internal call path for a composition owner that already canonicalized cwd. */
160
- export declare function callAkumaWithContext(akuma: Akuma, input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
112
+ export { AkumaHandle, akumaCallExecution, type LastAnswer } from "./akuma-handle.js";
113
+ export { Akuma, callAkumaWithContext } from "./akuma-product.js";
@@ -1,18 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { readdir, realpath, stat } from "node:fs/promises";
2
+ import { realpath, stat } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
- import { CONTROL_RESPONSE_MS, handoffPendingTells, spawnAkumaBody, wakeRecordedTell, } from "./body.js";
5
- import { HeldAkumaLeash, activitySlice, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
6
- import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, parseAkuId, pathsForAkuId, archetypeName, } from "./identity.js";
7
- import { ordinarySnapshotBudget, projectTurns, selectHistory, selectSnapshot, } from "./projection.js";
8
- import { listArchetypes as readArchetypes, loadArchetype } from "./archetype.js";
9
- import { publishAkuma } from "./publication.js";
10
- import { resolveProviderExecution } from "./providers/index.js";
11
- import { injectedBodyRequests, requestBodyCall } from "./requests.js";
12
- import { settings as readSettings } from "../settings.js";
13
- import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
14
- const POLL_MS = 100;
15
- async function canonicalBirthCwd(input) {
4
+ import { CONTROL_RESPONSE_MS, handoffPendingTells, wakeRecordedTell, } from "./body.js";
5
+ import { HeldAkumaLeash, readHeart, readKill, recordTell, requestStop, } from "./heart/index.js";
6
+ import { pathsForAkuId } from "./identity.js";
7
+ export const POLL_MS = 100;
8
+ export async function canonicalBirthCwd(input) {
16
9
  const selected = resolve(input);
17
10
  try {
18
11
  const canonical = await realpath(selected);
@@ -24,7 +17,7 @@ async function canonicalBirthCwd(input) {
24
17
  throw new Error(`cwd is not an existing directory: ${input}`);
25
18
  }
26
19
  }
27
- function callReadonly(value) {
20
+ export function callReadonly(value) {
28
21
  if (value === undefined)
29
22
  return {};
30
23
  if (value !== true)
@@ -36,21 +29,13 @@ export function defaultWaitComplete(status) {
36
29
  return (status.life !== "running" &&
37
30
  !status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
38
31
  }
39
- const CALL_EXECUTION = Symbol("akuma-call-execution");
40
- const CALL_WITH_CONTEXT = Symbol("akuma-call-with-context");
41
- export class AkumaNotBornError extends Error {
42
- id;
43
- kind = "akuma-not-born";
44
- constructor(id) {
45
- super(`Akuma ${id} is not born`);
46
- this.id = id;
47
- this.name = "AkumaNotBornError";
48
- }
49
- }
50
- function wait(milliseconds) {
32
+ export { CALL_WITH_CONTEXT } from "./akuma-product-symbols.js";
33
+ export { AkumaNotBornError } from "./akuma-errors.js";
34
+ import { AkumaNotBornError } from "./akuma-errors.js";
35
+ export function wait(milliseconds) {
51
36
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
52
37
  }
53
- async function takeLeashUntil(paths, deadline) {
38
+ export async function takeLeashUntil(paths, deadline) {
54
39
  for (;;) {
55
40
  const leash = await HeldAkumaLeash.try(paths);
56
41
  if (leash !== null)
@@ -60,7 +45,7 @@ async function takeLeashUntil(paths, deadline) {
60
45
  await wait(Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
61
46
  }
62
47
  }
63
- async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt = new Date().toISOString()) {
48
+ export async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt = new Date().toISOString()) {
64
49
  const admitted = await recordTell(paths, { kind: "tell", id, body, recordedAt });
65
50
  if (admitted.kind === "not-born")
66
51
  throw new AkumaNotBornError(akuma);
@@ -71,7 +56,7 @@ export async function tellAkumaWithId(input) {
71
56
  const recorded = await recordTellBody(paths, input.id, input.body, input.tellId, input.recordedAt);
72
57
  return await wakeRecordedTell(paths, recorded.tellId, input.runtime);
73
58
  }
74
- function diagnostic(error) {
59
+ export function diagnostic(error) {
75
60
  return error instanceof Error ? error.message : String(error);
76
61
  }
77
62
  export async function killAkumaWithRecovery(paths, recover = handoffPendingTells) {
@@ -110,407 +95,6 @@ export async function killAkumaWithRecovery(paths, recover = handoffPendingTells
110
95
  void recover(paths).catch(() => undefined);
111
96
  }
112
97
  }
113
- async function fleetListRow(paths, expected) {
114
- const snapshot = await readHeart(paths);
115
- if (snapshot.soul !== null)
116
- return await bornListRow(paths, expected, snapshot);
117
- try {
118
- if ((await probeLeash(paths)) === "held")
119
- return { id: expected, life: "unborn" };
120
- const seal = await readSeal(paths);
121
- return seal === null ? { id: expected, life: "unborn" } : { id: expected, life: "stillborn", seal };
122
- }
123
- catch (error) {
124
- if (isHeartAbsent(error))
125
- return { id: expected, life: "unborn" };
126
- throw error;
127
- }
128
- }
129
- async function bornListRow(paths, expected, snapshot) {
130
- snapshot ??= await readHeart(paths);
131
- if (snapshot.soul === null)
132
- throw new AkumaNotBornError(expected);
133
- if (snapshot.soul.id !== expected)
134
- throw new Error("Akuma soul does not match its coordinate");
135
- const currentLife = life({
136
- leash: await probeLeash(paths),
137
- body: snapshot.latestBody,
138
- kill: snapshot.latestKill,
139
- });
140
- return {
141
- id: snapshot.soul.id,
142
- archetype: snapshot.soul.archetype,
143
- ...(snapshot.soul.description === undefined ? {} : { description: snapshot.soul.description }),
144
- life: currentLife,
145
- lifeAt: lifeAt(currentLife, snapshot.latestBody, snapshot.latestKill, snapshot.soul.createdAt),
146
- lastActivityAt: snapshot.lastActivityAt,
147
- pending: snapshot.pending.map((tell) => tell.id),
148
- };
149
- }
150
- async function bornStatus(paths, expected, input) {
151
- if (input.ordinaryBudget !== undefined && (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0)) {
152
- throw new TypeError("ordinary budget must be a nonnegative safe integer");
153
- }
154
- const snapshot = await readHeart(paths);
155
- if (snapshot.soul === null)
156
- throw new AkumaNotBornError(expected);
157
- const current = await bornListRow(paths, expected, snapshot);
158
- const resumeUnsupported = current.life === "stranded" &&
159
- snapshot.latestSession?.provider === snapshot.soul.provider.name &&
160
- (await resolveProviderExecution(snapshot.soul.provider)).adapter.resume === undefined;
161
- const slice = await activitySlice(paths);
162
- const selected = selectSnapshot(projectTurns(slice.rows), {
163
- aperture: input.aperture,
164
- budget: ordinarySnapshotBudget(input.ordinaryBudget),
165
- });
166
- return {
167
- status: {
168
- id: current.id,
169
- life: current.life,
170
- ...(snapshot.soul.readonly === undefined ? {} : { readonly: snapshot.soul.readonly }),
171
- ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
172
- timeline: selected.snapshot,
173
- },
174
- ordinarySelected: selected.ordinaryCount,
175
- };
176
- }
177
- /** Package-internal observation; Fleet chooses the aperture and ordinary budget. */
178
- export async function readBudgetedStatus(worldPath, id, input) {
179
- return await bornStatus(pathsForAkuId(worldPath, id), id, input);
180
- }
181
- /** Package-internal birth projection used by the composed Library call result. */
182
- export async function readAkumaBirthCwd(worldPath, id) {
183
- const soul = await readSoul(pathsForAkuId(worldPath, id));
184
- if (soul === null)
185
- throw new AkumaNotBornError(id);
186
- return soul.cwd;
187
- }
188
- export class AkumaHandle {
189
- id;
190
- worldPath;
191
- [CALL_EXECUTION];
192
- constructor(id, worldPath, execution) {
193
- this.id = id;
194
- this.worldPath = worldPath;
195
- if (execution !== undefined)
196
- this[CALL_EXECUTION] = execution;
197
- }
198
- get paths() {
199
- return pathsForAkuId(this.worldPath, this.id);
200
- }
201
- async status() {
202
- return (await bornStatus(this.paths, this.id, { aperture: "monitoring" })).status;
203
- }
204
- async history(input = {}) {
205
- if (input.before !== undefined && input.since !== undefined) {
206
- throw new TypeError("Akuma history before and since are mutually exclusive");
207
- }
208
- for (const [name, value] of [
209
- ["before", input.before],
210
- ["since", input.since],
211
- ]) {
212
- if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
213
- throw new TypeError(`Akuma history ${name} must be a positive safe integer`);
214
- }
215
- }
216
- const limit = input.limit ?? 50;
217
- if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 5_000) {
218
- throw new TypeError("Akuma history limit must be a positive safe integer no greater than 5000");
219
- }
220
- const slice = await activitySlice(this.paths);
221
- return selectHistory(projectTurns(slice.rows, {
222
- lowestRetained: slice.lowestRetained,
223
- highest: slice.highest,
224
- }), {
225
- ...(input.before === undefined ? {} : { before: input.before }),
226
- ...(input.since === undefined ? {} : { since: input.since }),
227
- limit,
228
- });
229
- }
230
- async wait(predicate = defaultWaitComplete, options = {}) {
231
- if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
232
- throw new TypeError("Akuma wait timeoutMs must be a nonnegative finite millisecond duration");
233
- }
234
- const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
235
- for (;;) {
236
- const status = await this.status();
237
- if (predicate(status) || (deadline !== undefined && performance.now() >= deadline))
238
- return status;
239
- await wait(deadline === undefined ? POLL_MS : Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
240
- }
241
- }
242
- async tell(body) {
243
- const recorded = await recordTellBody(this.paths, this.id, body);
244
- return await wakeRecordedTell(this.paths, recorded.tellId);
245
- }
246
- async interrupt(body) {
247
- const request = await requestPause(this.paths, new Date().toISOString());
248
- if (request.kind === "not-born") {
249
- throw new AkumaNotBornError(this.id);
250
- }
251
- let putDown = "was-idle";
252
- let leash = await HeldAkumaLeash.try(this.paths);
253
- if (leash === null) {
254
- leash = await takeLeashUntil(this.paths, performance.now() + CONTROL_RESPONSE_MS);
255
- putDown = "self-aborted";
256
- }
257
- if (leash === null) {
258
- const body = (await readHeart(this.paths)).latestBody;
259
- return {
260
- kind: "unavailable",
261
- evidence: body?.sequence === request.body.sequence && body.hung !== undefined ? "hung" : "unavailable",
262
- };
263
- }
264
- const settledBody = (await readHeart(this.paths)).latestBody;
265
- if (settledBody?.sequence === request.body.sequence && settledBody.hung !== undefined) {
266
- try {
267
- await leash.clearPause(this.paths);
268
- }
269
- finally {
270
- leash.release();
271
- }
272
- return { kind: "unavailable", evidence: "hung" };
273
- }
274
- if (settledBody?.sequence !== request.body.sequence || settledBody.end === undefined) {
275
- try {
276
- await leash.clearPause(this.paths);
277
- }
278
- finally {
279
- leash.release();
280
- }
281
- return { kind: "unavailable", evidence: "untidy" };
282
- }
283
- if (request.body.end !== undefined || settledBody.end !== "put-down")
284
- putDown = "was-idle";
285
- let recorded;
286
- try {
287
- const id = randomUUID();
288
- const admitted = await leash.recordInterruptTell(this.paths, {
289
- kind: "tell",
290
- id,
291
- body,
292
- recordedAt: new Date().toISOString(),
293
- });
294
- if (admitted.kind === "not-born")
295
- throw new AkumaNotBornError(this.id);
296
- recorded = { kind: "recorded", tellId: admitted.tell.id };
297
- }
298
- finally {
299
- leash.release();
300
- }
301
- return { kind: "interrupted", putDown, tell: await wakeRecordedTell(this.paths, recorded.tellId) };
302
- }
303
- async fork(input) {
304
- const source = await readSoul(this.paths);
305
- if (source === null)
306
- throw new AkumaNotBornError(this.id);
307
- if (source.id !== this.id)
308
- throw new Error("Akuma soul does not match its coordinate");
309
- const adapter = (await resolveProviderExecution(source.provider)).adapter;
310
- if (adapter.fork === undefined)
311
- return { kind: "provider-cannot-fork", provider: source.provider.name };
312
- const point = await readForkPoint(this.paths, input.at);
313
- if (point === null)
314
- return { kind: "unknown-history", at: input.at };
315
- if (point.provider !== source.provider.name)
316
- throw new Error(`Akuma fork point ${input.at} has a mismatched provider`);
317
- let childSession;
318
- try {
319
- childSession = (await adapter.fork({ session: point.session, at: point.historyId, cwd: point.cwd })).session;
320
- }
321
- catch (error) {
322
- return { kind: "fork-failed", diagnostic: diagnostic(error) };
323
- }
324
- const admittedAt = new Date().toISOString();
325
- const birthSession = {
326
- provider: point.provider,
327
- coordinate: childSession,
328
- cwd: point.cwd,
329
- options: point.options,
330
- admittedAt,
331
- };
332
- try {
333
- const child = await publishAkuma({
334
- worldPath: this.worldPath,
335
- archetype: source.archetype,
336
- awaitAsleep: true,
337
- launch: async (allocated) => {
338
- (await spawnAkumaBody({
339
- paths: allocated.paths,
340
- seed: {
341
- id: allocated.id,
342
- archetype: source.archetype,
343
- ...(source.description === undefined ? {} : { description: source.description }),
344
- provider: source.provider,
345
- options: source.options,
346
- ...(source.readonly === undefined ? {} : { readonly: source.readonly }),
347
- allowed: source.allowed,
348
- cwd: source.cwd,
349
- origin: { kind: "fork", parent: this.id, at: input.at },
350
- },
351
- birthSession,
352
- })).release();
353
- },
354
- });
355
- return { kind: "forked", child: child.id };
356
- }
357
- catch (error) {
358
- return {
359
- kind: "upstream-forked",
360
- childSession,
361
- diagnostic: diagnostic(error),
362
- };
363
- }
364
- }
365
- async kill() {
366
- return await killAkumaWithRecovery(this.paths);
367
- }
368
- async lastAnswer() {
369
- const turn = await readLastAnsweredTurn(this.paths);
370
- return turn?.end?.outcome.kind === "answered"
371
- ? { kind: "answer", answer: turn.end.outcome.answer }
372
- : { kind: "no-answer" };
373
- }
374
- }
375
- /** Package-internal provenance retained only by the handle returned from call. */
376
- export function akumaCallExecution(handle) {
377
- return handle[CALL_EXECUTION];
378
- }
379
- export class Akuma {
380
- path;
381
- configuration;
382
- constructor(path, configuration) {
383
- this.path = path;
384
- this.configuration = configuration;
385
- }
386
- static of(root, input = {}) {
387
- if (typeof root !== "string")
388
- throw new TypeError("Akuma.of root must be a WorldRoot");
389
- return new Akuma(root, input);
390
- }
391
- of(input) {
392
- return new AkumaHandle(parseAkuId(input.id).id, this.path);
393
- }
394
- async listArchetypes() {
395
- return readArchetypes(this.configuration.home === undefined ? {} : { home: this.configuration.home });
396
- }
397
- async call(input) {
398
- return await this[CALL_WITH_CONTEXT](input, { initiatorCwd: process.cwd() });
399
- }
400
- async [CALL_WITH_CONTEXT](input, context) {
401
- const readonly = callReadonly(input.readonly);
402
- const name = archetypeName(input.archetype);
403
- const home = this.configuration.home === undefined ? {} : { home: this.configuration.home };
404
- const settings = this.configuration.settings ?? (await readSettings({ root: this.path, ...home }));
405
- const archetype = await loadArchetype({
406
- name,
407
- ...home,
408
- settings,
409
- ...readonly,
410
- });
411
- const allowed = input.allowed === undefined
412
- ? archetype.allowed
413
- : unionAllowedActions(archetype.allowed, decodeAllowedActions(input.allowed, "Akuma call allowed"));
414
- const requests = injectedBodyRequests();
415
- const requestRecipe = Object.freeze({
416
- ...(archetype.description === undefined ? {} : { description: archetype.description }),
417
- provider: archetype.provider,
418
- options: archetype.options,
419
- ...(archetype.readonly === undefined ? {} : { readonly: archetype.readonly }),
420
- allowed,
421
- });
422
- if (requests !== null) {
423
- const cwd = input.cwd === undefined
424
- ? undefined
425
- : context?.cwdCanonical === true
426
- ? input.cwd
427
- : await canonicalBirthCwd(input.cwd);
428
- const child = await requestBodyCall({
429
- directory: requests,
430
- id: randomUUID(),
431
- world: this.path,
432
- archetype: name,
433
- body: input.body,
434
- ...(cwd === undefined ? {} : { cwd }),
435
- recipe: requestRecipe,
436
- });
437
- const bornCwd = await readAkumaBirthCwd(this.path, child);
438
- return new AkumaHandle(child, this.path, {
439
- cwd: bornCwd,
440
- source: cwd === undefined ? "caller" : "input",
441
- });
442
- }
443
- const initiatorCwd = context.initiatorCwd;
444
- const selectedCwd = input.cwd ?? initiatorCwd ?? this.path;
445
- const cwd = input.cwd !== undefined && context?.cwdCanonical === true ? input.cwd : await canonicalBirthCwd(selectedCwd);
446
- const recipe = requestRecipe;
447
- const published = await publishAkuma({
448
- worldPath: this.path,
449
- archetype: archetype.name,
450
- launch: async (allocated) => {
451
- (await spawnAkumaBody({
452
- paths: allocated.paths,
453
- seed: {
454
- id: allocated.id,
455
- archetype: allocated.archetype,
456
- ...recipe,
457
- cwd,
458
- origin: { kind: "direct" },
459
- },
460
- initialBody: input.body,
461
- })).release();
462
- },
463
- });
464
- return new AkumaHandle(published.id, this.path, {
465
- cwd,
466
- source: input.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
467
- });
468
- }
469
- async list(input = {}) {
470
- if (typeof input !== "object" || input === null || Array.isArray(input)) {
471
- throw new TypeError("Akuma list input must be an object");
472
- }
473
- const unknown = Object.keys(input).find((key) => key !== "archetype");
474
- if (unknown !== undefined)
475
- throw new TypeError(`Akuma list input has unknown field: ${unknown}`);
476
- const selected = input.archetype === undefined ? undefined : archetypeName(input.archetype);
477
- const runRoot = akumaRunRoot(this.path);
478
- let names;
479
- try {
480
- names = (await readdir(runRoot, { withFileTypes: true }))
481
- .filter((entry) => entry.isDirectory())
482
- .map((entry) => entry.name)
483
- .sort();
484
- }
485
- catch (error) {
486
- if (error.code === "ENOENT")
487
- return { rows: [], searched: [runRoot] };
488
- throw error;
489
- }
490
- const rows = [];
491
- for (const name of names) {
492
- let physical;
493
- try {
494
- physical = akuIdFromDirectoryName(name);
495
- }
496
- catch {
497
- continue;
498
- }
499
- if (selected !== undefined && physical.archetype !== selected)
500
- continue;
501
- const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
502
- try {
503
- rows.push(await fleetListRow(paths, physical.id));
504
- }
505
- catch { }
506
- }
507
- return {
508
- rows,
509
- searched: [runRoot],
510
- };
511
- }
512
- }
513
- /** Package-internal call path for a composition owner that already canonicalized cwd. */
514
- export async function callAkumaWithContext(akuma, input, context) {
515
- return await akuma[CALL_WITH_CONTEXT](input, context);
516
- }
98
+ export { bornStatus, fleetListRow, readAkumaBirthCwd, readBudgetedStatus, } from "./akuma-observe.js";
99
+ export { AkumaHandle, akumaCallExecution } from "./akuma-handle.js";
100
+ export { Akuma, callAkumaWithContext } from "./akuma-product.js";
@@ -0,0 +1,26 @@
1
+ import { type HeartSnapshot, type HeldAkumaLeash } from "./heart/index.js";
2
+ import type { AkumaPaths } from "./identity.js";
3
+ export declare const CONTROL_RESPONSE_MS = 1000;
4
+ export declare class BodySupervisor {
5
+ private readonly paths;
6
+ private readonly bodySequence;
7
+ private readonly leash;
8
+ readonly signal: AbortSignal;
9
+ private readonly controller;
10
+ private readonly finished;
11
+ private readonly observer;
12
+ private waiter;
13
+ private observation;
14
+ private stopping?;
15
+ private constructor();
16
+ static open(paths: AkumaPaths, bodySequence: number, leash: HeldAkumaLeash): Promise<BodySupervisor>;
17
+ get reason(): "control" | "heart-gone" | undefined;
18
+ current(): HeartSnapshot;
19
+ recordHung(diagnostic: string, at: string): Promise<void>;
20
+ refresh(): Promise<HeartSnapshot>;
21
+ next(after: HeartSnapshot): Promise<HeartSnapshot>;
22
+ cancel(reason: "control" | "heart-gone"): void;
23
+ close(): Promise<void>;
24
+ private publish;
25
+ private observe;
26
+ }