@astrosheep/keiyaku 4.6.7 → 4.6.8

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 (165) hide show
  1. package/README.md +6 -2
  2. package/build/plugins/pi/keiyaku.ts +0 -5
  3. package/build/src/akuma/akuma-call.d.ts +2 -0
  4. package/build/src/akuma/akuma-call.js +8 -2
  5. package/build/src/akuma/akuma-instance.d.ts +12 -1
  6. package/build/src/akuma/akuma-instance.js +27 -7
  7. package/build/src/akuma/akuma-observe.js +2 -0
  8. package/build/src/akuma/akuma-owner.d.ts +3 -0
  9. package/build/src/akuma/akuma-owner.js +4 -0
  10. package/build/src/akuma/akuma.d.ts +35 -5
  11. package/build/src/akuma/akuma.js +8 -10
  12. package/build/src/akuma/archetype.d.ts +7 -15
  13. package/build/src/akuma/archetype.js +36 -35
  14. package/build/src/akuma/body.d.ts +5 -3
  15. package/build/src/akuma/body.js +29 -7
  16. package/build/src/akuma/call-request.d.ts +18 -10
  17. package/build/src/akuma/call-request.js +10 -3
  18. package/build/src/akuma/caller.d.ts +39 -0
  19. package/build/src/akuma/caller.js +36 -0
  20. package/build/src/akuma/dispatch-association.d.ts +0 -1
  21. package/build/src/akuma/dispatch-association.js +0 -3
  22. package/build/src/akuma/heart/facts.d.ts +16 -12
  23. package/build/src/akuma/heart/facts.js +8 -0
  24. package/build/src/akuma/heart/index.d.ts +2 -7
  25. package/build/src/akuma/heart/index.js +3 -17
  26. package/build/src/akuma/heart/rows.d.ts +3 -3
  27. package/build/src/akuma/heart/soul.d.ts +1 -1
  28. package/build/src/akuma/heart/soul.js +5 -24
  29. package/build/src/akuma/heart/storage.d.ts +5 -0
  30. package/build/src/akuma/heart/storage.js +7 -3
  31. package/build/src/akuma/identity.d.ts +0 -1
  32. package/build/src/akuma/identity.js +2 -5
  33. package/build/src/akuma/index.d.ts +5 -2
  34. package/build/src/akuma/index.js +1 -0
  35. package/build/src/akuma/nuke.js +6 -28
  36. package/build/src/akuma/projection.d.ts +0 -11
  37. package/build/src/akuma/provider-recipe.d.ts +7 -6
  38. package/build/src/akuma/provider-recipe.js +1 -1
  39. package/build/src/akuma/provider.d.ts +6 -10
  40. package/build/src/akuma/provider.js +6 -7
  41. package/build/src/akuma/providers/acp/events.js +5 -18
  42. package/build/src/akuma/providers/acp/index.js +2 -3
  43. package/build/src/akuma/providers/claude/events.js +35 -60
  44. package/build/src/akuma/providers/claude/index.d.ts +2 -2
  45. package/build/src/akuma/providers/claude/index.js +2 -3
  46. package/build/src/akuma/providers/codex-app-server/index.js +2 -2
  47. package/build/src/akuma/providers/decode.d.ts +25 -0
  48. package/build/src/akuma/providers/decode.js +44 -0
  49. package/build/src/akuma/providers/execution-environment.d.ts +2 -1
  50. package/build/src/akuma/providers/execution-environment.js +3 -0
  51. package/build/src/akuma/providers/grok-build/index.js +6 -22
  52. package/build/src/akuma/providers/index.js +2 -0
  53. package/build/src/akuma/providers/lifecycle.d.ts +9 -0
  54. package/build/src/akuma/providers/lifecycle.js +10 -0
  55. package/build/src/akuma/providers/opencode-sdk/events.js +1 -14
  56. package/build/src/akuma/providers/opencode-sdk/index.js +10 -17
  57. package/build/src/akuma/providers/opencode-sdk/session.js +1 -3
  58. package/build/src/akuma/providers/paseo/events.d.ts +25 -0
  59. package/build/src/akuma/providers/paseo/events.js +157 -0
  60. package/build/src/akuma/providers/paseo/index.d.ts +9 -0
  61. package/build/src/akuma/providers/paseo/index.js +353 -0
  62. package/build/src/akuma/providers/pi/events.js +5 -15
  63. package/build/src/akuma/publication.d.ts +2 -0
  64. package/build/src/akuma/publication.js +5 -0
  65. package/build/src/akuma/request-lifecycle.d.ts +3 -0
  66. package/build/src/akuma/request-lifecycle.js +25 -4
  67. package/build/src/akuma/requests.d.ts +3 -1
  68. package/build/src/akuma/requests.js +32 -0
  69. package/build/src/akuma/selection-observation.d.ts +72 -2
  70. package/build/src/akuma/selection-observation.js +0 -6
  71. package/build/src/akuma/turn-drive.js +2 -5
  72. package/build/src/cli/akuma-caller.d.ts +3 -0
  73. package/build/src/cli/akuma-caller.js +21 -0
  74. package/build/src/cli/commands/akuma-grammar.js +7 -12
  75. package/build/src/cli/commands/akuma.js +12 -4
  76. package/build/src/cli/commands/contract-grammar.d.ts +2 -1
  77. package/build/src/cli/commands/contract-grammar.js +13 -15
  78. package/build/src/cli/commands/contract-help.d.ts +2 -1
  79. package/build/src/cli/commands/contract-help.js +31 -1
  80. package/build/src/cli/commands/contract.js +24 -53
  81. package/build/src/cli/commands/task.js +18 -35
  82. package/build/src/cli/parse.d.ts +2 -2
  83. package/build/src/cli/render/catalog.d.ts +2 -5
  84. package/build/src/cli/render/contract-history.js +3 -6
  85. package/build/src/cli/render/contract.js +15 -50
  86. package/build/src/cli/render/receipt.js +3 -10
  87. package/build/src/cli/render/task.d.ts +4 -6
  88. package/build/src/cli/render/task.js +9 -26
  89. package/build/src/cli/runtime.d.ts +1 -1
  90. package/build/src/cli/runtime.js +11 -17
  91. package/build/src/cli/selectors.d.ts +3 -6
  92. package/build/src/cli/selectors.js +22 -53
  93. package/build/src/dispatch/index.d.ts +3 -3
  94. package/build/src/dispatch/index.js +1 -2
  95. package/build/src/git/observe.d.ts +6 -2
  96. package/build/src/git/observe.js +6 -2
  97. package/build/src/index.d.ts +6 -6
  98. package/build/src/index.js +2 -2
  99. package/build/src/kanshi/index.d.ts +1 -1
  100. package/build/src/kanshi/read.js +21 -6
  101. package/build/src/library/address.js +2 -4
  102. package/build/src/library/akuma-creation.d.ts +5 -1
  103. package/build/src/library/akuma-creation.js +27 -47
  104. package/build/src/library/akumas.d.ts +5 -4
  105. package/build/src/library/akumas.js +13 -9
  106. package/build/src/library/bind.d.ts +14 -14
  107. package/build/src/library/bind.js +11 -7
  108. package/build/src/library/contract-address.d.ts +9 -0
  109. package/build/src/library/contract-address.js +71 -0
  110. package/build/src/library/contract-composition.d.ts +4 -7
  111. package/build/src/library/contract-composition.js +6 -18
  112. package/build/src/library/contract-creation.d.ts +3 -5
  113. package/build/src/library/contract-creation.js +19 -17
  114. package/build/src/library/contract-execution.js +18 -23
  115. package/build/src/library/contract-outcomes.d.ts +2 -14
  116. package/build/src/library/contract-outcomes.js +2 -13
  117. package/build/src/library/contract-types.d.ts +36 -2
  118. package/build/src/library/delivery.d.ts +0 -2
  119. package/build/src/library/delivery.js +2 -5
  120. package/build/src/library/keiyaku.d.ts +0 -5
  121. package/build/src/library/keiyaku.js +6 -12
  122. package/build/src/library/outcome.d.ts +500 -499
  123. package/build/src/library/outcome.js +10 -0
  124. package/build/src/library/reconcile.js +8 -16
  125. package/build/src/library/refusal.d.ts +2 -2
  126. package/build/src/library/repo.d.ts +3 -2
  127. package/build/src/library/repo.js +10 -5
  128. package/build/src/library/selection.d.ts +4 -4
  129. package/build/src/library/selection.js +12 -12
  130. package/build/src/plugin/akuma-signals.d.ts +2 -0
  131. package/build/src/plugin/akuma-signals.js +4 -0
  132. package/build/src/plugin/public.d.ts +27 -2
  133. package/build/src/plugin/runtime.js +2 -2
  134. package/build/src/protocol/amend.js +3 -2
  135. package/build/src/protocol/attempt.js +3 -8
  136. package/build/src/protocol/bind.d.ts +2 -2
  137. package/build/src/protocol/bind.js +1 -1
  138. package/build/src/protocol/deliver.js +3 -3
  139. package/build/src/protocol/operations.d.ts +1 -3
  140. package/build/src/protocol/operations.js +0 -8
  141. package/build/src/protocol/outcome.d.ts +1 -2
  142. package/build/src/protocol/outcome.js +1 -13
  143. package/build/src/protocol/placement.js +3 -10
  144. package/build/src/protocol/read/status.js +31 -70
  145. package/build/src/protocol/reintegrate.js +3 -10
  146. package/build/src/protocol/review.js +3 -3
  147. package/build/src/protocol/run.d.ts +5 -1
  148. package/build/src/protocol/run.js +3 -3
  149. package/build/src/runtime/proc/windows-launch.exe +0 -0
  150. package/package.json +8 -4
  151. package/plugins/pi/keiyaku.ts +57 -0
  152. package/skills/keiyaku/SKILL.md +44 -0
  153. package/skills/keiyaku-akuma/SKILL.md +130 -0
  154. package/skills/keiyaku-akuma/references/automation.md +269 -0
  155. package/skills/keiyaku-babysit/SKILL.md +65 -0
  156. package/skills/keiyaku-bind/SKILL.md +216 -0
  157. package/skills/keiyaku-bind/agents/openai.yaml +4 -0
  158. package/skills/keiyaku-task/SKILL.md +104 -0
  159. package/skills/keiyaku-workflow/SKILL.md +129 -0
  160. package/build/src/cli/commands/amend.d.ts +0 -12
  161. package/build/src/cli/commands/amend.js +0 -10
  162. package/build/src/cli/commands/bind.d.ts +0 -11
  163. package/build/src/cli/commands/bind.js +0 -23
  164. package/build/src/dispatch/association.d.ts +0 -1
  165. package/build/src/dispatch/association.js +0 -1
package/README.md CHANGED
@@ -186,6 +186,8 @@ lists every recognized namespace.
186
186
  npm install -g @astrosheep/keiyaku
187
187
  ```
188
188
 
189
+ Pi loads the extension from the package source and invokes the `keiyaku` CLI from `PATH`, so Pi users must have that command installed and available in the host environment.
190
+
189
191
  Node ≥ 22.19. Product law lives in [`docs/`](docs/README.md).
190
192
 
191
193
  ## Source builds
@@ -194,8 +196,10 @@ Host integration sources live in `plugins/pi`, `plugins/claude`, `plugins/codex`
194
196
  and `plugins/opencode`. Shared skills live in `skills/`; the build copies them
195
197
  into each host package so native installations are self-contained. The separate
196
198
  `plugins/square` workspace is Keiyaku's internal Square observer, not a coding
197
- harness plugin. Installation recipes and Pi package metadata select the built
198
- assets under `build/plugins` and `build/skills`.
199
+ harness plugin. Installation recipes and harness package metadata select the built
200
+ assets under `build/plugins` and `build/skills`, except the Pi package metadata,
201
+ which selects the source extension and `skills/` so a source checkout is loadable
202
+ without a build.
199
203
 
200
204
  Building from source also builds the Windows launcher with Zig 0.14.1 or later. Install
201
205
  that system tool separately and make `zig` available on `PATH`, or set
@@ -1,13 +1,8 @@
1
- import { existsSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
1
  import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
5
2
 
6
3
  type ExecResult = Readonly<{ stdout: string; stderr: string; code: number }>;
7
4
 
8
5
  async function runStatus(pi: ExtensionAPI): Promise<ExecResult> {
9
- const bundledCli = resolve(dirname(fileURLToPath(import.meta.url)), "../../src/cli/index.js");
10
- if (existsSync(bundledCli)) return await pi.exec(process.execPath, [bundledCli, "status"]);
11
6
  return await pi.exec("keiyaku", ["status"]);
12
7
  }
13
8
 
@@ -1,3 +1,4 @@
1
+ import { type AkumaCaller } from "./caller.js";
1
2
  import type { Settings } from "../settings.js";
2
3
  import type { WorldRoot } from "../world.js";
3
4
  import { type AllowedAction } from "./allowed.js";
@@ -17,6 +18,7 @@ export type AkumaCallInput = Readonly<{
17
18
  allowed?: readonly AllowedAction[];
18
19
  schema?: Schema<unknown>;
19
20
  initiator?: string;
21
+ caller?: AkumaCaller;
20
22
  signal?: AbortSignal;
21
23
  }>;
22
24
  type AkumaCallContext = Readonly<{
@@ -1,3 +1,4 @@
1
+ import { decodeAkumaCaller } from "./caller.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { settings as readSettings } from "../settings.js";
3
4
  import { readAkumaBirthCwd } from "./akuma-observe.js";
@@ -7,7 +8,7 @@ import { AkumaArchetypeError, loadPreparedArchetype } from "./archetype.js";
7
8
  import { spawnAkumaBody } from "./body.js";
8
9
  import { canonicalBirthCwd } from "./call-input.js";
9
10
  import { requestForwardedAkumaCall } from "./call-request.js";
10
- import { archetypeName } from "./identity.js";
11
+ import { validateAkumaName } from "../identity/normalize.js";
11
12
  import { PreparedCallAdmissionError, executePreparedCall } from "./publication.js";
12
13
  import { executionChannel } from "./requests.js";
13
14
  async function admitBodyRequest(input) {
@@ -24,6 +25,8 @@ async function admitBodyRequest(input) {
24
25
  ...(input.call.initialTell === undefined ? {} : { initialTell: input.call.initialTell }),
25
26
  ...(cwd === undefined ? {} : { cwd }),
26
27
  recipe: input.recipe,
28
+ ...(input.call.initiator === undefined ? {} : { birthInitiator: input.call.initiator }),
29
+ ...(input.call.contractId === undefined ? {} : { contractId: input.call.contractId }),
27
30
  ...(input.call.signal === undefined ? {} : { signal: input.call.signal }),
28
31
  });
29
32
  const bornCwd = await readAkumaBirthCwd(input.path, response.id);
@@ -50,6 +53,7 @@ async function admitDirect(input) {
50
53
  result = await executePreparedCall({
51
54
  archetype: input.archetype.name,
52
55
  cwd,
56
+ ...(input.call.caller === undefined ? {} : { caller: input.call.caller }),
53
57
  ...(input.call.initialTell === undefined ? {} : { initialTell: input.call.initialTell }),
54
58
  ...(input.call.signal === undefined ? {} : { signal: input.call.signal }),
55
59
  custody: {
@@ -59,6 +63,7 @@ async function admitDirect(input) {
59
63
  spawn: async (launch) => await spawnAkumaBody({
60
64
  paths: launch.paths,
61
65
  seed: launch.seed,
66
+ ...(input.call.initiator === undefined ? {} : { birthInitiator: input.call.initiator }),
62
67
  ...(input.call.contractId === undefined ? {} : { completion: { contractId: input.call.contractId } }),
63
68
  }),
64
69
  admitInitialTell: async ({ id, initialTell, signal }) => await new AkumaOwner(id, input.path).admitInitialTell(initialTell, {
@@ -93,7 +98,8 @@ async function admitDirect(input) {
93
98
  * then invoke the P3 prepared-call executor under local or request custody.
94
99
  */
95
100
  export async function admitAkumaCall(path, configuration, input, context) {
96
- const name = archetypeName(input.archetype);
101
+ decodeAkumaCaller(input.caller);
102
+ const name = validateAkumaName(input.archetype);
97
103
  const home = configuration.home === undefined ? {} : { home: configuration.home };
98
104
  const settings = configuration.settings ?? (await readSettings({ root: path, ...home }));
99
105
  const archetype = await loadPreparedArchetype({ name, project: path, ...home, settings });
@@ -1,3 +1,4 @@
1
+ import type { AkumaCaller } from "./caller.js";
1
2
  import type { Settings } from "../settings.js";
2
3
  import type { WorldRoot } from "../world.js";
3
4
  import { type InterruptReceipt } from "./akuma-owner.js";
@@ -24,12 +25,21 @@ export type AkumaHistoryOptions = Readonly<{
24
25
  export type AkumaSignalOptions = Readonly<{
25
26
  signal?: AbortSignal;
26
27
  }>;
28
+ /** Observes only plugin diagnostics produced in this host, never remote Body logs. */
29
+ export type AkumaHostOptions = Readonly<{
30
+ reportDiagnostic?: (message: string) => void;
31
+ }>;
32
+ /** Capture the observer capability, not the caller's mutable options object. */
33
+ export declare function captureAkumaHostDiagnostic(host: AkumaHostOptions | undefined): AkumaHostOptions["reportDiagnostic"];
27
34
  export type AkumaBirthInput = Readonly<{
28
35
  root: WorldRoot;
29
36
  cwd?: string;
30
37
  home?: string;
31
38
  settings?: Settings;
32
39
  allowed?: readonly AllowedAction[];
40
+ caller?: AkumaCaller;
41
+ initiator?: string;
42
+ host?: AkumaHostOptions;
33
43
  }>;
34
44
  export type AkumaTellOptions = AkumaSignalOptions & Readonly<{
35
45
  interrupt?: boolean;
@@ -43,10 +53,11 @@ export type { AkumaAskResult, AkumaTellResult, InterruptReceipt, KillEvidence };
43
53
  export declare class Akuma {
44
54
  readonly id: AkuId;
45
55
  private readonly root;
56
+ private readonly reportDiagnostic;
46
57
  private constructor();
47
58
  private owner;
48
59
  static birth(archetype: string, input: AkumaBirthInput): Promise<Akuma>;
49
- static select(root: WorldRoot, selector: string): Akuma;
60
+ static select(root: WorldRoot, selector: string, host?: AkumaHostOptions): Akuma;
50
61
  tell(text: string, options?: AkumaTellOptions): Promise<AkumaTellResult>;
51
62
  ask(text: string, options?: AkumaAskOptions<string>): Promise<AkumaAskResult<string>>;
52
63
  ask<T>(text: string, options: AkumaAskOptions<T> & Readonly<{
@@ -1,4 +1,4 @@
1
- import { outcomePluginObserver } from "../plugin/akuma-signals.js";
1
+ import { outcomePluginObserver, stderrPluginDiagnostic } from "../plugin/akuma-signals.js";
2
2
  import { abortable } from "./abort.js";
3
3
  import { admitAkumaCall } from "./akuma-call.js";
4
4
  import { defaultWaitComplete } from "./akuma-observe.js";
@@ -6,6 +6,20 @@ import { AkumaOwner } from "./akuma-owner.js";
6
6
  import { decodeAskObservation, executeAskAkuma, executeTellAkuma } from "./akuma-selection-execution.js";
7
7
  import { parseAkuId } from "./identity.js";
8
8
  import { schemaFromStandard, schemaJsonText } from "./schema.js";
9
+ /** Capture the observer capability, not the caller's mutable options object. */
10
+ export function captureAkumaHostDiagnostic(host) {
11
+ if (host === undefined)
12
+ return undefined;
13
+ if (typeof host !== "object" || host === null || Array.isArray(host))
14
+ throw new TypeError("Akuma host options must be an object");
15
+ const unknown = Object.keys(host).find((key) => key !== "reportDiagnostic");
16
+ if (unknown !== undefined)
17
+ throw new TypeError(`Akuma host options has unknown field: ${unknown}`);
18
+ const reportDiagnostic = host.reportDiagnostic;
19
+ if (reportDiagnostic !== undefined && typeof reportDiagnostic !== "function")
20
+ throw new TypeError("Akuma host reportDiagnostic must be a function");
21
+ return reportDiagnostic;
22
+ }
9
23
  function signalOption(value) {
10
24
  if (value === undefined)
11
25
  return undefined;
@@ -16,13 +30,15 @@ function signalOption(value) {
16
30
  export class Akuma {
17
31
  id;
18
32
  root;
19
- constructor(id, root) {
33
+ reportDiagnostic;
34
+ constructor(id, root, reportDiagnostic) {
20
35
  this.id = id;
21
36
  this.root = root;
37
+ this.reportDiagnostic = reportDiagnostic;
22
38
  Object.freeze(this);
23
39
  }
24
40
  owner(observing = false) {
25
- return new AkumaOwner(this.id, this.root, observing ? outcomePluginObserver({ world: this.root, reportDiagnostic: console.error }) : undefined);
41
+ return new AkumaOwner(this.id, this.root, observing ? outcomePluginObserver({ world: this.root, reportDiagnostic: this.reportDiagnostic }) : undefined);
26
42
  }
27
43
  static async birth(archetype, input) {
28
44
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
@@ -30,6 +46,7 @@ export class Akuma {
30
46
  }
31
47
  if (typeof input.root !== "string")
32
48
  throw new TypeError("Akuma birth root must be a WorldRoot");
49
+ const reportDiagnostic = captureAkumaHostDiagnostic(input.host) ?? stderrPluginDiagnostic;
33
50
  const admitted = await admitAkumaCall(input.root, {
34
51
  ...(input.home === undefined ? {} : { home: input.home }),
35
52
  ...(input.settings === undefined ? {} : { settings: input.settings }),
@@ -37,15 +54,18 @@ export class Akuma {
37
54
  archetype,
38
55
  ...(input.cwd === undefined ? {} : { cwd: input.cwd }),
39
56
  ...(input.allowed === undefined ? {} : { allowed: input.allowed }),
57
+ ...(input.caller === undefined ? {} : { caller: input.caller }),
58
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
40
59
  }, {});
41
60
  if (admitted.failure !== undefined)
42
61
  throw admitted.failure;
43
- return new Akuma(admitted.id, input.root);
62
+ return new Akuma(admitted.id, input.root, reportDiagnostic);
44
63
  }
45
- static select(root, selector) {
64
+ static select(root, selector, host) {
46
65
  if (typeof root !== "string")
47
66
  throw new TypeError("Akuma.select root must be a WorldRoot");
48
- return new Akuma(parseAkuId(selector).id, root);
67
+ const reportDiagnostic = captureAkumaHostDiagnostic(host) ?? stderrPluginDiagnostic;
68
+ return new Akuma(parseAkuId(selector).id, root, reportDiagnostic);
49
69
  }
50
70
  async tell(text, options = {}) {
51
71
  if (typeof text !== "string")
@@ -85,7 +105,7 @@ export class Akuma {
85
105
  const result = await executeAskAkuma({
86
106
  path: this.root,
87
107
  id: this.id,
88
- outcomeObserved: outcomePluginObserver({ world: this.root, reportDiagnostic: console.error }),
108
+ outcomeObserved: outcomePluginObserver({ world: this.root, reportDiagnostic: this.reportDiagnostic }),
89
109
  body: text,
90
110
  ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
91
111
  ...(schema === undefined ? {} : { schemaJson: schemaJsonText(schema) }),
@@ -1,3 +1,4 @@
1
+ import { projectAkumaBirth } from "./caller.js";
1
2
  import { readStatusFacts, createStatusFactReader, readLifeSnapshot, HeldAkumaLeash, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readSeal, readSoul, } from "./heart/index.js";
2
3
  import { pathsForAkuId } from "./identity.js";
3
4
  import { ordinarySnapshotBudget, projectTurns, selectHistory, selectSnapshot, } from "./projection.js";
@@ -124,6 +125,7 @@ export async function bornLiveStatus(paths, expected, input, signal, readProject
124
125
  return {
125
126
  status: {
126
127
  id: soul.id,
128
+ birth: projectAkumaBirth(soul),
127
129
  life: currentLife,
128
130
  cwd: soul.cwd,
129
131
  allowed: soul.allowed,
@@ -1,3 +1,4 @@
1
+ import { type AkumaCaller } from "./caller.js";
1
2
  import type { WorldRoot } from "../world.js";
2
3
  import { type OutcomeObserver, type LiveStatusObservation, type WaitReason } from "./akuma-observe.js";
3
4
  import type { AkumaStatus } from "./akuma.js";
@@ -147,6 +148,8 @@ export declare class AkumaOwner {
147
148
  }>): Promise<InterruptAdmission>;
148
149
  fork(input: Readonly<{
149
150
  at: string;
151
+ caller?: AkumaCaller;
152
+ initiator?: string;
150
153
  }>): Promise<ForkReceipt>;
151
154
  kill(options?: Readonly<{
152
155
  signal?: AbortSignal;
@@ -1,3 +1,4 @@
1
+ import { decodeAkumaCaller } from "./caller.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { AkumaNotBornError, AkumaProviderError } from "./akuma-errors.js";
3
4
  import { bornLiveStatus, bornStatus, defaultWaitComplete, observeOutcomeRows, observeStatusOutcome, waitForObservation, } from "./akuma-observe.js";
@@ -319,6 +320,7 @@ export class AkumaOwner {
319
320
  };
320
321
  }
321
322
  async fork(input) {
323
+ const caller = decodeAkumaCaller(input.caller);
322
324
  const source = await readSoul(this.paths);
323
325
  if (source === null)
324
326
  throw new AkumaNotBornError(this.id);
@@ -366,8 +368,10 @@ export class AkumaOwner {
366
368
  allowed: source.allowed,
367
369
  cwd: source.cwd,
368
370
  origin: { kind: "fork", parent: this.id, at: input.at },
371
+ ...(caller === undefined ? {} : { caller }),
369
372
  },
370
373
  birthSession,
374
+ ...(input.initiator === undefined ? {} : { birthInitiator: input.initiator }),
371
375
  });
372
376
  },
373
377
  });
@@ -3,7 +3,7 @@ import { type BoundedList } from "../bounded-list.js";
3
3
  import type { AkumaAlias } from "../identity/selector.js";
4
4
  import type { WorldRoot } from "../world.js";
5
5
  import type { AkumaLife } from "./heart/index.js";
6
- import { type AkuId } from "./identity.js";
6
+ import { type AkuId, type AkumaPaths } from "./identity.js";
7
7
  export type AkumaListRow = Readonly<{
8
8
  id: AkuId;
9
9
  archetype: string;
@@ -34,6 +34,30 @@ export type AkumaListInput = Readonly<{
34
34
  }>;
35
35
  export declare const akumaStatusSchema: z.ZodObject<{
36
36
  id: z.ZodPipe<z.ZodString, z.ZodTransform<AkuId, string>>;
37
+ birth: z.ZodObject<{
38
+ origin: z.ZodDiscriminatedUnion<[z.ZodObject<{
39
+ kind: z.ZodLiteral<"direct">;
40
+ }, z.core.$strip>, z.ZodObject<{
41
+ kind: z.ZodLiteral<"request">;
42
+ parent: z.ZodPipe<z.ZodString, z.ZodTransform<AkuId, string>>;
43
+ requestId: z.ZodString;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ kind: z.ZodLiteral<"fork">;
46
+ parent: z.ZodPipe<z.ZodString, z.ZodTransform<AkuId, string>>;
47
+ at: z.ZodString;
48
+ }, z.core.$strip>], "kind">;
49
+ caller: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
50
+ kind: z.ZodLiteral<"akuma">;
51
+ world: z.ZodCustom<WorldRoot, WorldRoot>;
52
+ akuId: z.ZodPipe<z.ZodString, z.ZodTransform<AkuId, string>>;
53
+ }, z.core.$strict>, z.ZodObject<{
54
+ kind: z.ZodLiteral<"external">;
55
+ integration: z.ZodString;
56
+ id: z.ZodString;
57
+ }, z.core.$strict>], "kind">, z.ZodObject<{
58
+ kind: z.ZodLiteral<"unknown">;
59
+ }, z.core.$strict>]>;
60
+ }, z.core.$strict>;
37
61
  life: z.ZodEnum<{
38
62
  killed: "killed";
39
63
  hung: "hung";
@@ -441,10 +465,16 @@ export declare const akumaStatusSchema: z.ZodObject<{
441
465
  strandedReason: z.ZodOptional<z.ZodLiteral<"resume-unsupported">>;
442
466
  }, z.core.$strict>;
443
467
  export type AkumaStatus = z.infer<typeof akumaStatusSchema>;
444
- export declare function parseAkumaStatus(value: unknown): AkumaStatus;
445
- export declare function listAkumaArchetypes(path: WorldRoot, input?: Readonly<{
446
- home?: string;
447
- }>): Promise<readonly string[]>;
468
+ /** One recognized Akuma directory under this World's run root. */
469
+ export type KnownAkuma = Readonly<{
470
+ id: AkuId;
471
+ paths: AkumaPaths;
472
+ }>;
473
+ /** Enumerate the recognized Akuma directories under this World's run root. */
474
+ export declare function knownAkuma(path: WorldRoot, selected?: string): Promise<Readonly<{
475
+ runRoot: string;
476
+ rows: readonly KnownAkuma[];
477
+ }>>;
448
478
  /**
449
479
  * The bounded recent-activity roster: native membership, semantic order and
450
480
  * observed extent. World Alias context is attached by the upper composition.
@@ -1,16 +1,18 @@
1
+ import { akumaBirthSchema } from "./caller.js";
1
2
  import { readdir, stat } from "node:fs/promises";
2
3
  import { z } from "zod";
3
4
  import { boundedListLimit, projectBoundedList } from "../bounded-list.js";
4
5
  import { rosterListRow } from "./akuma-observe.js";
5
6
  import { AkumaOwner } from "./akuma-owner.js";
6
7
  import { allowedActionsSchema } from "./allowed.js";
7
- import { listArchetypes as readArchetypes } from "./archetype.js";
8
- import { akuIdFromDirectoryName, akumaIdSchema, akumaPaths, akumaRunRoot, archetypeName, } from "./identity.js";
8
+ import { akuIdFromDirectoryName, akumaIdSchema, akumaPaths, akumaRunRoot, } from "./identity.js";
9
+ import { validateAkumaName } from "../identity/normalize.js";
9
10
  import { activitySnapshotSchema } from "./projection.js";
10
11
  import { PAGE_POOL_SIZE, boundedMap } from "./read-pool.js";
11
12
  export const akumaStatusSchema = z
12
13
  .object({
13
14
  id: akumaIdSchema,
15
+ birth: akumaBirthSchema,
14
16
  life: z.enum(["running", "asleep", "stranded", "hung", "untidy", "killed"]),
15
17
  cwd: z.string().optional(),
16
18
  allowed: allowedActionsSchema,
@@ -18,12 +20,6 @@ export const akumaStatusSchema = z
18
20
  strandedReason: z.literal("resume-unsupported").optional(),
19
21
  })
20
22
  .strict();
21
- export function parseAkumaStatus(value) {
22
- return akumaStatusSchema.parse(value);
23
- }
24
- export async function listAkumaArchetypes(path, input = {}) {
25
- return readArchetypes({ project: path, ...(input.home === undefined ? {} : { home: input.home }) });
26
- }
27
23
  function activityAt(row) {
28
24
  if (!("lifeAt" in row))
29
25
  return null;
@@ -59,7 +55,8 @@ async function mtimeBound(paths) {
59
55
  };
60
56
  return Math.max(await read(paths.heart), await read(`${paths.heart}-wal`));
61
57
  }
62
- async function knownAkuma(path, selected) {
58
+ /** Enumerate the recognized Akuma directories under this World's run root. */
59
+ export async function knownAkuma(path, selected) {
63
60
  const runRoot = akumaRunRoot(path);
64
61
  let names;
65
62
  try {
@@ -80,6 +77,7 @@ async function knownAkuma(path, selected) {
80
77
  physical = akuIdFromDirectoryName(name);
81
78
  }
82
79
  catch {
80
+ // The run root can contain bytes not produced by Keiyaku. Preserve them.
83
81
  continue;
84
82
  }
85
83
  if (selected !== undefined && physical.archetype !== selected)
@@ -109,7 +107,7 @@ function rosterArchetype(input, allowLimit) {
109
107
  const unknown = Object.keys(input).find((key) => !allowed.includes(key));
110
108
  if (unknown !== undefined)
111
109
  throw new TypeError(`Akuma list input has unknown field: ${unknown}`);
112
- return input.archetype === undefined ? undefined : archetypeName(input.archetype);
110
+ return input.archetype === undefined ? undefined : validateAkumaName(input.archetype);
113
111
  }
114
112
  /**
115
113
  * The bounded recent-activity roster: native membership, semantic order and
@@ -1,6 +1,5 @@
1
1
  import { type BoundedList } from "../bounded-list.js";
2
2
  import type { Settings } from "../settings.js";
3
- import type { ProviderAdapter } from "./provider.js";
4
3
  import { type ProviderExecution, type ProviderOptions } from "./provider-recipe.js";
5
4
  import { type AllowedActions } from "./allowed.js";
6
5
  type DecodedArchetype = Readonly<{
@@ -17,11 +16,12 @@ export type ArchetypeCatalogRow = Readonly<{
17
16
  model?: string;
18
17
  description?: string;
19
18
  }>;
20
- type ArchetypeDefinition = DecodedArchetype & Readonly<{
21
- adapter: ProviderAdapter;
22
- }>;
23
- type AdmittedArchetype = Omit<ArchetypeDefinition, "provider"> & Readonly<{
24
- provider: ProviderExecution;
19
+ export type ArchetypeCatalog = BoundedList<ArchetypeCatalogRow>;
20
+ /** One closed catalogue request: omitted project means no project scope, never ambient discovery. */
21
+ export type ArchetypeCatalogInput = Readonly<{
22
+ project?: string;
23
+ home?: string;
24
+ limit?: number;
25
25
  }>;
26
26
  /** One loaded archetype whose provider owner has decoded its execution but not yet admitted its options. */
27
27
  type PreparedArchetype = Omit<DecodedArchetype, "provider"> & Readonly<{
@@ -39,9 +39,7 @@ type ArchetypeCoordinates = Readonly<{
39
39
  home?: string;
40
40
  }>;
41
41
  export declare function listArchetypes(input?: ArchetypeCoordinates): Promise<readonly string[]>;
42
- export declare function listArchetypeDefinitions(input?: ArchetypeCoordinates & Readonly<{
43
- limit?: number;
44
- }>): Promise<BoundedList<ArchetypeCatalogRow>>;
42
+ export declare function listArchetypeDefinitions(input?: ArchetypeCatalogInput): Promise<ArchetypeCatalog>;
45
43
  /** Resolve one archetype and decode its provider execution, leaving option admission to the caller. */
46
44
  export declare function loadPreparedArchetype(input: Readonly<{
47
45
  name: string;
@@ -49,10 +47,4 @@ export declare function loadPreparedArchetype(input: Readonly<{
49
47
  home?: string;
50
48
  settings: Settings;
51
49
  }>): Promise<PreparedArchetype>;
52
- export declare function loadArchetype(input: Readonly<{
53
- name: string;
54
- project?: string;
55
- home?: string;
56
- settings: Settings;
57
- }>): Promise<AdmittedArchetype>;
58
50
  export {};
@@ -1,11 +1,10 @@
1
1
  import { readFile, readdir } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
- import { join } from "node:path";
3
+ import { isAbsolute, join } from "node:path";
4
4
  import { parseDocument } from "yaml";
5
5
  import { boundedListLimit } from "../bounded-list.js";
6
- import { archetypeName } from "./identity.js";
6
+ import { validateAkumaName } from "../identity/normalize.js";
7
7
  import { decodeProviderOptions, decodeProviderRecipe, } from "./provider-recipe.js";
8
- import { resolveProviderExecution } from "./providers/index.js";
9
8
  import { DEFAULT_ALLOWED_ACTIONS, decodeAllowedActions } from "./allowed.js";
10
9
  export class AkumaArchetypeError extends Error {
11
10
  archetype;
@@ -37,7 +36,7 @@ async function pathsInDirectory(directory) {
37
36
  return [];
38
37
  const name = entry.name.slice(0, -3);
39
38
  try {
40
- return [{ name: archetypeName(name), path: join(directory, entry.name) }];
39
+ return [{ name: validateAkumaName(name), path: join(directory, entry.name) }];
41
40
  }
42
41
  catch {
43
42
  return [];
@@ -127,7 +126,7 @@ function parseArchetypeFrontmatter(markdown) {
127
126
  function decodeArchetypeFields(values) {
128
127
  const provider = archetypeField(values, "provider");
129
128
  const baseValue = archetypeField(values, "base");
130
- const base = baseValue === undefined ? undefined : archetypeName(baseValue);
129
+ const base = baseValue === undefined ? undefined : validateAkumaName(baseValue);
131
130
  const model = archetypeField(values, "model");
132
131
  const effort = archetypeField(values, "effort");
133
132
  const network = archetypeEnum(values, "network", ["disabled", "enabled"]);
@@ -237,14 +236,41 @@ async function resolveArchetype(name, input, source, chain = []) {
237
236
  const missingChain = chain.length === 0 ? chain : [...chain, name];
238
237
  throw archetypeError(name, searched, "was not found", missingChain, chain.length === 0 ? "use `keiyaku ls aku/` to list available Akuma" : undefined);
239
238
  }
239
+ function catalogueCoordinate(value, field) {
240
+ if (value === undefined)
241
+ return undefined;
242
+ if (typeof value !== "string" || value.trim().length === 0) {
243
+ throw new TypeError(`Akuma catalogue ${field} must be a nonblank string`);
244
+ }
245
+ if (!isAbsolute(value))
246
+ throw new TypeError(`Akuma catalogue ${field} must be absolute: ${value}`);
247
+ return value;
248
+ }
249
+ /** Malformed caller input fails here, before any filesystem observation. */
250
+ function catalogueInput(value) {
251
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
252
+ throw new TypeError("Akuma catalogue input must be an object");
253
+ }
254
+ const input = value;
255
+ const unknown = Object.keys(input).find((key) => !["project", "home", "limit"].includes(key));
256
+ if (unknown !== undefined)
257
+ throw new TypeError(`Akuma catalogue input has unknown field: ${unknown}`);
258
+ const project = catalogueCoordinate(input.project, "project");
259
+ const home = catalogueCoordinate(input.home, "home");
260
+ return {
261
+ ...(project === undefined ? {} : { project }),
262
+ ...(home === undefined ? {} : { home }),
263
+ ...(input.limit === undefined ? {} : { limit: boundedListLimit(input.limit) }),
264
+ };
265
+ }
240
266
  export async function listArchetypeDefinitions(input = {}) {
241
267
  // Reads still run concurrently, but the reported failure is the first invalid
242
268
  // definition in catalog byte order, not whichever read happened to finish first.
243
- const limit = input.limit === undefined ? undefined : boundedListLimit(input.limit);
244
- const all = await archetypePaths(input);
269
+ const { limit, ...coordinates } = catalogueInput(input);
270
+ const all = await archetypePaths(coordinates);
245
271
  const settled = await Promise.allSettled(all.map(async ({ name, path }) => {
246
272
  try {
247
- return await resolveArchetype(name, input, undefined);
273
+ return await resolveArchetype(name, coordinates, undefined);
248
274
  }
249
275
  catch (error) {
250
276
  if (error instanceof AkumaArchetypeError)
@@ -307,6 +333,7 @@ const BUILTIN_EXECUTIONS = {
307
333
  executable: "kimi",
308
334
  config: { argvBefore: ["acp"], argvAfter: [], modelConfigId: "model", effortConfigId: "thinking" },
309
335
  },
336
+ paseo: { kind: "paseo" },
310
337
  pi: { kind: "pi" },
311
338
  "grok-build": {
312
339
  kind: "grok-build",
@@ -325,32 +352,9 @@ function providerExecution(settings, name) {
325
352
  return decodeProviderRecipe({ name, ...builtin });
326
353
  throw new TypeError(`unknown provider ${name}`);
327
354
  }
328
- async function admitArchetype(archetype) {
329
- let selected;
330
- try {
331
- selected = await resolveProviderExecution(archetype.provider);
332
- }
333
- catch (error) {
334
- if (error instanceof TypeError)
335
- throw new AkumaArchetypeError(archetype.name, [archetype.path], `uses ${error.message}`);
336
- throw error;
337
- }
338
- const execution = selected.execution;
339
- const adapter = selected.adapter;
340
- const admission = adapter.admitOptions(archetype.options);
341
- if (admission.kind === "refused") {
342
- throw new AkumaArchetypeError(archetype.name, [archetype.path], `is unsupported: ${admission.diagnostic}`);
343
- }
344
- return Object.freeze({
345
- ...archetype,
346
- provider: execution,
347
- adapter,
348
- options: admission.options,
349
- });
350
- }
351
355
  /** Resolve one archetype and decode its provider execution, leaving option admission to the caller. */
352
356
  export async function loadPreparedArchetype(input) {
353
- const name = archetypeName(input.name);
357
+ const name = validateAkumaName(input.name);
354
358
  const definition = await resolveArchetype(name, input, undefined);
355
359
  let provider;
356
360
  try {
@@ -363,6 +367,3 @@ export async function loadPreparedArchetype(input) {
363
367
  }
364
368
  return Object.freeze({ ...definition, provider });
365
369
  }
366
- export async function loadArchetype(input) {
367
- return await admitArchetype(await loadPreparedArchetype(input));
368
- }
@@ -25,6 +25,7 @@ export declare const bodyLaunchSchema: z.ZodObject<{
25
25
  options: import("./provider-recipe.js").ProviderOptions;
26
26
  cwd: string;
27
27
  origin: import("./heart/facts.js").AkumaOrigin;
28
+ caller?: import("./caller.js").AkumaCaller;
28
29
  allowed: import("./allowed.js").AllowedActions;
29
30
  createdAt: string;
30
31
  }>, "createdAt">, unknown>>>;
@@ -55,11 +56,11 @@ export declare const bodyLaunchSchema: z.ZodObject<{
55
56
  }>>;
56
57
  systemPrompt: z.ZodOptional<z.ZodString>;
57
58
  systemPromptMode: z.ZodOptional<z.ZodEnum<{
58
- append: "append";
59
59
  replace: "replace";
60
+ append: "append";
60
61
  }>>;
61
62
  }, z.core.$strip>, z.ZodTransform<Readonly<{
62
- systemPromptMode?: "append" | "replace";
63
+ systemPromptMode?: "replace" | "append";
63
64
  systemPrompt?: string;
64
65
  network?: "disabled" | "enabled";
65
66
  effort?: string;
@@ -69,12 +70,13 @@ export declare const bodyLaunchSchema: z.ZodObject<{
69
70
  effort?: string | undefined;
70
71
  network?: "disabled" | "enabled" | undefined;
71
72
  systemPrompt?: string | undefined;
72
- systemPromptMode?: "append" | "replace" | undefined;
73
+ systemPromptMode?: "replace" | "append" | undefined;
73
74
  }>>;
74
75
  admittedAt: z.ZodString;
75
76
  }, z.core.$strict>>;
76
77
  initialBody: z.ZodOptional<z.ZodString>;
77
78
  initiator: z.ZodOptional<z.ZodString>;
79
+ birthInitiator: z.ZodOptional<z.ZodString>;
78
80
  initialSchemaJson: z.ZodOptional<z.ZodString>;
79
81
  refuseIfHeld: z.ZodOptional<z.ZodBoolean>;
80
82
  completion: z.ZodOptional<z.ZodObject<{