@astrosheep/keiyaku 4.5.19 → 4.5.21

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 (197) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +23 -0
  14. package/build/src/akuma/akuma-product.d.ts +2 -0
  15. package/build/src/akuma/akuma-product.js +6 -0
  16. package/build/src/akuma/akuma.d.ts +20 -1
  17. package/build/src/akuma/akuma.js +2 -0
  18. package/build/src/akuma/allowed.d.ts +20 -0
  19. package/build/src/akuma/allowed.js +11 -0
  20. package/build/src/akuma/archetype.js +3 -0
  21. package/build/src/akuma/body.d.ts +25 -5
  22. package/build/src/akuma/body.js +70 -31
  23. package/build/src/akuma/call-request.d.ts +5 -0
  24. package/build/src/akuma/call-request.js +14 -4
  25. package/build/src/akuma/fleet-execution.d.ts +28 -0
  26. package/build/src/akuma/fleet-execution.js +36 -4
  27. package/build/src/akuma/fleet-observation.d.ts +54 -0
  28. package/build/src/akuma/fleet-request.d.ts +10 -0
  29. package/build/src/akuma/fleet-request.js +13 -3
  30. package/build/src/akuma/heart/facts.d.ts +2 -0
  31. package/build/src/akuma/heart/index.d.ts +1 -0
  32. package/build/src/akuma/heart/index.js +4 -1
  33. package/build/src/akuma/heart/rows.d.ts +2 -0
  34. package/build/src/akuma/heart/rows.js +7 -5
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +3 -1
  37. package/build/src/akuma/heart/tells.js +6 -5
  38. package/build/src/akuma/heart/timeline.js +1 -1
  39. package/build/src/akuma/provider-recipe.d.ts +1 -0
  40. package/build/src/akuma/provider-recipe.js +5 -0
  41. package/build/src/akuma/provider.d.ts +1 -1
  42. package/build/src/akuma/provider.js +1 -1
  43. package/build/src/akuma/providers/acp/core.js +3 -6
  44. package/build/src/akuma/providers/acp/index.js +3 -0
  45. package/build/src/akuma/providers/claude/index.js +10 -10
  46. package/build/src/akuma/providers/codex-app-server/index.js +12 -11
  47. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  48. package/build/src/akuma/providers/execution-environment.js +24 -0
  49. package/build/src/akuma/providers/grok-build/index.js +3 -0
  50. package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
  51. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  52. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  53. package/build/src/akuma/providers/pi/events.js +7 -2
  54. package/build/src/akuma/providers/pi/index.js +5 -2
  55. package/build/src/akuma/request-observation.d.ts +45 -0
  56. package/build/src/akuma/request-observation.js +162 -0
  57. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  58. package/build/src/akuma/request-rendezvous.js +80 -14
  59. package/build/src/akuma/request-serve.js +27 -4
  60. package/build/src/akuma/request-wire.d.ts +5 -0
  61. package/build/src/akuma/request-wire.js +2 -0
  62. package/build/src/akuma/turn-drive.d.ts +1 -0
  63. package/build/src/akuma/turn-drive.js +2 -0
  64. package/build/src/akuma-body.js +5 -0
  65. package/build/src/body/amend.js +6 -4
  66. package/build/src/body/decode.d.ts +3 -1
  67. package/build/src/body/decode.js +53 -17
  68. package/build/src/body/region.d.ts +9 -1
  69. package/build/src/body/region.js +87 -12
  70. package/build/src/body/render.js +1 -1
  71. package/build/src/body/verification.d.ts +3 -1
  72. package/build/src/body/verification.js +4 -1
  73. package/build/src/cli/accepted.js +19 -2
  74. package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
  75. package/build/src/cli/commands/akuma-invoke.js +134 -14
  76. package/build/src/cli/commands/akuma.js +24 -12
  77. package/build/src/cli/commands/contract-help.d.ts +15 -15
  78. package/build/src/cli/commands/contract-help.js +45 -19
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  80. package/build/src/cli/commands/contract-invoke.js +25 -11
  81. package/build/src/cli/commands/contract.js +6 -4
  82. package/build/src/cli/commands/install.d.ts +1 -1
  83. package/build/src/cli/commands/install.js +1 -1
  84. package/build/src/cli/commands/status-set.js +15 -24
  85. package/build/src/cli/commands/task.js +18 -18
  86. package/build/src/cli/invoke.d.ts +3 -0
  87. package/build/src/cli/invoke.js +58 -23
  88. package/build/src/cli/main.js +5 -0
  89. package/build/src/cli/parse.d.ts +3 -0
  90. package/build/src/cli/parse.js +26 -4
  91. package/build/src/cli/render/akuma-activity.d.ts +100 -2
  92. package/build/src/cli/render/akuma-activity.js +485 -40
  93. package/build/src/cli/render/akuma.d.ts +10 -0
  94. package/build/src/cli/render/akuma.js +31 -21
  95. package/build/src/cli/render/audit.js +1 -1
  96. package/build/src/cli/render/board.js +1 -1
  97. package/build/src/cli/render/catalog.js +16 -13
  98. package/build/src/cli/render/contract-history.js +39 -10
  99. package/build/src/cli/render/contract-observation.js +10 -6
  100. package/build/src/cli/render/contract.js +109 -106
  101. package/build/src/cli/render/execution-progress.d.ts +26 -0
  102. package/build/src/cli/render/execution-progress.js +178 -0
  103. package/build/src/cli/render/kanshi-akuma.js +27 -39
  104. package/build/src/cli/render/kanshi.js +33 -26
  105. package/build/src/cli/render/nuke.js +1 -1
  106. package/build/src/cli/render/receipt.d.ts +1 -1
  107. package/build/src/cli/render/receipt.js +6 -6
  108. package/build/src/cli/render/refusal.js +10 -7
  109. package/build/src/cli/render/region.js +1 -1
  110. package/build/src/cli/render/settings.js +12 -2
  111. package/build/src/cli/render/status-line.d.ts +30 -0
  112. package/build/src/cli/render/status-line.js +58 -0
  113. package/build/src/cli/render/status-set.js +1 -1
  114. package/build/src/cli/render/task.d.ts +2 -0
  115. package/build/src/cli/render/task.js +11 -7
  116. package/build/src/cli/render/terminal.d.ts +0 -1
  117. package/build/src/cli/render/terminal.js +47 -15
  118. package/build/src/cli/result.d.ts +1 -0
  119. package/build/src/cli/runtime.d.ts +2 -0
  120. package/build/src/cli/runtime.js +36 -4
  121. package/build/src/cli/usage.js +2 -2
  122. package/build/src/cli/version.d.ts +1 -0
  123. package/build/src/cli/version.js +25 -0
  124. package/build/src/git/hooks.d.ts +13 -1
  125. package/build/src/git/hooks.js +2 -1
  126. package/build/src/git/workspace.d.ts +1 -1
  127. package/build/src/git/workspace.js +1 -1
  128. package/build/src/identity/mint.d.ts +13 -0
  129. package/build/src/identity/mint.js +26 -0
  130. package/build/src/identity/normalize.d.ts +4 -0
  131. package/build/src/identity/normalize.js +30 -0
  132. package/build/src/index.d.ts +2 -0
  133. package/build/src/index.js +1 -0
  134. package/build/src/kanshi/read.js +17 -4
  135. package/build/src/kanshi/report.d.ts +1 -0
  136. package/build/src/library/akuma-creation.d.ts +2 -0
  137. package/build/src/library/akuma-creation.js +34 -18
  138. package/build/src/library/audit.d.ts +2 -0
  139. package/build/src/library/audit.js +1 -0
  140. package/build/src/library/bind.js +18 -16
  141. package/build/src/library/composition.d.ts +1 -0
  142. package/build/src/library/contract-bind.d.ts +3 -1
  143. package/build/src/library/contract-bind.js +5 -3
  144. package/build/src/library/contract-execution.d.ts +2 -0
  145. package/build/src/library/contract-execution.js +15 -1
  146. package/build/src/library/contract-forwarding.d.ts +4 -0
  147. package/build/src/library/contract-forwarding.js +3 -0
  148. package/build/src/library/contract-handle.d.ts +7 -0
  149. package/build/src/library/contract-handle.js +25 -0
  150. package/build/src/library/contract-operations.d.ts +7 -2
  151. package/build/src/library/contract-operations.js +21 -1
  152. package/build/src/library/contract-types.d.ts +1 -0
  153. package/build/src/library/execution-result.d.ts +2 -2
  154. package/build/src/library/execution.d.ts +8 -0
  155. package/build/src/library/execution.js +102 -0
  156. package/build/src/library/fleet.d.ts +4 -1
  157. package/build/src/library/fleet.js +32 -8
  158. package/build/src/library/keiyaku.d.ts +2 -0
  159. package/build/src/library/mutation.js +15 -7
  160. package/build/src/plugin/akuma-signals.d.ts +6 -0
  161. package/build/src/plugin/akuma-signals.js +9 -1
  162. package/build/src/plugin/public.d.ts +6 -2
  163. package/build/src/plugin/runtime.d.ts +1 -3
  164. package/build/src/plugin/runtime.js +95 -103
  165. package/build/src/protocol/amend.js +4 -3
  166. package/build/src/protocol/attempt.d.ts +2 -0
  167. package/build/src/protocol/bind.js +41 -14
  168. package/build/src/protocol/completion.d.ts +3 -0
  169. package/build/src/protocol/completion.js +35 -11
  170. package/build/src/protocol/deliver.js +165 -92
  171. package/build/src/protocol/execution-observation.d.ts +181 -0
  172. package/build/src/protocol/execution-observation.js +45 -0
  173. package/build/src/protocol/intent.d.ts +18 -22
  174. package/build/src/protocol/intent.js +63 -41
  175. package/build/src/protocol/operations.d.ts +2 -0
  176. package/build/src/protocol/placement.js +5 -1
  177. package/build/src/protocol/progress.d.ts +5 -1
  178. package/build/src/protocol/progress.js +10 -1
  179. package/build/src/protocol/read/status.d.ts +1 -0
  180. package/build/src/protocol/read/status.js +10 -2
  181. package/build/src/protocol/reintegrate.d.ts +1 -1
  182. package/build/src/protocol/reintegrate.js +70 -43
  183. package/build/src/protocol/result-codec.js +27 -11
  184. package/build/src/protocol/review.js +53 -30
  185. package/build/src/protocol/run.d.ts +83 -36
  186. package/build/src/protocol/run.js +90 -75
  187. package/build/src/runtime/proc/run.d.ts +16 -4
  188. package/build/src/runtime/proc/run.js +64 -22
  189. package/build/src/runtime/proc/windows-launch.exe +0 -0
  190. package/build/src/settings.js +5 -14
  191. package/build/src/task/document.d.ts +5 -1
  192. package/build/src/task/document.js +6 -1
  193. package/build/src/verification/execution.d.ts +10 -2
  194. package/build/src/verification/execution.js +143 -42
  195. package/build/src/verification/observation.d.ts +41 -0
  196. package/build/src/verification/observation.js +29 -0
  197. package/package.json +3 -1
@@ -72,6 +72,7 @@ export async function bornStatus(paths, expected, input) {
72
72
  status: {
73
73
  id: soul.id,
74
74
  life: currentLife,
75
+ allowed: soul.allowed,
75
76
  ...(soul.readonly === undefined ? {} : { readonly: soul.readonly }),
76
77
  ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
77
78
  timeline: selected.snapshot,
@@ -93,6 +94,28 @@ export async function readWaitComplete(worldPath, id) {
93
94
  const observed = await bornObservation(paths, id, () => readLifeSnapshot(paths));
94
95
  return complete(observed.currentLife, observed.snapshot.hasPendingTell);
95
96
  }
97
+ const OBSERVATION_POLL_MS = 100;
98
+ function observeDelay(milliseconds) {
99
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
100
+ }
101
+ /**
102
+ * Successive status observations of one already born Akuma until its
103
+ * observation window closes. Every status observed while the window is open is
104
+ * reported to `observe`; the window's last status is returned. A window that is
105
+ * already closed, or an Akuma already at its completion judgment, reports
106
+ * nothing and returns the current status.
107
+ */
108
+ export async function observeAkumaStatus(worldPath, expected, input) {
109
+ const paths = pathsForAkuId(worldPath, expected);
110
+ const deadline = performance.now() + input.timeoutMs;
111
+ for (;;) {
112
+ const status = (await bornStatus(paths, expected, { aperture: "monitoring" })).status;
113
+ if (defaultWaitComplete(status) || performance.now() >= deadline)
114
+ return status;
115
+ input.observe(status);
116
+ await observeDelay(Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - performance.now())));
117
+ }
118
+ }
96
119
  export async function readAkumaBirthCwd(worldPath, id) {
97
120
  const soul = await readSoul(pathsForAkuId(worldPath, id));
98
121
  if (soul === null)
@@ -21,7 +21,9 @@ export type BornAkumaCall = Readonly<{
21
21
  };
22
22
  }>;
23
23
  initialBody?: string;
24
+ initiator?: string;
24
25
  initialSchemaJson?: string;
26
+ awaitAsleep?: true;
25
27
  execution: BornExecution;
26
28
  }>;
27
29
  export type RequestedAkumaCall = Readonly<{
@@ -26,6 +26,8 @@ async function admitBodyRequest(input) {
26
26
  world: input.path,
27
27
  archetype: input.name,
28
28
  ...(input.call.body === undefined ? {} : { body: input.call.body }),
29
+ ...(input.call.initiator === undefined ? {} : { initiator: input.call.initiator }),
30
+ ...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
29
31
  ...(cwd === undefined ? {} : { cwd }),
30
32
  recipe: input.recipe,
31
33
  });
@@ -55,7 +57,9 @@ async function admitDirect(input) {
55
57
  origin: { kind: "direct" },
56
58
  },
57
59
  ...(input.call.body === undefined ? {} : { initialBody: input.call.body }),
60
+ ...(input.call.initiator === undefined ? {} : { initiator: input.call.initiator }),
58
61
  ...(input.call.schema === undefined ? {} : { initialSchemaJson: schemaJsonText(input.call.schema) }),
62
+ ...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
59
63
  execution: {
60
64
  cwd,
61
65
  source: input.call.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
@@ -206,10 +210,12 @@ class AkumaProduct {
206
210
  }
207
211
  const published = await launchAkuma({
208
212
  allocated: born.allocated,
213
+ ...(born.awaitAsleep === undefined ? {} : { awaitAsleep: born.awaitAsleep }),
209
214
  launch: async (allocated) => await spawnAkumaBody({
210
215
  paths: allocated.paths,
211
216
  seed: born.seed,
212
217
  ...(born.initialBody === undefined ? {} : { initialBody: born.initialBody }),
218
+ ...(born.initiator === undefined ? {} : { initiator: born.initiator }),
213
219
  ...(born.initialSchemaJson === undefined ? {} : { initialSchemaJson: born.initialSchemaJson }),
214
220
  ...(Object.keys(completion).length === 0 ? {} : { completion }),
215
221
  }),
@@ -3,7 +3,7 @@ export type { KillEvidence };
3
3
  import { type AkuId } from "./identity.js";
4
4
  import type { Settings } from "../settings.js";
5
5
  import type { WorldRoot } from "../world.js";
6
- import type { AllowedAction } from "./allowed.js";
6
+ import { type AllowedAction } from "./allowed.js";
7
7
  import type { ExecutionContext } from "./requests.js";
8
8
  import { z } from "zod";
9
9
  import type { BoundedList } from "../bounded-list.js";
@@ -32,6 +32,24 @@ export declare const akumaStatusSchema: z.ZodObject<{
32
32
  asleep: "asleep";
33
33
  stranded: "stranded";
34
34
  }>;
35
+ allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
36
+ "task.add": "task.add";
37
+ "task.addDocument": "task.addDocument";
38
+ "task.compose": "task.compose";
39
+ "task.done": "task.done";
40
+ "task.drop": "task.drop";
41
+ "task.hold": "task.hold";
42
+ "task.resume": "task.resume";
43
+ "task.start": "task.start";
44
+ "task.stop": "task.stop";
45
+ "task.update": "task.update";
46
+ "akuma.call": "akuma.call";
47
+ "akuma.kill": "akuma.kill";
48
+ "akuma.tell": "akuma.tell";
49
+ "contract.audit": "contract.audit";
50
+ "contract.deliver": "contract.deliver";
51
+ "contract.review": "contract.review";
52
+ }>>, z.ZodTransform<import("./allowed.js").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")[]>>;
35
53
  readonly: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
36
54
  enforcement: z.ZodLiteral<"native">;
37
55
  }, z.core.$strict>, z.ZodObject<{
@@ -439,6 +457,7 @@ export type AkumaCallInput = Readonly<{
439
457
  readonly?: true;
440
458
  allowed?: readonly AllowedAction[];
441
459
  schema?: Schema<unknown>;
460
+ initiator?: string;
442
461
  }>;
443
462
  export type AkumaCallContext = Readonly<{
444
463
  initiatorCwd?: string;
@@ -1,5 +1,6 @@
1
1
  import { parseAkuId } from "./identity.js";
2
2
  import { activitySnapshotSchema } from "./projection.js";
3
+ import { allowedActionsSchema } from "./allowed.js";
3
4
  import { z } from "zod";
4
5
  import { createAkumaProduct } from "./akuma-product.js";
5
6
  import { killAkumaWithRecovery } from "./akuma-handle.js";
@@ -25,6 +26,7 @@ export const akumaStatusSchema = z
25
26
  .object({
26
27
  id: akumaIdSchema,
27
28
  life: z.enum(["running", "asleep", "stranded", "hung", "untidy", "killed"]),
29
+ allowed: allowedActionsSchema,
28
30
  readonly: readonlySchema.optional(),
29
31
  timeline: activitySnapshotSchema,
30
32
  strandedReason: z.literal("resume-unsupported").optional(),
@@ -1,8 +1,28 @@
1
+ import { z } from "zod";
1
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"];
2
3
  export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
3
4
  export type AllowedActions = readonly AllowedAction[];
4
5
  export declare function isAllowedAction(value: unknown): value is AllowedAction;
5
6
  export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
7
+ /** Public codecs retain the Soul's canonical, duplicate-free action membership. */
8
+ export declare const allowedActionsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
9
+ "task.add": "task.add";
10
+ "task.addDocument": "task.addDocument";
11
+ "task.compose": "task.compose";
12
+ "task.done": "task.done";
13
+ "task.drop": "task.drop";
14
+ "task.hold": "task.hold";
15
+ "task.resume": "task.resume";
16
+ "task.start": "task.start";
17
+ "task.stop": "task.stop";
18
+ "task.update": "task.update";
19
+ "akuma.call": "akuma.call";
20
+ "akuma.kill": "akuma.kill";
21
+ "akuma.tell": "akuma.tell";
22
+ "contract.audit": "contract.audit";
23
+ "contract.deliver": "contract.deliver";
24
+ "contract.review": "contract.review";
25
+ }>>, 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")[]>>;
6
26
  export declare function effectiveAllowedActions(value: unknown): AllowedActions;
7
27
  export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
8
28
  export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
@@ -1,4 +1,5 @@
1
1
  import { TASK_MUTATION_ACTIONS } from "../task/mutation.js";
2
+ import { z } from "zod";
2
3
  export const ALLOWED_ACTIONS = Object.freeze([
3
4
  "akuma.call",
4
5
  "akuma.kill",
@@ -25,6 +26,16 @@ export function decodeAllowedActions(value, label = "allowed") {
25
26
  }
26
27
  return Object.freeze([...seen].sort());
27
28
  }
29
+ /** Public codecs retain the Soul's canonical, duplicate-free action membership. */
30
+ export const allowedActionsSchema = z.array(z.enum(ALLOWED_ACTIONS)).transform((value, context) => {
31
+ try {
32
+ return decodeAllowedActions(value);
33
+ }
34
+ catch (error) {
35
+ context.addIssue({ code: "custom", message: error instanceof Error ? error.message : String(error) });
36
+ return z.NEVER;
37
+ }
38
+ });
28
39
  export function effectiveAllowedActions(value) {
29
40
  return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
30
41
  }
@@ -132,6 +132,7 @@ function decodeArchetypeFields(values) {
132
132
  const effort = archetypeField(values, "effort");
133
133
  const readonly = archetypeReadonly(values);
134
134
  const network = archetypeEnum(values, "network", ["disabled", "enabled"]);
135
+ const sandbox = archetypeEnum(values, "sandbox", ["full-access"]);
135
136
  const description = archetypeField(values, "description");
136
137
  const allowedPresent = "allowed" in values;
137
138
  const allowed = allowedPresent ? effectiveAllowedActions(values.allowed) : undefined;
@@ -143,6 +144,7 @@ function decodeArchetypeFields(values) {
143
144
  ...(effort === undefined ? {} : { effort }),
144
145
  ...(readonly === undefined ? {} : { readonly }),
145
146
  ...(network === undefined ? {} : { network }),
147
+ ...(sandbox === undefined ? {} : { sandbox }),
146
148
  ...(description === undefined ? {} : { description }),
147
149
  ...(allowed === undefined ? {} : { allowed }),
148
150
  allowedPresent,
@@ -166,6 +168,7 @@ function decodeArchetype(name, path, markdown) {
166
168
  ...(fields.model === undefined ? {} : { model: fields.model }),
167
169
  ...(fields.effort === undefined ? {} : { effort: fields.effort }),
168
170
  ...(fields.network === undefined ? {} : { network: fields.network }),
171
+ ...(fields.sandbox === undefined ? {} : { sandbox: fields.sandbox }),
169
172
  ...(systemPrompt.length === 0
170
173
  ? {}
171
174
  : {
@@ -12,6 +12,7 @@ export type BodyLaunch = Readonly<{
12
12
  seed?: Omit<Soul, "createdAt">;
13
13
  birthSession?: Omit<SessionFact, "sequence">;
14
14
  initialBody?: string;
15
+ initiator?: string;
15
16
  initialSchemaJson?: string;
16
17
  refuseIfHeld?: boolean;
17
18
  completion?: Readonly<{
@@ -53,16 +54,35 @@ type BodyRuntime = Readonly<{
53
54
  world: WorldRoot;
54
55
  externalCommands: Readonly<Record<string, ErasedRequestCommand>>;
55
56
  }>;
56
- export declare function bodyProcessInput(launch: BodyLaunch, bodyModuleUrl?: string): Promise<{
57
+ type RuntimeEnvironment = Readonly<{
58
+ /** The current process executable, preferred only while it is a different live value. */
59
+ current?: string;
60
+ /** The present PATH used to resolve the recorded executable basename. */
61
+ path?: string | undefined;
62
+ exists?: (path: string) => boolean | Promise<boolean>;
63
+ }>;
64
+ /** One Body launch's runtime input: the runtime recorded for it and its resolution environment. */
65
+ export type BodyLaunchRuntime = Readonly<{
66
+ recorded?: string;
67
+ environment?: RuntimeEnvironment;
68
+ }>;
69
+ /**
70
+ * Resolve the runtime executable a Body spawn will use. The path recorded for
71
+ * this process's birth wins while it exists. Once it is gone, wake re-resolves
72
+ * the runtime before any spawn can fail: the current process executable while
73
+ * that is a different live value, then the recorded executable basename on the
74
+ * present PATH. An unresolvable runtime refuses with the stale record, the
75
+ * failed re-resolution, and the kill-and-call-fresh remedy.
76
+ */
77
+ export declare function resolveRuntimeExecutable(recorded?: string, environment?: RuntimeEnvironment): Promise<string>;
78
+ export declare function bodyProcessInput(launch: BodyLaunch, bodyModuleUrl?: string, runtime?: BodyLaunchRuntime): Promise<{
57
79
  argv: string[];
58
80
  cwd: string;
59
- env: {
60
- KEIYAKU_ACTOR_ID: import("./identity.js").AkuId;
61
- };
81
+ env: Record<string, string>;
62
82
  log: string;
63
83
  }>;
64
84
  export declare function handoffPendingTells(paths: AkumaPaths, spawn?: (launch: BodyLaunch) => Promise<OwnedProcess>): Promise<void>;
65
85
  export declare function driveAkumaBody(launch: BodyLaunch, adapter?: ProviderAdapter, runtimeInput?: Partial<BodyRuntime>): Promise<"held" | void>;
66
- export declare function spawnAkumaBody(launch: BodyLaunch): Promise<OwnedProcess>;
86
+ export declare function spawnAkumaBody(launch: BodyLaunch, runtime?: BodyLaunchRuntime): Promise<OwnedProcess>;
67
87
  export declare function runAkumaBody(launch: BodyLaunch, world: WorldRoot, externalCommands: Readonly<Record<string, ErasedRequestCommand>>): Promise<"held" | void>;
68
88
  export declare function wakeRecordedTell(paths: AkumaPaths, tellId: string, runtime?: TellWakeRuntime): Promise<TellResult>;
@@ -1,10 +1,12 @@
1
- import { appendFile } from "node:fs/promises";
1
+ import { appendFile, stat } from "node:fs/promises";
2
+ import { basename, delimiter, join } from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { abortableDelay } from "./abort.js";
4
5
  import { BodySupervisor } from "./body-supervisor.js";
5
6
  import { driveTurn, turnRecipe } from "./turn-drive.js";
6
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
8
  import { worldRootForAkumaPaths } from "./identity.js";
9
+ import { akumaExecutionEnvironment } from "./providers/execution-environment.js";
8
10
  import { pluginRuntime } from "../plugin/runtime.js";
9
11
  import { World } from "../world.js";
10
12
  import { resolveProviderExecution } from "./providers/index.js";
@@ -144,19 +146,71 @@ const DEFAULT_RUNTIME = {
144
146
  externalCommands: {},
145
147
  spawnBody: spawnAkumaBody,
146
148
  };
147
- export async function bodyProcessInput(launch, bodyModuleUrl = import.meta.url) {
149
+ /**
150
+ * The runtime executable this process recorded at its birth. A package-manager
151
+ * upgrade can remove the file under a live process, so a wake re-resolves that
152
+ * record instead of surfacing a bare missing-file launch failure.
153
+ */
154
+ const RECORDED_RUNTIME = process.execPath;
155
+ async function runtimeFileExists(path) {
156
+ try {
157
+ return (await stat(path)).isFile();
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ async function runtimeCommandOnPath(command, path, exists) {
164
+ if (command.length === 0 || command.includes("/") || command.includes("\\"))
165
+ return null;
166
+ for (const directory of (path ?? "").split(delimiter)) {
167
+ if (directory.length === 0)
168
+ continue;
169
+ const candidate = join(directory, command);
170
+ if (await exists(candidate))
171
+ return candidate;
172
+ }
173
+ return null;
174
+ }
175
+ class StaleRuntimeError extends Error {
176
+ constructor(recorded, current, command) {
177
+ super(`recorded Body runtime ${recorded} no longer exists and re-resolution failed: neither this process executable ` +
178
+ `${current} nor the command "${command}" on PATH names an existing file; kill this Akuma and call a fresh one`);
179
+ this.name = "StaleRuntimeError";
180
+ }
181
+ }
182
+ /**
183
+ * Resolve the runtime executable a Body spawn will use. The path recorded for
184
+ * this process's birth wins while it exists. Once it is gone, wake re-resolves
185
+ * the runtime before any spawn can fail: the current process executable while
186
+ * that is a different live value, then the recorded executable basename on the
187
+ * present PATH. An unresolvable runtime refuses with the stale record, the
188
+ * failed re-resolution, and the kill-and-call-fresh remedy.
189
+ */
190
+ export async function resolveRuntimeExecutable(recorded = RECORDED_RUNTIME, environment = {}) {
191
+ const exists = environment.exists ?? runtimeFileExists;
192
+ if (await exists(recorded))
193
+ return recorded;
194
+ const current = environment.current ?? process.execPath;
195
+ const command = basename(recorded);
196
+ const reResolved = (current !== recorded && (await exists(current)) ? current : null) ??
197
+ (await runtimeCommandOnPath(command, environment.path ?? process.env.PATH, exists));
198
+ if (reResolved === null)
199
+ throw new StaleRuntimeError(recorded, current, command);
200
+ return reResolved;
201
+ }
202
+ export async function bodyProcessInput(launch, bodyModuleUrl = import.meta.url, runtime = {}) {
148
203
  const encoded = Buffer.from(JSON.stringify(launch), "utf8").toString("base64url");
149
204
  const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
150
205
  if (actorId === undefined)
151
206
  throw new Error("Akuma wake has no born soul");
207
+ const executable = await resolveRuntimeExecutable(runtime.recorded, runtime.environment);
152
208
  const source = bodyModuleUrl.endsWith(".ts");
153
209
  const entry = fileURLToPath(new URL(source ? "../akuma-body.ts" : "../akuma-body.js", bodyModuleUrl));
154
210
  return {
155
- argv: source
156
- ? [process.execPath, "--import", import.meta.resolve("tsx"), entry, encoded]
157
- : [process.execPath, entry, encoded],
211
+ argv: source ? [executable, "--import", import.meta.resolve("tsx"), entry, encoded] : [executable, entry, encoded],
158
212
  cwd: await launchCwd(launch),
159
- env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
213
+ env: akumaExecutionEnvironment(process.env, { KEIYAKU_ACTOR_ID: actorId }),
160
214
  log: launch.paths.log,
161
215
  };
162
216
  }
@@ -231,14 +285,13 @@ async function recoverBodyRequests(input) {
231
285
  }
232
286
  function turnOutcomeEmitter(launch, soul) {
233
287
  let plugins;
234
- const pending = new Set();
235
288
  const reportDiagnostic = (message) => {
236
289
  void recordPluginDiagnostic(launch.paths, "turn outcome", message);
237
290
  };
238
- const emit = (turnSequence, outcome) => {
239
- let delivery;
240
- delivery = (async () => {
291
+ return {
292
+ async emit(turnSequence, outcome) {
241
293
  try {
294
+ const initiator = (await readTurn(launch.paths, turnSequence))?.initiator;
242
295
  plugins ??= pluginRuntime({
243
296
  world: await World.at(worldRootForAkumaPaths(launch.paths)),
244
297
  reportDiagnostic,
@@ -247,6 +300,7 @@ function turnOutcomeEmitter(launch, soul) {
247
300
  kind: "akuma.turn-outcome",
248
301
  akumaId: soul.id,
249
302
  turnSequence,
303
+ ...(initiator === undefined ? {} : { initiator }),
250
304
  outcome: outcome.outcome === "answered"
251
305
  ? { kind: "answered", text: outcome.answer }
252
306
  : { kind: "failed", reason: outcome.diagnostic },
@@ -256,17 +310,8 @@ function turnOutcomeEmitter(launch, soul) {
256
310
  catch (error) {
257
311
  await recordPluginDiagnostic(launch.paths, "turn outcome", error);
258
312
  }
259
- })().finally(() => pending.delete(delivery));
260
- pending.add(delivery);
261
- return delivery;
313
+ },
262
314
  };
263
- const drain = async () => {
264
- while (pending.size > 0)
265
- await Promise.all([...pending]);
266
- if (plugins !== undefined)
267
- await (await plugins).drain("akuma.turn-outcome");
268
- };
269
- return { emit, drain };
270
315
  }
271
316
  function bodyEndEmitter(launch, soul) {
272
317
  let plugins;
@@ -288,7 +333,6 @@ function bodyEndEmitter(launch, soul) {
288
333
  ...(diagnostic === undefined ? {} : { diagnostic }),
289
334
  ...(launch.completion?.contractId === undefined ? {} : { contractId: launch.completion.contractId }),
290
335
  }, reportDiagnostic);
291
- await (await plugins).drain("akuma.body-ended");
292
336
  }
293
337
  catch (error) {
294
338
  await recordPluginDiagnostic(launch.paths, "body end", error);
@@ -324,6 +368,7 @@ async function runBodyTurns(input) {
324
368
  runtimeSpawn: runtime.spawnChild ?? spawnAkumaBody,
325
369
  body: initial ?? "",
326
370
  ...(initial === undefined ? {} : { call: initial }),
371
+ ...(launch.initiator === undefined ? {} : { initiator: launch.initiator }),
327
372
  launchTells,
328
373
  ...(schema === undefined ? {} : { schemaJson: schema }),
329
374
  world: runtime.world,
@@ -346,7 +391,7 @@ async function runBodyTurns(input) {
346
391
  return;
347
392
  }
348
393
  const outcome = await persistTurn(launch.paths, result.turnSequence, result, runtime.now());
349
- void turnOutcome.emit(result.turnSequence, outcome);
394
+ await turnOutcome.emit(result.turnSequence, outcome);
350
395
  if (outcome.outcome === "failed") {
351
396
  await failOpenBoundTurnsIfPresent(launch.paths, bodySequence, outcome.diagnostic, runtime.now());
352
397
  await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
@@ -508,19 +553,13 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
508
553
  await resolveDecidedPendingTellDisposition(launch.paths, finished.decided, runtime.spawnBody ?? spawnAkumaBody);
509
554
  if (finished.notification !== undefined)
510
555
  await finished.notification;
511
- void emitTurnOutcome?.drain();
512
556
  }
513
557
  }
514
- export async function spawnAkumaBody(launch) {
515
- return await spawnDetachedProcess(await bodyProcessInput(launch));
558
+ export async function spawnAkumaBody(launch, runtime = {}) {
559
+ return await spawnDetachedProcess(await bodyProcessInput(launch, import.meta.url, runtime));
516
560
  }
517
561
  export async function runAkumaBody(launch, world, externalCommands) {
518
- try {
519
- return await driveAkumaBody(launch, undefined, { world, externalCommands });
520
- }
521
- finally {
522
- await (await pluginRuntime({ world })).drain();
523
- }
562
+ return await driveAkumaBody(launch, undefined, { world, externalCommands });
524
563
  }
525
564
  const DIRECT_TELL_WAKE = {
526
565
  spawn: async (paths) => await spawnAkumaBody({ paths, refuseIfHeld: true }),
@@ -19,6 +19,7 @@ declare const akumaCallRecipeSchema: z.ZodObject<{
19
19
  effort?: string;
20
20
  readonly?: true;
21
21
  network?: "disabled" | "enabled";
22
+ sandbox?: "full-access";
22
23
  systemPrompt?: string;
23
24
  systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
24
25
  }>, unknown>>;
@@ -28,6 +29,8 @@ declare const akumaCallPayloadSchema: z.ZodObject<{
28
29
  world: z.ZodString;
29
30
  archetype: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
30
31
  body: z.ZodOptional<z.ZodString>;
32
+ initiator: z.ZodOptional<z.ZodString>;
33
+ awaitAsleep: z.ZodOptional<z.ZodLiteral<true>>;
31
34
  cwd: z.ZodOptional<z.ZodString>;
32
35
  recipe: z.ZodObject<{
33
36
  description: z.ZodOptional<z.ZodString>;
@@ -44,6 +47,7 @@ declare const akumaCallPayloadSchema: z.ZodObject<{
44
47
  effort?: string;
45
48
  readonly?: true;
46
49
  network?: "disabled" | "enabled";
50
+ sandbox?: "full-access";
47
51
  systemPrompt?: string;
48
52
  systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
49
53
  }>, unknown>>;
@@ -58,6 +62,7 @@ export type AkumaCallRequestChildLaunch = Readonly<{
58
62
  paths: AkumaPaths;
59
63
  seed: Omit<Soul, "createdAt">;
60
64
  initialBody?: string;
65
+ initiator?: string;
61
66
  }>;
62
67
  type AkumaCallRequestCapabilities = Readonly<{
63
68
  world: WorldRoot;
@@ -4,7 +4,7 @@ import { refuseRequest, reserveRequest } from "./heart/index.js";
4
4
  import { archetypeName, parseAkuId } from "./identity.js";
5
5
  import { publishAkuma } from "./publication.js";
6
6
  import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
7
- import { decodeProviderExecution } from "./providers/index.js";
7
+ import { decodeProviderExecution, resolveProviderExecution } from "./providers/index.js";
8
8
  import { requestBodyCommand } from "./request-rendezvous.js";
9
9
  import { eraseRequestCommand, } from "./request-wire.js";
10
10
  import { z } from "zod";
@@ -67,6 +67,8 @@ const akumaCallPayloadSchema = z
67
67
  world: absolutePathSchema,
68
68
  archetype: archetypeSchema,
69
69
  body: z.string().optional(),
70
+ initiator: z.string().min(1).optional(),
71
+ awaitAsleep: z.literal(true).optional(),
70
72
  cwd: absolutePathSchema.optional(),
71
73
  recipe: akumaCallRecipeSchema,
72
74
  })
@@ -86,17 +88,24 @@ async function executeAkumaCall(request, facts, capabilities) {
86
88
  await refuseRequest(paths, facts.id, `request world ${requestWorld} does not match ${world}`);
87
89
  throw new Error(`request world ${requestWorld} does not match ${world}`);
88
90
  }
91
+ const selected = await resolveProviderExecution(request.recipe.provider);
92
+ const admission = selected.adapter.admitOptions(request.recipe.options);
93
+ if (admission.kind === "refused") {
94
+ await refuseRequest(paths, facts.id, admission.diagnostic);
95
+ throw new Error(admission.diagnostic);
96
+ }
89
97
  const recipe = {
90
98
  ...(request.recipe.description === undefined ? {} : { description: request.recipe.description }),
91
99
  allowed: clipAllowedActions(request.recipe.allowed, parent.allowed),
92
- provider: request.recipe.provider,
93
- options: request.recipe.options,
94
- ...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
100
+ provider: selected.execution,
101
+ options: admission.options,
102
+ ...(admission.readonly === undefined ? {} : { readonly: admission.readonly }),
95
103
  };
96
104
  const published = await publishAkuma({
97
105
  worldPath: world,
98
106
  archetype: request.archetype,
99
107
  signal: facts.signal,
108
+ ...(request.awaitAsleep === undefined ? {} : { awaitAsleep: request.awaitAsleep }),
100
109
  launch: async (allocated) => {
101
110
  if (!facts.admissionOpen())
102
111
  throw new Error("body closed request admission");
@@ -111,6 +120,7 @@ async function executeAkumaCall(request, facts, capabilities) {
111
120
  origin: { kind: "request", parent: parent.id, requestId: facts.id },
112
121
  },
113
122
  ...(request.body === undefined ? {} : { initialBody: request.body }),
123
+ ...(request.initiator === undefined ? {} : { initiator: request.initiator }),
114
124
  });
115
125
  },
116
126
  });
@@ -1,12 +1,39 @@
1
1
  import { type AkumaStatus } from "./akuma.js";
2
+ import { type DispatchAssociation } from "./dispatch-association.js";
3
+ import type { AkumaAlias } from "../identity/selector.js";
2
4
  import type { WorldRoot } from "../world.js";
3
5
  import { type AkumaKillResult, type AkumaTellResult, type AkumaWaitResult } from "./fleet-observation.js";
6
+ export type WaitIdentityFacts = Readonly<{
7
+ /** The alias currently addressing this Akuma in its world, when one is bound to it. */
8
+ alias?: AkumaAlias;
9
+ /** This Akuma's Dispatch association, read from the observing repository. */
10
+ contract: DispatchAssociation;
11
+ }>;
12
+ /** One observed Akuma as a live wait viewer sees it: its status plus its identity facts. */
13
+ export type WaitObservedAkuma = Readonly<{
14
+ status: AkumaStatus;
15
+ }> & WaitIdentityFacts;
16
+ /** One selected Akuma's frozen identity, resolved before the first observation round. */
17
+ export type WaitSelectedAkuma = Readonly<{
18
+ id: AkumaStatus["id"];
19
+ }> & WaitIdentityFacts;
20
+ /** A live wait viewer: the frozen selected set, then every observation round. */
21
+ export type WaitObserver = Readonly<{
22
+ selected?: (selected: readonly WaitSelectedAkuma[]) => void;
23
+ observe?: (observed: readonly WaitObservedAkuma[]) => void;
24
+ }>;
4
25
  export type WaitExecutionInput = Readonly<{
5
26
  path: WorldRoot;
6
27
  ids: readonly AkumaStatus["id"][];
7
28
  completion: "any" | "all";
8
29
  timeoutMs?: number;
9
30
  signal?: AbortSignal;
31
+ /** Resolves one Akuma's identity facts; consulted once per Akuma a viewer first sees. */
32
+ identity?: (id: AkumaStatus["id"]) => Promise<WaitIdentityFacts>;
33
+ /** Reports the frozen selected set before the first round, so a viewer can fix its layout. */
34
+ onSelected?: (selected: readonly WaitSelectedAkuma[]) => void;
35
+ /** Reports every observation round to a live viewer; absent keeps the cheap completion probe. */
36
+ observe?: (observed: readonly WaitObservedAkuma[]) => void;
10
37
  }>;
11
38
  export declare function executeWaitAkuma(input: WaitExecutionInput): Promise<AkumaWaitResult>;
12
39
  export type TellExecutionInput = Readonly<{
@@ -15,6 +42,7 @@ export type TellExecutionInput = Readonly<{
15
42
  body: string;
16
43
  tellId?: string;
17
44
  recordedAt?: string;
45
+ initiator?: string;
18
46
  signal?: AbortSignal;
19
47
  }>;
20
48
  export declare function executeTellAkuma(input: TellExecutionInput): Promise<AkumaTellResult>;
@@ -89,10 +89,44 @@ function delay(milliseconds, signal) {
89
89
  }
90
90
  export async function executeWaitAkuma(input) {
91
91
  const deadline = input.timeoutMs === undefined ? undefined : performance.now() + input.timeoutMs;
92
+ // Identity facts are read once per observed Akuma, not once per 100ms round.
93
+ const facts = new Map();
94
+ if (input.onSelected !== undefined) {
95
+ const selected = [];
96
+ for (const id of input.ids) {
97
+ input.signal?.throwIfAborted();
98
+ const known = input.identity === undefined ? undefined : await input.identity(id);
99
+ const resolved = known ?? { contract: NO_DISPATCH_ASSOCIATION };
100
+ facts.set(id, resolved);
101
+ selected.push({ id, ...resolved });
102
+ }
103
+ input.onSelected(selected);
104
+ }
105
+ const observeRound = async (statuses) => {
106
+ if (input.observe === undefined)
107
+ return;
108
+ const observed = [];
109
+ for (const status of statuses) {
110
+ input.signal?.throwIfAborted();
111
+ let known = facts.get(status.id);
112
+ if (known === undefined && input.identity !== undefined) {
113
+ known = await input.identity(status.id);
114
+ facts.set(status.id, known);
115
+ }
116
+ if (known === undefined) {
117
+ observed.push({ status, contract: NO_DISPATCH_ASSOCIATION });
118
+ continue;
119
+ }
120
+ observed.push({ status, ...known });
121
+ }
122
+ input.observe(observed);
123
+ };
92
124
  for (;;) {
93
- if ((deadline !== undefined && performance.now() >= deadline) || (await probeWaitRound(input))) {
125
+ const expired = deadline !== undefined && performance.now() >= deadline;
126
+ if (expired || input.observe !== undefined || (await probeWaitRound(input))) {
94
127
  const round = await observeWaitRound(input.path, input.ids, input.signal);
95
128
  input.signal?.throwIfAborted();
129
+ await observeRound(round.statuses);
96
130
  // The probe is not a completion receipt. Judge the actual returned values.
97
131
  if (roundComplete(round, input.completion) || (deadline !== undefined && performance.now() >= deadline)) {
98
132
  return fleetResultSchemas.wait.parse({
@@ -108,9 +142,7 @@ export async function executeWaitAkuma(input) {
108
142
  export async function executeTellAkuma(input) {
109
143
  input.signal?.throwIfAborted();
110
144
  const handle = source(input.path).selectHandle({ id: input.id });
111
- const tell = input.tellId === undefined
112
- ? await handle.tell(input.body)
113
- : await handle.tell(input.body, input.tellId, input.recordedAt);
145
+ const tell = await handle.tell(input.body, input.tellId, input.recordedAt, undefined, input.initiator === undefined ? {} : { initiator: input.initiator });
114
146
  input.signal?.throwIfAborted();
115
147
  return fleetResultSchemas.tell.parse({
116
148
  akuma: input.id,