@astrosheep/keiyaku 4.0.2 → 4.0.4

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 (145) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
  9. package/build/src/akuma/akuma.d.ts +34 -11
  10. package/build/src/akuma/akuma.js +121 -61
  11. package/build/src/akuma/allowed.d.ts +7 -0
  12. package/build/src/akuma/allowed.js +40 -0
  13. package/build/src/akuma/archetype.d.ts +7 -4
  14. package/build/src/akuma/archetype.js +15 -16
  15. package/build/src/akuma/body.d.ts +3 -1
  16. package/build/src/akuma/body.js +19 -8
  17. package/build/src/akuma/heart/facts.d.ts +49 -7
  18. package/build/src/akuma/heart/index.d.ts +4 -8
  19. package/build/src/akuma/heart/index.js +76 -15
  20. package/build/src/akuma/heart/rows.d.ts +7 -5
  21. package/build/src/akuma/heart/rows.js +98 -11
  22. package/build/src/akuma/heart/schema.d.ts +1 -1
  23. package/build/src/akuma/heart/schema.js +15 -10
  24. package/build/src/akuma/heart/soul.js +3 -1
  25. package/build/src/akuma/heart/storage.d.ts +2 -0
  26. package/build/src/akuma/heart/storage.js +20 -0
  27. package/build/src/akuma/heart/timeline.d.ts +1 -5
  28. package/build/src/akuma/heart/timeline.js +8 -17
  29. package/build/src/akuma/index.d.ts +4 -1
  30. package/build/src/akuma/index.js +1 -0
  31. package/build/src/akuma/projection.d.ts +17 -3
  32. package/build/src/akuma/projection.js +40 -3
  33. package/build/src/akuma/provider.d.ts +1 -1
  34. package/build/src/akuma/provider.js +2 -1
  35. package/build/src/akuma/providers/acp/core.d.ts +3 -1
  36. package/build/src/akuma/providers/acp/core.js +4 -4
  37. package/build/src/akuma/providers/acp/events.d.ts +11 -3
  38. package/build/src/akuma/providers/acp/events.js +78 -4
  39. package/build/src/akuma/providers/claude/events.js +62 -3
  40. package/build/src/akuma/providers/codex-app-server/events.js +1 -13
  41. package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
  42. package/build/src/akuma/providers/grok-build/index.js +72 -2
  43. package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
  44. package/build/src/akuma/providers/pi/events.js +17 -3
  45. package/build/src/akuma/providers/unified-patch.d.ts +4 -0
  46. package/build/src/akuma/providers/unified-patch.js +14 -0
  47. package/build/src/akuma/publication.d.ts +1 -1
  48. package/build/src/akuma/publication.js +36 -7
  49. package/build/src/akuma/requests.d.ts +70 -4
  50. package/build/src/akuma/requests.js +288 -77
  51. package/build/src/akuma-body.d.ts +1 -0
  52. package/build/src/akuma-body.js +31 -0
  53. package/build/src/cli/accepted.d.ts +4 -1
  54. package/build/src/cli/accepted.js +26 -4
  55. package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
  56. package/build/src/cli/commands/akuma-invoke.js +18 -10
  57. package/build/src/cli/commands/akuma.d.ts +11 -2
  58. package/build/src/cli/commands/akuma.js +67 -28
  59. package/build/src/cli/commands/amend.d.ts +1 -1
  60. package/build/src/cli/commands/amend.js +1 -1
  61. package/build/src/cli/commands/contract.d.ts +3 -3
  62. package/build/src/cli/commands/contract.js +1 -1
  63. package/build/src/cli/commands/install.js +4 -4
  64. package/build/src/cli/commands/task-query.d.ts +1 -1
  65. package/build/src/cli/commands/task-query.js +11 -5
  66. package/build/src/cli/commands/task.js +4 -4
  67. package/build/src/cli/coordinates.d.ts +1 -0
  68. package/build/src/cli/coordinates.js +5 -1
  69. package/build/src/cli/invoke.d.ts +1 -0
  70. package/build/src/cli/invoke.js +90 -73
  71. package/build/src/cli/main.d.ts +2 -0
  72. package/build/src/cli/main.js +29 -4
  73. package/build/src/cli/parse.d.ts +1 -0
  74. package/build/src/cli/parse.js +8 -4
  75. package/build/src/cli/render/akuma.d.ts +2 -2
  76. package/build/src/cli/render/akuma.js +84 -13
  77. package/build/src/cli/render/audit.js +16 -2
  78. package/build/src/cli/render/contract.d.ts +2 -0
  79. package/build/src/cli/render/contract.js +203 -36
  80. package/build/src/cli/render/kanshi.js +64 -29
  81. package/build/src/cli/render/receipt.d.ts +2 -1
  82. package/build/src/cli/render/receipt.js +9 -1
  83. package/build/src/cli/render/text.js +3 -1
  84. package/build/src/cli/result.d.ts +38 -3
  85. package/build/src/cli/selectors.d.ts +1 -0
  86. package/build/src/cli/selectors.js +9 -6
  87. package/build/src/cli/usage.d.ts +2 -0
  88. package/build/src/cli/usage.js +4 -0
  89. package/build/src/contract-worktree.js +3 -0
  90. package/build/src/dispatch/index.js +50 -39
  91. package/build/src/git/hooks.d.ts +0 -4
  92. package/build/src/git/hooks.js +0 -36
  93. package/build/src/git/integration.d.ts +2 -6
  94. package/build/src/git/integration.js +8 -8
  95. package/build/src/git/read-observation.js +1 -0
  96. package/build/src/git/reconcile.d.ts +6 -1
  97. package/build/src/git/reconcile.js +55 -9
  98. package/build/src/git/tender.d.ts +19 -4
  99. package/build/src/git/tender.js +44 -13
  100. package/build/src/git/terminal-seal.d.ts +14 -0
  101. package/build/src/git/terminal-seal.js +15 -2
  102. package/build/src/git/workspace.d.ts +1 -0
  103. package/build/src/git/workspace.js +12 -4
  104. package/build/src/index.d.ts +1 -1
  105. package/build/src/kanshi/index.d.ts +1 -1
  106. package/build/src/kanshi/index.js +1 -1
  107. package/build/src/kanshi/read.d.ts +9 -1
  108. package/build/src/kanshi/read.js +91 -37
  109. package/build/src/kanshi/report.d.ts +1 -0
  110. package/build/src/library/address.d.ts +5 -14
  111. package/build/src/library/address.js +20 -22
  112. package/build/src/library/akuma-creation.d.ts +4 -2
  113. package/build/src/library/akuma-creation.js +43 -26
  114. package/build/src/library/bind.js +3 -3
  115. package/build/src/library/catalog.d.ts +1 -2
  116. package/build/src/library/catalog.js +11 -6
  117. package/build/src/library/configuration.d.ts +4 -0
  118. package/build/src/library/configuration.js +19 -0
  119. package/build/src/library/contract.d.ts +15 -1
  120. package/build/src/library/contract.js +51 -4
  121. package/build/src/library/fleet.d.ts +47 -9
  122. package/build/src/library/fleet.js +184 -44
  123. package/build/src/library/input.js +1 -0
  124. package/build/src/library/keiyaku.d.ts +4 -4
  125. package/build/src/library/keiyaku.js +1 -2
  126. package/build/src/protocol/bind.d.ts +1 -2
  127. package/build/src/protocol/bind.js +2 -16
  128. package/build/src/protocol/operations.d.ts +4 -1
  129. package/build/src/protocol/operations.js +33 -27
  130. package/build/src/protocol/read/status.d.ts +3 -1
  131. package/build/src/protocol/read/status.js +24 -14
  132. package/build/src/runtime/proc/run.d.ts +1 -0
  133. package/build/src/runtime/proc/run.js +9 -2
  134. package/build/src/settlement/settle.d.ts +1 -0
  135. package/build/src/settlement/settle.js +4 -1
  136. package/build/src/task/board.d.ts +18 -3
  137. package/build/src/task/board.js +73 -26
  138. package/build/src/task/index.d.ts +3 -3
  139. package/build/src/task/index.js +7 -5
  140. package/build/src/task/operations.d.ts +2 -19
  141. package/build/src/task/operations.js +29 -7
  142. package/build/src/task/query.d.ts +9 -2
  143. package/build/src/task/query.js +32 -64
  144. package/build/src/verification/execution.js +2 -1
  145. package/package.json +3 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku",
3
- "version": "0.1.0+codex.20260815181541",
3
+ "version": "0.1.0+codex.20260818034655",
4
4
  "description": "Use the Keiyaku contract, task, and Akuma CLI.",
5
5
  "author": { "name": "Keiyaku" },
6
6
  "skills": "./skills/"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku",
3
- "version": "0.1.0+codex.20260815181541",
3
+ "version": "0.1.0+codex.20260818034655",
4
4
  "description": "Use the Keiyaku contract, task, and Akuma CLI.",
5
5
  "author": {
6
6
  "name": "Keiyaku"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku-harness",
3
- "version": "0.1.0+codex.20260815181541",
3
+ "version": "0.1.0+codex.20260818034655",
4
4
  "type": "module",
5
5
  "main": "./opencode.js",
6
6
  "keywords": ["pi-package"],
@@ -31,7 +31,7 @@ keiyaku -C <repo> review [<contract>|@<contract>] --satisfied
31
31
  ```
32
32
 
33
33
  ```bash
34
- keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
34
+ keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
35
35
  keiyaku -C <repo> wait <akuma-selector>... [--any | --all]
36
36
  keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
37
37
  ```
@@ -13,7 +13,7 @@ is accepted; the identity underneath never changes.
13
13
  ## Start One
14
14
 
15
15
  ```bash
16
- keiyaku -C <cwd> call <akuma-name> [--alias @name] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
16
+ keiyaku -C <cwd> call <akuma-name> [--alias @name] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
17
17
  ```
18
18
 
19
19
  Give the worker's initial prompt as one argument (quote it when it contains
@@ -102,6 +102,11 @@ Decidable without consulting you.>
102
102
  KEIYAKU
103
103
  ~~~~
104
104
 
105
+ Use separate fences for checks that need separate timeouts or results. Fences
106
+ run top-to-bottom, and later fences may use earlier outputs. Put setup/build
107
+ before its consumers; use `&&` in one fence only when the consumer must stop
108
+ if setup fails.
109
+
105
110
  Each declaration may set an individual timeout in its fence info string, using
106
111
  an explicit duration unit such as `bash timeout=5m`. Omit it for an unbounded
107
112
  declaration; there is no Verification-wide timeout.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: keiyaku-task
3
- description: "Use when planning work in a Keiyaku v4 repo: add tasks, wire dependencies, inspect readiness, and track lifecycle."
3
+ description: >-
4
+ Use when recording, decomposing, or tracking a complex task, typically
5
+ involving three or more steps.
4
6
  ---
5
7
 
6
8
  # Keiyaku Task
@@ -165,19 +165,20 @@ old Contract onto a different delivery.
165
165
 
166
166
  ## Audit Before Delivery
167
167
 
168
- Audit is how you see a delivery before it exists:
168
+ Audit is an evidence window for a prospective delivery:
169
169
 
170
170
  ```bash
171
171
  keiyaku audit <contract> --diff
172
172
  ```
173
173
 
174
- Audit answers three already-adjudicated questions: candidate, Verification,
175
- and target. It uses the same candidate preparation as deliver, shows the
176
- prospective identity and optional requested diff, and runs declared
177
- Verification against that candidate. A terminal run records ordinary
178
- subject-bound `verified` testimony; it does not record a delivery or request
179
- placement. Read those three answers instead of trusting a worker's completion
180
- report.
174
+ It aggregates facts from the same candidate preparation used by `deliver`:
175
+ the prospective candidate and integration identities, the requested diff, the
176
+ declared Verification commands and their observed results, and the target
177
+ placement observation. A terminal run may record subject-bound `verified`
178
+ testimony for those observed commands; it does not deliver, request placement,
179
+ satisfy a review gate, or decide whether the candidate should land. The
180
+ coordinator judges the returned facts; a worker's completion report is not a
181
+ substitute for them.
181
182
 
182
183
  ## Deliver
183
184
 
@@ -3,6 +3,7 @@ import { type AkuId } from "./identity.js";
3
3
  import { type ActivityHistory, type ActivitySnapshot } from "./projection.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
7
  export type AkumaListRow = Readonly<{
7
8
  id: AkuId;
8
9
  archetype: string;
@@ -36,6 +37,22 @@ export type AkumaList = Readonly<{
36
37
  export type AkumaListInput = Readonly<{
37
38
  archetype?: string;
38
39
  }>;
40
+ export type AkumaCallExecution = Readonly<{
41
+ cwd: string;
42
+ source: "input" | "caller" | "process" | "world";
43
+ }>;
44
+ declare const CALL_EXECUTION: unique symbol;
45
+ declare const CALL_WITH_CONTEXT: unique symbol;
46
+ export type AkumaCallInput = Readonly<{
47
+ archetype: string;
48
+ body: string;
49
+ cwd?: string;
50
+ allowed?: readonly AllowedAction[];
51
+ }>;
52
+ type AkumaCallContext = Readonly<{
53
+ initiatorCwd?: string;
54
+ cwdCanonical?: true;
55
+ }>;
39
56
  export type TellResult = Readonly<{
40
57
  admission: Readonly<{
41
58
  tellId: string;
@@ -76,8 +93,7 @@ export declare class AkumaNotBornError extends Error {
76
93
  readonly kind = "akuma-not-born";
77
94
  constructor(id: AkuId);
78
95
  }
79
- /** Package-internal action observation; it uses the same snapshot selector as status. */
80
- export declare function readActionFeedbackStatus(worldPath: WorldRoot, id: AkuId): Promise<AkumaStatus>;
96
+ export declare function tellAkumaWithId(worldPath: WorldRoot, id: AkuId, body: string, tellId: string, recordedAt?: string): Promise<TellResult>;
81
97
  export type BudgetedStatusObservation = Readonly<{
82
98
  status: AkumaStatus;
83
99
  ordinarySelected: number;
@@ -86,10 +102,13 @@ export type BudgetedStatusObservation = Readonly<{
86
102
  export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, input: Readonly<{
87
103
  ordinaryBudget: number;
88
104
  }>): Promise<BudgetedStatusObservation>;
105
+ /** Package-internal birth projection used by the composed Library call result. */
106
+ export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
89
107
  export declare class AkumaHandle {
90
108
  readonly id: AkuId;
91
109
  private readonly worldPath;
92
- constructor(id: AkuId, worldPath: WorldRoot);
110
+ readonly [CALL_EXECUTION]?: AkumaCallExecution;
111
+ constructor(id: AkuId, worldPath: WorldRoot, execution?: AkumaCallExecution);
93
112
  private get paths();
94
113
  status(): Promise<AkumaStatus>;
95
114
  history(input?: Readonly<{
@@ -108,26 +127,30 @@ export declare class AkumaHandle {
108
127
  kill(): Promise<KillEvidence>;
109
128
  lastAnswer(): Promise<LastAnswer>;
110
129
  }
130
+ /** Package-internal provenance retained only by the handle returned from call. */
131
+ export declare function akumaCallExecution(handle: AkumaHandle): AkumaCallExecution | undefined;
111
132
  export type LastAnswer = Readonly<{
112
133
  kind: "answer";
113
134
  answer: string;
114
135
  }> | Readonly<{
115
136
  kind: "no-answer";
116
137
  }>;
138
+ type AkumaConfiguration = Readonly<{
139
+ home?: string;
140
+ settings?: Settings;
141
+ }>;
117
142
  export declare class Akuma {
118
143
  private readonly path;
119
- private readonly configuredSettings?;
144
+ private readonly configuration;
120
145
  private constructor();
121
- static of(root: WorldRoot, settings?: Settings): Akuma;
122
- private settings;
146
+ static of(root: WorldRoot, input?: AkumaConfiguration): Akuma;
123
147
  of(input: Readonly<{
124
148
  id: string;
125
149
  }>): AkumaHandle;
126
150
  listArchetypes(): Promise<readonly string[]>;
127
- call(input: Readonly<{
128
- archetype: string;
129
- body: string;
130
- cwd?: string;
131
- }>): Promise<AkumaHandle>;
151
+ call(input: AkumaCallInput): Promise<AkumaHandle>;
152
+ [CALL_WITH_CONTEXT](input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
132
153
  list(input?: AkumaListInput): Promise<AkumaList>;
133
154
  }
155
+ /** Package-internal call path for a composition owner that already canonicalized cwd. */
156
+ export declare function callAkumaWithContext(akuma: Akuma, input: AkumaCallInput, context: AkumaCallContext): Promise<AkumaHandle>;
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { readdir } from "node:fs/promises";
2
+ import { readdir, realpath, stat } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { CONTROL_RESPONSE_MS, spawnAkumaBody } from "./body.js";
5
- import { HeldAkumaLeash, activitySlice, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
5
+ import { HeldAkumaLeash, activitySlice, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
6
6
  import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, parseAkuId, pathsForAkuId, archetypeName, } from "./identity.js";
7
7
  import { ordinarySelectedCount, ordinarySnapshotBudget, projectTurns, selectActivitySnapshot, selectHistory, selectSnapshot, } from "./projection.js";
8
8
  import { listArchetypes as readArchetypes, loadArchetype } from "./archetype.js";
@@ -10,7 +10,22 @@ import { publishAkuma } from "./publication.js";
10
10
  import { resolveProviderExecution } from "./providers/index.js";
11
11
  import { injectedBodyRequests, requestBodyCall } from "./requests.js";
12
12
  import { settings as readSettings } from "../settings.js";
13
+ import { decodeAllowedActions, } from "./allowed.js";
13
14
  const POLL_MS = 25;
15
+ async function canonicalBirthCwd(input) {
16
+ const selected = resolve(input);
17
+ try {
18
+ const canonical = await realpath(selected);
19
+ if (!(await stat(canonical)).isDirectory())
20
+ throw new Error("not a directory");
21
+ return canonical;
22
+ }
23
+ catch {
24
+ throw new Error(`cwd is not an existing directory: ${input}`);
25
+ }
26
+ }
27
+ const CALL_EXECUTION = Symbol("akuma-call-execution");
28
+ const CALL_WITH_CONTEXT = Symbol("akuma-call-with-context");
14
29
  export class AkumaNotBornError extends Error {
15
30
  id;
16
31
  kind = "akuma-not-born";
@@ -33,9 +48,8 @@ async function takeLeashUntil(paths, deadline) {
33
48
  await wait(POLL_MS);
34
49
  }
35
50
  }
36
- async function recordTellBody(paths, akuma, body) {
37
- const id = randomUUID();
38
- const admitted = await recordTell(paths, { kind: "tell", id, body, recordedAt: new Date().toISOString() });
51
+ async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt = new Date().toISOString()) {
52
+ const admitted = await recordTell(paths, { kind: "tell", id, body, recordedAt });
39
53
  if (admitted.kind === "not-born")
40
54
  throw new AkumaNotBornError(akuma);
41
55
  return { kind: "recorded", tellId: admitted.tell.id };
@@ -48,10 +62,34 @@ async function wakeTell(paths, tellId) {
48
62
  catch (error) {
49
63
  return {
50
64
  admission: { tellId, fact: "recorded" },
51
- wake: { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error) },
65
+ wake: { kind: "failed", diagnostic: diagnostic(error) },
52
66
  };
53
67
  }
54
68
  }
69
+ export async function tellAkumaWithId(worldPath, id, body, tellId, recordedAt = new Date().toISOString()) {
70
+ const paths = pathsForAkuId(worldPath, id);
71
+ const recorded = await recordTellBody(paths, id, body, tellId, recordedAt);
72
+ return await wakeTell(paths, recorded.tellId);
73
+ }
74
+ function diagnostic(error) {
75
+ return error instanceof Error ? error.message : String(error);
76
+ }
77
+ async function fleetListRow(paths, expected) {
78
+ const snapshot = await readHeart(paths);
79
+ if (snapshot.soul !== null)
80
+ return await bornListRow(paths, expected, snapshot);
81
+ try {
82
+ if (await probeLeash(paths) === "held")
83
+ return { id: expected, life: "unborn" };
84
+ const seal = await readSeal(paths);
85
+ return seal === null ? { id: expected, life: "unborn" } : { id: expected, life: "stillborn", seal };
86
+ }
87
+ catch (error) {
88
+ if (isHeartAbsent(error))
89
+ return { id: expected, life: "unborn" };
90
+ throw error;
91
+ }
92
+ }
55
93
  async function bornListRow(paths, expected, snapshot) {
56
94
  snapshot ??= await readHeart(paths);
57
95
  if (snapshot.soul === null)
@@ -81,7 +119,7 @@ async function bornStatus(paths, expected, budget) {
81
119
  const resumeUnsupported = current.life === "stranded"
82
120
  && snapshot.latestSession?.provider === snapshot.soul.provider.name
83
121
  && resolveProviderExecution(snapshot.soul.provider).adapter.resume === undefined;
84
- const slice = await activitySlice(paths, { limit: Number.MAX_SAFE_INTEGER });
122
+ const slice = await activitySlice(paths);
85
123
  return {
86
124
  id: current.id,
87
125
  life: current.life,
@@ -92,10 +130,6 @@ async function bornStatus(paths, expected, budget) {
92
130
  : selectSnapshot(projectTurns(slice.rows), budget),
93
131
  };
94
132
  }
95
- /** Package-internal action observation; it uses the same snapshot selector as status. */
96
- export async function readActionFeedbackStatus(worldPath, id) {
97
- return await bornStatus(pathsForAkuId(worldPath, id), id);
98
- }
99
133
  /** Package-internal budgeted observation; Fleet allocates, Akuma still selects. */
100
134
  export async function readBudgetedStatus(worldPath, id, input) {
101
135
  if (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0) {
@@ -104,15 +138,22 @@ export async function readBudgetedStatus(worldPath, id, input) {
104
138
  const status = await bornStatus(pathsForAkuId(worldPath, id), id, ordinarySnapshotBudget(input.ordinaryBudget));
105
139
  return { status, ordinarySelected: ordinarySelectedCount(status.timeline) };
106
140
  }
107
- function diagnostic(error) {
108
- return error instanceof Error ? error.message : String(error);
141
+ /** Package-internal birth projection used by the composed Library call result. */
142
+ export async function readAkumaBirthCwd(worldPath, id) {
143
+ const soul = await readSoul(pathsForAkuId(worldPath, id));
144
+ if (soul === null)
145
+ throw new AkumaNotBornError(id);
146
+ return soul.cwd;
109
147
  }
110
148
  export class AkumaHandle {
111
149
  id;
112
150
  worldPath;
113
- constructor(id, worldPath) {
151
+ [CALL_EXECUTION];
152
+ constructor(id, worldPath, execution) {
114
153
  this.id = id;
115
154
  this.worldPath = worldPath;
155
+ if (execution !== undefined)
156
+ this[CALL_EXECUTION] = execution;
116
157
  }
117
158
  get paths() {
118
159
  return pathsForAkuId(this.worldPath, this.id);
@@ -133,11 +174,7 @@ export class AkumaHandle {
133
174
  if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 5_000) {
134
175
  throw new TypeError("Akuma history limit must be a positive safe integer no greater than 5000");
135
176
  }
136
- const slice = await activitySlice(this.paths, {
137
- ...(input.before === undefined ? {} : { before: input.before }),
138
- ...(input.since === undefined ? {} : { since: input.since }),
139
- limit: 5_000,
140
- });
177
+ const slice = await activitySlice(this.paths);
141
178
  return selectHistory(projectTurns(slice.rows, {
142
179
  lowestRetained: slice.lowestRetained,
143
180
  highest: slice.highest,
@@ -254,6 +291,7 @@ export class AkumaHandle {
254
291
  provider: source.provider,
255
292
  options: source.options,
256
293
  ...(source.readonly === undefined ? {} : { readonly: source.readonly }),
294
+ allowed: source.allowed,
257
295
  cwd: source.cwd,
258
296
  origin: { kind: "fork", parent: this.id, at: input.at },
259
297
  confinement: source.confinement,
@@ -264,7 +302,11 @@ export class AkumaHandle {
264
302
  return { kind: "forked", child: child.id };
265
303
  }
266
304
  catch (error) {
267
- return { kind: "upstream-forked", childSession, diagnostic: diagnostic(error) };
305
+ return {
306
+ kind: "upstream-forked",
307
+ childSession,
308
+ diagnostic: diagnostic(error),
309
+ };
268
310
  }
269
311
  }
270
312
  async kill() {
@@ -309,52 +351,75 @@ export class AkumaHandle {
309
351
  : { kind: "no-answer" };
310
352
  }
311
353
  }
354
+ /** Package-internal provenance retained only by the handle returned from call. */
355
+ export function akumaCallExecution(handle) {
356
+ return handle[CALL_EXECUTION];
357
+ }
312
358
  export class Akuma {
313
359
  path;
314
- configuredSettings;
315
- constructor(path, configuredSettings) {
360
+ configuration;
361
+ constructor(path, configuration) {
316
362
  this.path = path;
317
- this.configuredSettings = configuredSettings;
363
+ this.configuration = configuration;
318
364
  }
319
- static of(root, settings) {
365
+ static of(root, input = {}) {
320
366
  if (typeof root !== "string")
321
367
  throw new TypeError("Akuma.of root must be a WorldRoot");
322
- return new Akuma(root, settings);
323
- }
324
- async settings() {
325
- return this.configuredSettings ?? await readSettings({ root: this.path });
368
+ return new Akuma(root, input);
326
369
  }
327
370
  of(input) {
328
371
  return new AkumaHandle(parseAkuId(input.id).id, this.path);
329
372
  }
330
373
  async listArchetypes() {
331
- return readArchetypes({ settings: await this.settings() });
374
+ return readArchetypes(this.configuration.home === undefined ? {} : { home: this.configuration.home });
332
375
  }
333
376
  async call(input) {
377
+ return await this[CALL_WITH_CONTEXT](input, { initiatorCwd: process.cwd() });
378
+ }
379
+ async [CALL_WITH_CONTEXT](input, context) {
334
380
  const name = archetypeName(input.archetype);
335
- const archetype = await loadArchetype({ name, settings: await this.settings() });
336
- const provider = archetype.adapter;
337
- const cwd = resolve(input.cwd ?? this.path);
338
- const recipe = Object.freeze({
381
+ const home = this.configuration.home === undefined ? {} : { home: this.configuration.home };
382
+ const settings = this.configuration.settings ?? await readSettings({ root: this.path, ...home });
383
+ const archetype = await loadArchetype({ name, ...home, settings });
384
+ const allowed = input.allowed === undefined
385
+ ? archetype.allowed
386
+ : decodeAllowedActions(input.allowed, "Akuma call allowed");
387
+ const requests = injectedBodyRequests();
388
+ const requestRecipe = Object.freeze({
339
389
  ...(archetype.description === undefined ? {} : { description: archetype.description }),
340
390
  provider: archetype.provider,
341
391
  options: archetype.options,
342
392
  ...(archetype.readonly === undefined ? {} : { readonly: archetype.readonly }),
343
- confinement: provider.confinement({ cwd, options: archetype.options }),
393
+ allowed,
344
394
  });
345
- const requests = injectedBodyRequests();
346
395
  if (requests !== null) {
396
+ const cwd = input.cwd === undefined
397
+ ? undefined
398
+ : context?.cwdCanonical === true ? input.cwd : await canonicalBirthCwd(input.cwd);
347
399
  const child = await requestBodyCall({
348
400
  directory: requests,
349
401
  id: randomUUID(),
350
402
  world: this.path,
351
403
  archetype: name,
352
404
  body: input.body,
353
- cwd,
354
- recipe,
405
+ ...(cwd === undefined ? {} : { cwd }),
406
+ recipe: requestRecipe,
407
+ });
408
+ const bornCwd = await readAkumaBirthCwd(this.path, child);
409
+ return new AkumaHandle(child, this.path, {
410
+ cwd: bornCwd,
411
+ source: cwd === undefined ? "caller" : "input",
355
412
  });
356
- return new AkumaHandle(child, this.path);
357
413
  }
414
+ const initiatorCwd = context.initiatorCwd;
415
+ const selectedCwd = input.cwd ?? initiatorCwd ?? this.path;
416
+ const cwd = input.cwd !== undefined && context?.cwdCanonical === true
417
+ ? input.cwd
418
+ : await canonicalBirthCwd(selectedCwd);
419
+ const recipe = Object.freeze({
420
+ ...requestRecipe,
421
+ confinement: archetype.adapter.confinement({ cwd, options: archetype.options }),
422
+ });
358
423
  const published = await publishAkuma({
359
424
  worldPath: this.path,
360
425
  archetype: archetype.name,
@@ -363,18 +428,17 @@ export class Akuma {
363
428
  seed: {
364
429
  id: allocated.id,
365
430
  archetype: allocated.archetype,
366
- ...(archetype.description === undefined ? {} : { description: archetype.description }),
367
- provider: archetype.provider,
368
- options: archetype.options,
369
- ...(archetype.readonly === undefined ? {} : { readonly: archetype.readonly }),
431
+ ...recipe,
370
432
  cwd,
371
433
  origin: { kind: "direct" },
372
- confinement: recipe.confinement,
373
434
  },
374
435
  initialBody: input.body,
375
436
  }),
376
437
  });
377
- return new AkumaHandle(published.id, this.path);
438
+ return new AkumaHandle(published.id, this.path, {
439
+ cwd,
440
+ source: input.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
441
+ });
378
442
  }
379
443
  async list(input = {}) {
380
444
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
@@ -399,28 +463,20 @@ export class Akuma {
399
463
  }
400
464
  const rows = [];
401
465
  for (const name of names) {
466
+ let physical;
402
467
  try {
403
- const physical = akuIdFromDirectoryName(name);
404
- if (selected !== undefined && physical.archetype !== selected)
405
- continue;
406
- const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
407
- const snapshot = await readHeart(paths);
408
- if (snapshot.soul !== null) {
409
- rows.push(await bornListRow(paths, physical.id, snapshot));
410
- continue;
411
- }
412
- if (await probeLeash(paths) === "held") {
413
- rows.push({ id: physical.id, life: "unborn" });
414
- continue;
415
- }
416
- const seal = await readSeal(paths);
417
- rows.push(seal === null
418
- ? { id: physical.id, life: "unborn" }
419
- : { id: physical.id, life: "stillborn", seal });
468
+ physical = akuIdFromDirectoryName(name);
420
469
  }
421
470
  catch {
422
471
  continue;
423
472
  }
473
+ if (selected !== undefined && physical.archetype !== selected)
474
+ continue;
475
+ const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
476
+ try {
477
+ rows.push(await fleetListRow(paths, physical.id));
478
+ }
479
+ catch { }
424
480
  }
425
481
  return {
426
482
  rows,
@@ -428,3 +484,7 @@ export class Akuma {
428
484
  };
429
485
  }
430
486
  }
487
+ /** Package-internal call path for a composition owner that already canonicalized cwd. */
488
+ export async function callAkumaWithContext(akuma, input, context) {
489
+ return await akuma[CALL_WITH_CONTEXT](input, context);
490
+ }
@@ -0,0 +1,7 @@
1
+ export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.deliver", "task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
2
+ export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
3
+ export type AllowedActions = readonly AllowedAction[];
4
+ export declare function isAllowedAction(value: unknown): value is AllowedAction;
5
+ export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
6
+ export declare function effectiveAllowedActions(value: unknown): AllowedActions;
7
+ export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
@@ -0,0 +1,40 @@
1
+ export const ALLOWED_ACTIONS = Object.freeze([
2
+ "akuma.call",
3
+ "akuma.kill",
4
+ "akuma.tell",
5
+ "contract.deliver",
6
+ "task.add",
7
+ "task.addDocument",
8
+ "task.compose",
9
+ "task.done",
10
+ "task.drop",
11
+ "task.hold",
12
+ "task.resume",
13
+ "task.start",
14
+ "task.stop",
15
+ "task.update",
16
+ ]);
17
+ const ALLOWED_ACTION_SET = new Set(ALLOWED_ACTIONS);
18
+ export function isAllowedAction(value) {
19
+ return typeof value === "string" && ALLOWED_ACTION_SET.has(value);
20
+ }
21
+ export function decodeAllowedActions(value, label = "allowed") {
22
+ if (!Array.isArray(value))
23
+ throw new TypeError(`${label} must be an array`);
24
+ const seen = new Set();
25
+ for (const action of value) {
26
+ if (!isAllowedAction(action))
27
+ throw new TypeError(`${label} contains an unknown action: ${String(action)}`);
28
+ if (seen.has(action))
29
+ throw new TypeError(`${label} contains a duplicate action: ${action}`);
30
+ seen.add(action);
31
+ }
32
+ return Object.freeze([...seen].sort());
33
+ }
34
+ export function effectiveAllowedActions(value) {
35
+ return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
36
+ }
37
+ export function clipAllowedActions(requested, parent) {
38
+ const ceiling = new Set(parent);
39
+ return Object.freeze(requested.filter((action) => ceiling.has(action)));
40
+ }
@@ -1,12 +1,14 @@
1
1
  import type { Settings } from "../settings.js";
2
2
  import type { ProviderAdapter } from "./provider.js";
3
3
  import { type ProviderExecution, type ProviderOptions, type ReadonlyRestraint } from "./provider-recipe.js";
4
+ import { type AllowedActions } from "./allowed.js";
4
5
  type DecodedArchetype = Readonly<{
5
6
  name: string;
6
7
  path: string;
7
8
  provider: string;
8
9
  description?: string;
9
10
  options: ProviderOptions;
11
+ allowed: AllowedActions;
10
12
  }>;
11
13
  export type ArchetypeCatalogRow = Readonly<{
12
14
  name: string;
@@ -27,14 +29,15 @@ export declare class AkumaArchetypeError extends Error {
27
29
  readonly kind = "akuma-archetype";
28
30
  constructor(archetype: string, searched: readonly string[], reason: string, guidance?: string);
29
31
  }
30
- export declare function listArchetypes(input: Readonly<{
31
- settings: Settings;
32
+ export declare function listArchetypes(input?: Readonly<{
33
+ home?: string;
32
34
  }>): Promise<readonly string[]>;
33
- export declare function listArchetypeDefinitions(input: Readonly<{
34
- settings: Settings;
35
+ export declare function listArchetypeDefinitions(input?: Readonly<{
36
+ home?: string;
35
37
  }>): Promise<readonly ArchetypeCatalogRow[]>;
36
38
  export declare function loadArchetype(input: Readonly<{
37
39
  name: string;
40
+ home?: string;
38
41
  settings: Settings;
39
42
  }>): Promise<AdmittedArchetype>;
40
43
  export {};