@astrosheep/keiyaku 4.5.24 → 4.5.26

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 (43) hide show
  1. package/build/src/akuma/akuma-handle.d.ts +10 -0
  2. package/build/src/akuma/akuma-handle.js +18 -18
  3. package/build/src/akuma/akuma-instance.d.ts +5 -10
  4. package/build/src/akuma/akuma-instance.js +55 -40
  5. package/build/src/akuma/akuma-observe.d.ts +40 -7
  6. package/build/src/akuma/akuma-observe.js +51 -16
  7. package/build/src/akuma/akuma-product.d.ts +1 -2
  8. package/build/src/akuma/akuma-product.js +8 -5
  9. package/build/src/akuma/akuma.d.ts +2 -0
  10. package/build/src/akuma/body.d.ts +1 -1
  11. package/build/src/akuma/body.js +48 -30
  12. package/build/src/akuma/call-request.d.ts +0 -1
  13. package/build/src/akuma/call-request.js +1 -2
  14. package/build/src/akuma/fleet-execution.d.ts +3 -1
  15. package/build/src/akuma/fleet-execution.js +30 -71
  16. package/build/src/akuma/fleet-observation.d.ts +13 -2
  17. package/build/src/akuma/fleet-observation.js +2 -1
  18. package/build/src/akuma/fleet-request.js +3 -2
  19. package/build/src/akuma/heart/timeline.d.ts +1 -1
  20. package/build/src/akuma/heart/timeline.js +5 -5
  21. package/build/src/akuma/projection-read.js +37 -7
  22. package/build/src/akuma/projection.d.ts +1 -0
  23. package/build/src/akuma/projection.js +24 -9
  24. package/build/src/akuma/providers/pi/index.d.ts +2 -1
  25. package/build/src/akuma/providers/pi/index.js +2 -1
  26. package/build/src/akuma/publication.js +7 -4
  27. package/build/src/akuma-body.js +1 -0
  28. package/build/src/cli/commands/akuma-invoke.d.ts +1 -0
  29. package/build/src/cli/commands/akuma-invoke.js +16 -3
  30. package/build/src/cli/invoke.js +1 -0
  31. package/build/src/cli/render/akuma-activity.d.ts +13 -5
  32. package/build/src/cli/render/akuma-activity.js +118 -112
  33. package/build/src/cli/render/akuma-tool.js +5 -3
  34. package/build/src/library/address.d.ts +1 -1
  35. package/build/src/library/address.js +10 -8
  36. package/build/src/library/akuma-creation.d.ts +3 -0
  37. package/build/src/library/akuma-creation.js +111 -46
  38. package/build/src/library/composition.d.ts +3 -1
  39. package/build/src/library/fleet.d.ts +7 -1
  40. package/build/src/library/fleet.js +35 -9
  41. package/build/src/library/keiyaku.d.ts +3 -1
  42. package/build/src/runtime/proc/windows-launch.exe +0 -0
  43. package/package.json +3 -3
@@ -51,7 +51,7 @@ export declare function addressAkuma(input: UncheckedAkumaAddressInput): Promise
51
51
  }>>;
52
52
  export declare function addressAkumaSet(input: UncheckedAkumaAddressInput): Promise<Readonly<{
53
53
  path: WorldRoot;
54
- /** The complete selected set in canonical order: the durable result order. */
54
+ /** The complete selected set in canonical order. */
55
55
  ids: readonly AkuId[];
56
56
  /** The same set in the caller's selector order, for surfaces that name the selection as chosen. */
57
57
  orderedIds: readonly AkuId[];
@@ -39,12 +39,6 @@ export function resolveNamedAddress(input) {
39
39
  if (selector.startsWith("aku/"))
40
40
  return { kind: "akuma", id: parseAkuId(selector).id };
41
41
  const alias = parseAkumaAlias(selector);
42
- if (input.report.contracts.kind === "failed") {
43
- throw new TypeError("cannot resolve a named selector while the Contract world is failed");
44
- }
45
- if (input.aliases.kind === "failed") {
46
- throw new TypeError("cannot resolve a named selector while Alias authority is failed");
47
- }
48
42
  const contractMatches = (input.report.contracts.kind === "present" ? input.report.contracts.value.rows : []).filter((row) => row.disposition === "active" &&
49
43
  row.workspace === "worktree" &&
50
44
  row.worktreePath !== null &&
@@ -54,12 +48,20 @@ export function resolveNamedAddress(input) {
54
48
  : null;
55
49
  if (contractMatches.length > 0 && aliasId !== null)
56
50
  throw new TypeError(`ambiguous selector matches Contract and Akuma: ${selector}`);
51
+ // A readable alias selects its Akuma independently. Contract composition can
52
+ // enrich the subsequent observation, but cannot erase this core address.
53
+ if (aliasId !== null)
54
+ return { kind: "akuma", id: aliasId };
57
55
  if (contractMatches.length === 1)
58
56
  return { kind: "contract", id: contractMatches[0].id };
59
57
  if (contractMatches.length > 1)
60
58
  throw new TypeError(`ambiguous Contract selector: ${selector}`);
61
- if (aliasId !== null)
62
- return { kind: "akuma", id: aliasId };
59
+ if (input.report.contracts.kind === "failed") {
60
+ throw new TypeError("cannot resolve a named selector while the Contract world is failed");
61
+ }
62
+ if (input.aliases.kind === "failed") {
63
+ throw new TypeError("cannot resolve a named selector while Alias authority is failed");
64
+ }
63
65
  throw new TypeError(`unknown selector: ${selector}`);
64
66
  }
65
67
  export async function addressAkuma(input) {
@@ -55,11 +55,13 @@ export type CallInput = Readonly<{
55
55
  allowed?: readonly AllowedAction[];
56
56
  schema?: Schema<unknown>;
57
57
  initiator?: string;
58
+ signal?: AbortSignal;
58
59
  }>;
59
60
  export type CallObservation = Readonly<{
60
61
  kind: "detached";
61
62
  }> | Readonly<{
62
63
  kind: "observed";
64
+ reason: "completed" | "deadline";
63
65
  status: AkumaStatus;
64
66
  }> | Readonly<{
65
67
  kind: "failed";
@@ -85,6 +87,7 @@ export type BornCall = Readonly<{
85
87
  execution: CallExecution;
86
88
  mode: "wait" | "detach";
87
89
  timeoutMs: number;
90
+ signal?: AbortSignal;
88
91
  dispatch: DispatchStage;
89
92
  alias: AliasStage;
90
93
  schemaTell?: Readonly<{
@@ -1,6 +1,7 @@
1
1
  /** @architectureCompositionRoot */
2
2
  import { appendFile } from "node:fs/promises";
3
3
  import { moveAlias } from "../alias/index.js";
4
+ import { defaultWaitComplete } from "../akuma/akuma.js";
4
5
  import { createAkumaProduct } from "../akuma/akuma-product.js";
5
6
  import { pathsForAkuId } from "../akuma/identity.js";
6
7
  import { readSoul } from "../akuma/heart/index.js";
@@ -83,6 +84,13 @@ function callTimeout(value, mode) {
83
84
  }
84
85
  return value;
85
86
  }
87
+ function callSignal(value) {
88
+ if (value === undefined)
89
+ return undefined;
90
+ if (!(value instanceof AbortSignal))
91
+ throw new TypeError("signal must be an AbortSignal");
92
+ return value;
93
+ }
86
94
  function callSeat(contract) {
87
95
  if (contract === undefined)
88
96
  return undefined;
@@ -91,14 +99,17 @@ function callSeat(contract) {
91
99
  throw new TypeError("contract must be a Keiyaku");
92
100
  return seat;
93
101
  }
94
- async function observeCall(handle, mode, timeoutMs) {
102
+ async function observeCall(handle, mode, timeoutMs, signal) {
95
103
  if (mode === "detach") {
96
104
  return { kind: "detached" };
97
105
  }
98
106
  try {
99
- return { kind: "observed", status: await handle.wait(undefined, { timeoutMs }) };
107
+ const observed = await handle.waitReceipt(undefined, { timeoutMs, ...(signal === undefined ? {} : { signal }) });
108
+ return { kind: "observed", reason: observed.reason, status: observed.status };
100
109
  }
101
110
  catch (error) {
111
+ if (signal?.aborted)
112
+ throw error;
102
113
  return { kind: "failed", failure: integrationFailure(error) };
103
114
  }
104
115
  }
@@ -257,8 +268,9 @@ const CALL_INPUT_KEYS = [
257
268
  "allowed",
258
269
  "schema",
259
270
  "initiator",
271
+ "signal",
260
272
  ];
261
- async function prepareCall(input, context) {
273
+ async function parseCallInput(input) {
262
274
  const values = requireInput(input, "Keiyaku.call input");
263
275
  onlyKeys(values, CALL_INPUT_KEYS, "Keiyaku.call input");
264
276
  const path = await World.prove(nonblank(values.path, "path"));
@@ -269,55 +281,81 @@ async function prepareCall(input, context) {
269
281
  const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
270
282
  const mode = callMode(values.mode);
271
283
  const timeoutMs = callTimeout(values.timeoutMs, mode);
284
+ const signal = callSignal(values.signal);
272
285
  const home = homeOption(values.home);
273
286
  const settings = settingsOption(values.settings);
274
287
  const alias = values.alias === undefined ? undefined : parseAkumaAlias(nonblank(values.alias, "alias"));
275
288
  const seat = callSeat(values.contract);
276
- const execution = await resolveCallExecution({
289
+ return {
290
+ values,
277
291
  path,
278
- ...(cwd === undefined ? {} : { cwd }),
279
- ...(seat === undefined ? {} : { contract: values.contract }),
280
- });
281
- const world = akumaWorld(path, home, settings, context);
282
- const call = {
283
292
  archetype,
284
- ...(values.schema === undefined ? { body } : {}),
293
+ body,
285
294
  ...(initiator === undefined ? {} : { initiator }),
286
- ...(readonlyRequested === undefined ? {} : { readonly: readonlyRequested }),
287
- ...(values.allowed === undefined ? {} : { allowed: values.allowed }),
288
- ...(values.schema === undefined ? {} : { schema: values.schema }),
295
+ ...(readonlyRequested === undefined ? {} : { readonlyRequested }),
296
+ ...(cwd === undefined ? {} : { cwd }),
297
+ mode,
298
+ timeoutMs,
299
+ ...(signal === undefined ? {} : { signal }),
300
+ ...(home === undefined ? {} : { home }),
301
+ ...(settings === undefined ? {} : { settings }),
302
+ ...(alias === undefined ? {} : { alias }),
303
+ seat,
304
+ };
305
+ }
306
+ function callAdmissionInput(input, execution) {
307
+ return {
308
+ archetype: input.archetype,
309
+ ...(input.values.schema === undefined ? { body: input.body } : {}),
310
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
311
+ ...(input.readonlyRequested === undefined ? {} : { readonly: input.readonlyRequested }),
312
+ ...(input.values.allowed === undefined ? {} : { allowed: input.values.allowed }),
313
+ ...(input.values.schema === undefined ? {} : { schema: input.values.schema }),
289
314
  ...(execution === undefined ? {} : { cwd: execution.cwd }),
315
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
290
316
  };
317
+ }
318
+ function schemaTell(input) {
319
+ if (input.values.schema === undefined)
320
+ return undefined;
321
+ return {
322
+ body: input.body,
323
+ schema: input.values.schema,
324
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
325
+ };
326
+ }
327
+ async function prepareCall(input, context) {
328
+ const parsed = await parseCallInput(input);
329
+ const execution = await resolveCallExecution({
330
+ path: parsed.path,
331
+ ...(parsed.cwd === undefined ? {} : { cwd: parsed.cwd }),
332
+ ...(parsed.seat === undefined ? {} : { contract: parsed.values.contract }),
333
+ });
334
+ const world = akumaWorld(parsed.path, parsed.home, parsed.settings, context);
291
335
  const { born, akuma } = await admitCall({
292
- path,
336
+ path: parsed.path,
293
337
  world,
294
- call: call,
338
+ call: callAdmissionInput(parsed, execution),
295
339
  context: execution === undefined ? {} : { cwdCanonical: true },
296
- ...(settings === undefined ? {} : { settings }),
297
- ...(seat === undefined ? {} : { contractId: seat.id }),
340
+ ...(parsed.settings === undefined ? {} : { settings: parsed.settings }),
341
+ ...(parsed.seat === undefined ? {} : { contractId: parsed.seat.id }),
298
342
  });
299
343
  const completedExecution = execution ?? born.execution;
300
- const dispatch = seat === undefined
344
+ const dispatch = parsed.seat === undefined
301
345
  ? { kind: "none" }
302
- : await dispatchStage({ repository: seat.scope, akuId: akuma, contractId: seat.id });
303
- const aliasStage = await resolveAliasStage(path, alias, dispatch, akuma);
346
+ : await dispatchStage({ repository: parsed.seat.scope, akuId: akuma, contractId: parsed.seat.id });
347
+ const aliasStage = await resolveAliasStage(parsed.path, parsed.alias, dispatch, akuma);
348
+ const initialSchemaTell = schemaTell(parsed);
304
349
  return {
305
- path,
350
+ path: parsed.path,
306
351
  born,
307
352
  execution: completedExecution,
308
- mode,
309
- timeoutMs,
353
+ mode: parsed.mode,
354
+ timeoutMs: parsed.timeoutMs,
355
+ ...(parsed.signal === undefined ? {} : { signal: parsed.signal }),
310
356
  dispatch,
311
357
  alias: aliasStage,
312
- ...(values.schema === undefined
313
- ? {}
314
- : {
315
- schemaTell: {
316
- body,
317
- schema: values.schema,
318
- ...(initiator === undefined ? {} : { initiator }),
319
- },
320
- }),
358
+ ...(initialSchemaTell === undefined ? {} : { schemaTell: initialSchemaTell }),
321
359
  };
322
360
  }
323
361
  async function publishCall(born, execution) {
@@ -325,31 +363,58 @@ async function publishCall(born, execution) {
325
363
  const contractId = born.dispatch.kind === "dispatched" ? born.dispatch.dispatch.contractId : undefined;
326
364
  const handle = await world.publish(born.born, {
327
365
  ...(contractId === undefined ? {} : { contractId }),
328
- });
366
+ }, born.signal);
367
+ const deadline = born.mode === "wait" ? performance.now() + born.timeoutMs : undefined;
368
+ const remaining = () => (deadline === undefined ? born.timeoutMs : Math.max(0, deadline - performance.now()));
369
+ const schemaSignal = () => {
370
+ if (deadline === undefined)
371
+ return born.signal;
372
+ const timeout = AbortSignal.timeout(Math.ceil(remaining()));
373
+ return born.signal === undefined ? timeout : AbortSignal.any([born.signal, timeout]);
374
+ };
329
375
  const readonly = (await handle.status()).readonly;
330
376
  let schemaAnswer;
331
377
  if (born.schemaTell !== undefined) {
332
378
  const tell = born.schemaTell;
333
379
  // Birth publishes Soul before its prompt-free Body has necessarily settled.
334
380
  // Schema admission still belongs to Tell, after that initial Body is idle.
335
- const pending = handle.wait().then(() => execution.channel.kind === "body-request"
336
- ? requestForwardedFleetTellAnswer({
337
- directory: execution.channel.directory,
338
- target: handle.id,
339
- body: tell.body,
340
- schema: tell.schema,
341
- ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
342
- })
343
- : PublicAkuma.select(born.path, handle.id).tell(tell.body, {
344
- schema: tell.schema,
345
- ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
346
- }));
381
+ const pending = handle
382
+ .wait(undefined, {
383
+ timeoutMs: remaining(),
384
+ ...(born.signal === undefined ? {} : { signal: born.signal }),
385
+ })
386
+ .then(async (initial) => {
387
+ if (initial.life !== "asleep" || !defaultWaitComplete(initial) || (deadline !== undefined && remaining() <= 0))
388
+ return undefined;
389
+ const signal = schemaSignal();
390
+ try {
391
+ return execution.channel.kind === "body-request"
392
+ ? await requestForwardedFleetTellAnswer({
393
+ directory: execution.channel.directory,
394
+ target: handle.id,
395
+ body: tell.body,
396
+ schema: tell.schema,
397
+ ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
398
+ ...(signal === undefined ? {} : { signal }),
399
+ })
400
+ : await PublicAkuma.select(born.path, handle.id).tell(tell.body, {
401
+ schema: tell.schema,
402
+ ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
403
+ ...(signal === undefined ? {} : { signal }),
404
+ });
405
+ }
406
+ catch (error) {
407
+ if (born.signal?.aborted || deadline === undefined || remaining() > 0)
408
+ throw error;
409
+ return undefined;
410
+ }
411
+ });
347
412
  if (born.mode === "detach")
348
413
  void pending.catch(() => undefined);
349
414
  else
350
415
  schemaAnswer = await pending;
351
416
  }
352
- const observation = await observeCall(handle, born.mode, born.timeoutMs);
417
+ const observation = await observeCall(handle, born.mode, remaining(), born.signal);
353
418
  return {
354
419
  kind: "called",
355
420
  akuma: handle.id,
@@ -85,7 +85,8 @@ export declare function composeLibrary(context?: ExecutionContext, composition?:
85
85
  };
86
86
  }>;
87
87
  wait: (input: Parameters<typeof waitAkuma>[0]) => Promise<{
88
- completion: "any" | "all";
88
+ mode: "any" | "all";
89
+ reason: "deadline" | "completed";
89
90
  observations: {
90
91
  status: {
91
92
  id: import("./keiyaku.js").AkuId;
@@ -238,6 +239,7 @@ export declare function composeLibrary(context?: ExecutionContext, composition?:
238
239
  };
239
240
  })[];
240
241
  omitted: number;
242
+ openingSequence?: number | undefined;
241
243
  } | {
242
244
  reportedChanges: readonly {
243
245
  sequence: number;
@@ -8,10 +8,12 @@ import { type AkumaKillResult, type AkumaObservation, type AkumaObservationStage
8
8
  export type AkumaWaitInput = AkumaSetAddressInput & Readonly<{
9
9
  completion?: "any" | "all";
10
10
  timeoutMs?: number;
11
+ signal?: AbortSignal;
11
12
  }>;
12
13
  export type AkumaTellInput = AkumaAddressInput & Readonly<{
13
14
  body: string;
14
15
  initiator?: string;
16
+ signal?: AbortSignal;
15
17
  }>;
16
18
  export type { TellResult, TellWake } from "../akuma/akuma.js";
17
19
  export type { CreatedTaskObservation } from "../task/created-observation.js";
@@ -20,6 +22,10 @@ export type { AkumaKillResult, AkumaObservation, AkumaObservationStage, AkumaTel
20
22
  export type AkumaInterruptInput = AkumaAddressInput & Readonly<{
21
23
  body: string;
22
24
  initiator?: string;
25
+ signal?: AbortSignal;
26
+ }>;
27
+ export type AkumaKillInput = AkumaSetAddressInput & Readonly<{
28
+ signal?: AbortSignal;
23
29
  }>;
24
30
  export type AkumaInterruptResult = Readonly<{
25
31
  id: AkumaStatus["id"];
@@ -60,7 +66,7 @@ export type AkumaHistoryResult = Readonly<{
60
66
  }>;
61
67
  export declare function statusAkuma(input: AkumaAddressInput): Promise<AkumaObservation>;
62
68
  export declare function waitAkuma(input: AkumaWaitInput, execution?: ExecutionContext, observer?: WaitObserver): Promise<AkumaWaitResult>;
63
- export declare function killAkuma(input: AkumaSetAddressInput, execution?: ExecutionContext): Promise<AkumaKillResult>;
69
+ export declare function killAkuma(input: AkumaKillInput, execution?: ExecutionContext): Promise<AkumaKillResult>;
64
70
  export declare function tellAkuma(input: AkumaTellInput, execution?: ExecutionContext): Promise<AkumaTellResult>;
65
71
  export declare function interruptAkuma(input: AkumaInterruptInput): Promise<AkumaInterruptResult>;
66
72
  export declare function historyAkuma(input: AkumaHistoryInput): Promise<AkumaHistoryResult>;
@@ -115,6 +115,13 @@ function timeout(value) {
115
115
  }
116
116
  return value;
117
117
  }
118
+ function signal(value) {
119
+ if (value === undefined)
120
+ return undefined;
121
+ if (!(value instanceof AbortSignal))
122
+ throw new TypeError("signal must be an AbortSignal");
123
+ return value;
124
+ }
118
125
  async function attachWaitAssociations(path, repo, result) {
119
126
  const created = await createdTasksFor(path, result.observations.map((observation) => observation.status));
120
127
  const discharged = placementDischarged(repo);
@@ -151,7 +158,7 @@ export async function statusAkuma(input) {
151
158
  export async function waitAkuma(input, execution = localExecutionContext(), observer) {
152
159
  const values = requireInput(input, "Keiyaku.wait input");
153
160
  for (const key of Object.keys(values)) {
154
- if (!["path", "akuma", "repo", "completion", "timeoutMs"].includes(key)) {
161
+ if (!["path", "akuma", "repo", "completion", "timeoutMs", "signal"].includes(key)) {
155
162
  throw new TypeError(`Keiyaku.wait input has unknown field: ${key}`);
156
163
  }
157
164
  }
@@ -163,21 +170,24 @@ export async function waitAkuma(input, execution = localExecutionContext(), obse
163
170
  // An omitted mode is any: a plural wait returns when any selected Akuma is complete.
164
171
  const selected = completion ?? "any";
165
172
  const timeoutMs = timeout(values.timeoutMs);
173
+ const callerSignal = signal(values.signal);
166
174
  const channel = executionChannel(execution);
167
175
  const repo = values.repo;
168
176
  if (channel.kind === "body-request") {
169
177
  return await attachWaitAssociations(addressed.path, repo, await requestForwardedFleetWait({
170
178
  directory: channel.directory,
171
- targets: addressed.ids,
179
+ targets: addressed.orderedIds,
172
180
  completion: selected,
173
181
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
182
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
174
183
  }));
175
184
  }
176
185
  return await attachWaitAssociations(addressed.path, repo, await executeWaitAkuma({
177
186
  path: addressed.path,
178
- ids: addressed.ids,
187
+ ids: addressed.orderedIds,
179
188
  completion: selected,
180
189
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
190
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
181
191
  identity: waitIdentityFacts(addressed.path, repo),
182
192
  selectionOrder: addressed.orderedIds,
183
193
  ...(observer?.selected === undefined ? {} : { onSelected: observer.selected }),
@@ -185,28 +195,38 @@ export async function waitAkuma(input, execution = localExecutionContext(), obse
185
195
  }));
186
196
  }
187
197
  export async function killAkuma(input, execution = localExecutionContext()) {
188
- const addressed = await addressAkumaSet(input);
198
+ const values = requireInput(input, "Keiyaku.kill input");
199
+ for (const key of Object.keys(values)) {
200
+ if (!["path", "akuma", "repo", "signal"].includes(key)) {
201
+ throw new TypeError(`Keiyaku.kill input has unknown field: ${key}`);
202
+ }
203
+ }
204
+ const callerSignal = signal(values.signal);
205
+ const addressed = await addressAkumaSet(setAddress(values));
189
206
  const channel = executionChannel(execution);
190
207
  if (channel.kind === "body-request") {
191
208
  return await requestForwardedFleetKill({
192
209
  directory: channel.directory,
193
- targets: addressed.ids,
210
+ targets: addressed.orderedIds,
211
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
194
212
  });
195
213
  }
196
214
  return await executeKillAkuma({
197
215
  path: addressed.path,
198
- ids: addressed.ids,
216
+ ids: addressed.orderedIds,
217
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
199
218
  });
200
219
  }
201
220
  export async function tellAkuma(input, execution = localExecutionContext()) {
202
221
  const values = requireInput(input, "Keiyaku.tell input");
203
222
  for (const key of Object.keys(values)) {
204
- if (!["path", "akuma", "body", "repo", "initiator"].includes(key)) {
223
+ if (!["path", "akuma", "body", "repo", "initiator", "signal"].includes(key)) {
205
224
  throw new TypeError(`Keiyaku.tell input has unknown field: ${key}`);
206
225
  }
207
226
  }
208
227
  if (typeof values.body !== "string")
209
228
  throw new TypeError("body must be a string");
229
+ const callerSignal = signal(values.signal);
210
230
  const addressed = await addressAkuma(directAddress(values));
211
231
  const channel = executionChannel(execution);
212
232
  if (channel.kind === "body-request") {
@@ -215,6 +235,7 @@ export async function tellAkuma(input, execution = localExecutionContext()) {
215
235
  target: addressed.id,
216
236
  body: values.body,
217
237
  ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
238
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
218
239
  });
219
240
  }
220
241
  return await executeTellAkuma({
@@ -222,20 +243,25 @@ export async function tellAkuma(input, execution = localExecutionContext()) {
222
243
  id: addressed.id,
223
244
  body: values.body,
224
245
  ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
246
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
225
247
  });
226
248
  }
227
249
  export async function interruptAkuma(input) {
228
250
  const values = requireInput(input, "Keiyaku.interrupt input");
229
251
  for (const key of Object.keys(values)) {
230
- if (!["path", "akuma", "body", "repo", "initiator"].includes(key)) {
252
+ if (!["path", "akuma", "body", "repo", "initiator", "signal"].includes(key)) {
231
253
  throw new TypeError(`Keiyaku.interrupt input has unknown field: ${key}`);
232
254
  }
233
255
  }
234
256
  if (typeof values.body !== "string")
235
257
  throw new TypeError("body must be a string");
258
+ const callerSignal = signal(values.signal);
236
259
  const addressed = await addressAkuma(directAddress(values));
237
260
  const handle = source(addressed.path).selectHandle({ id: addressed.id });
238
- const receipt = await handle.interrupt(values.body, input.initiator === undefined ? {} : { initiator: input.initiator });
261
+ const receipt = await handle.interrupt(values.body, {
262
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
263
+ ...(callerSignal === undefined ? {} : { signal: callerSignal }),
264
+ });
239
265
  const observation = await observeAkumaStage(addressed.path, addressed.id, values.repo);
240
266
  return { id: addressed.id, receipt, observation };
241
267
  }
@@ -117,7 +117,8 @@ export declare const Keiyaku: Readonly<{
117
117
  };
118
118
  }>;
119
119
  wait: (input: Parameters<typeof import("./fleet.js").waitAkuma>[0]) => Promise<{
120
- completion: "any" | "all";
120
+ mode: "any" | "all";
121
+ reason: "deadline" | "completed";
121
122
  observations: {
122
123
  status: {
123
124
  id: import("../akuma/identity.js").AkuId;
@@ -270,6 +271,7 @@ export declare const Keiyaku: Readonly<{
270
271
  };
271
272
  })[];
272
273
  omitted: number;
274
+ openingSequence?: number | undefined;
273
275
  } | {
274
276
  reportedChanges: readonly {
275
277
  sequence: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.5.24",
3
+ "version": "4.5.26",
4
4
  "workspaces": [
5
5
  "plugins/*"
6
6
  ],
@@ -80,8 +80,8 @@
80
80
  "dependencies": {
81
81
  "@agentclientprotocol/sdk": "^1.3.0",
82
82
  "@anthropic-ai/claude-agent-sdk": "^0.3.226",
83
- "@astrosheep/keiyaku-plugin-square": "0.1.5",
84
- "@astrosheep/square": "^0.3.50",
83
+ "@astrosheep/keiyaku-plugin-square": "0.1.6",
84
+ "@astrosheep/square": "^0.3.57",
85
85
  "@earendil-works/pi-coding-agent": "0.85.1",
86
86
  "@opencode-ai/sdk": "1.18.3",
87
87
  "cross-spawn": "^7.0.6",