@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,7 @@
1
1
  import { Repo } from "../library/repo.js";
2
2
  import { scopeForRepo } from "../library/repo.js";
3
- import { observeTaskStatusRows } from "../task/operations.js";
3
+ import { observeTaskBoard } from "../task/operations.js";
4
+ import { contractNamespace } from "../settlement/settle.js";
4
5
  import { Akuma } from "../akuma/index.js";
5
6
  import { readAliases } from "../alias/index.js";
6
7
  import { readDispatchesAt } from "../dispatch/index.js";
@@ -10,7 +11,7 @@ import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observ
10
11
  import { readDocuments } from "../protocol/read/documents.js";
11
12
  import { readRegionDeclarations, validateRegionPath } from "../library/region.js";
12
13
  import { contractId } from "../core/facts/types.js";
13
- import { selectRegion } from "./select.js";
14
+ import { selectKanshi, selectRegion } from "./select.js";
14
15
  function diagnostic(error) {
15
16
  let source;
16
17
  if (error instanceof Error)
@@ -85,15 +86,21 @@ function regionSelection(value) {
85
86
  function coordinate(input) {
86
87
  if (typeof input !== "object" || input === null || Array.isArray(input))
87
88
  throw new TypeError("kanshi input must be an object");
88
- for (const key of Object.keys(input))
89
- if (key !== "world" && key !== "repo" && key !== "region")
89
+ for (const key of Object.keys(input)) {
90
+ if (!["world", "repo", "region", "contract"].includes(key)) {
90
91
  throw new TypeError(`kanshi input has unknown field: ${key}`);
92
+ }
93
+ }
91
94
  if (input.world !== null && (typeof input.world !== "string" || input.world.trim().length === 0)) {
92
95
  throw new TypeError("kanshi world must be a WorldRoot or null");
93
96
  }
94
97
  if (input.repo !== undefined && !(input.repo instanceof Repo))
95
98
  throw new TypeError("kanshi repo must be a Repo");
96
- return input.region === undefined ? input : { ...input, region: regionSelection(input.region) };
99
+ return {
100
+ ...input,
101
+ ...(input.region === undefined ? {} : { region: regionSelection(input.region) }),
102
+ ...(input.contract === undefined ? {} : { contract: contractId(input.contract) }),
103
+ };
97
104
  }
98
105
  async function readRegion(observation, selection) {
99
106
  try {
@@ -114,9 +121,9 @@ async function readBranch(repo) {
114
121
  return null;
115
122
  }
116
123
  }
117
- async function readContracts(observation) {
124
+ async function readContracts(observation, selected) {
118
125
  try {
119
- return { kind: "present", value: await readContractBoard(observation) };
126
+ return { kind: "present", value: await readContractBoard(observation, selected) };
120
127
  }
121
128
  catch (error) {
122
129
  return { kind: "failed", failure: { message: diagnostic(error) } };
@@ -151,7 +158,7 @@ function attachFleet(contracts, akuma) {
151
158
  },
152
159
  };
153
160
  }
154
- function decorateContracts(contracts, holders) {
161
+ function decorateContracts(contracts, holders, namespaceTasks) {
155
162
  if (contracts.kind !== "present")
156
163
  return contracts;
157
164
  return {
@@ -159,12 +166,15 @@ function decorateContracts(contracts, holders) {
159
166
  value: {
160
167
  ...contracts.value,
161
168
  rows: contracts.value.rows.map((row) => {
162
- if (holders.kind === "failed")
163
- return { ...row, holder: { kind: "unavailable" }, fleet: [] };
169
+ const selected = namespaceTasks(row.id);
170
+ if (holders.kind === "failed") {
171
+ return { ...row, holder: { kind: "unavailable" }, fleet: [], namespaceTasks: selected };
172
+ }
164
173
  const holder = holders.kind === "present" ? holders.value.get(row.id) : undefined;
165
174
  return {
166
175
  ...row,
167
176
  fleet: [],
177
+ namespaceTasks: selected,
168
178
  holder: holder?.disposition === "held"
169
179
  ? { kind: "held", taskId: holder.taskId }
170
180
  : { kind: "none" },
@@ -192,25 +202,42 @@ function joinTasks(rows, holders, observeContract) {
192
202
  return { ...row, contract: { id: contractId, observed: observeContract(contractId) } };
193
203
  });
194
204
  }
195
- async function readTasks(path, holders, observeContract) {
196
- if (holders.kind === "failed")
197
- return { kind: "failed", failure: holders.failure };
205
+ async function readTaskWorld(path) {
206
+ if (path === null)
207
+ return { kind: "absent" };
198
208
  try {
199
- return {
200
- kind: "present",
201
- value: { root: path, rows: joinTasks(await observeTaskStatusRows(path), holders, observeContract) },
202
- };
209
+ return { kind: "present", observation: await observeTaskBoard(path) };
203
210
  }
204
211
  catch (error) {
205
212
  return { kind: "failed", failure: { message: diagnostic(error) } };
206
213
  }
207
214
  }
208
- async function joinAkuma(path, observeContract, dispatches) {
215
+ function namespaceTaskSection(board, id) {
216
+ if (board.kind === "absent")
217
+ return { kind: "absent" };
218
+ if (board.kind === "failed")
219
+ return { kind: "failed", failure: board.failure };
220
+ return { kind: "present", value: board.observation.selectNamespace(contractNamespace(id)) };
221
+ }
222
+ function readTasks(path, board, holders, observeContract) {
223
+ if (holders.kind === "failed")
224
+ return { kind: "failed", failure: holders.failure };
225
+ if (board.kind === "failed")
226
+ return { kind: "failed", failure: board.failure };
227
+ if (board.kind === "absent")
228
+ return { kind: "absent" };
229
+ return {
230
+ kind: "present",
231
+ value: { root: path, rows: joinTasks(board.observation.statusRows, holders, observeContract) },
232
+ };
233
+ }
234
+ async function joinAkuma(path, observeContract, dispatches, aliases) {
235
+ if (aliases.kind !== "present")
236
+ return aliases;
209
237
  try {
210
238
  const source = await Akuma.of(path).list();
211
- const aliases = await readAliases(path);
212
239
  const aliasById = new Map();
213
- for (const binding of aliases)
240
+ for (const binding of aliases.value)
214
241
  aliasById.set(binding.akuId, [...(aliasById.get(binding.akuId) ?? []), binding]);
215
242
  const dispatchById = new Map(dispatches.map((dispatch) => [dispatch.akuId, dispatch]));
216
243
  return {
@@ -237,6 +264,14 @@ async function joinAkuma(path, observeContract, dispatches) {
237
264
  return { kind: "failed", failure: { message: diagnostic(error) } };
238
265
  }
239
266
  }
267
+ async function readAliasBindings(path) {
268
+ try {
269
+ return { kind: "present", value: await readAliases(path) };
270
+ }
271
+ catch (error) {
272
+ return { kind: "failed", failure: { message: diagnostic(error) } };
273
+ }
274
+ }
240
275
  async function readDispatches(observation) {
241
276
  try {
242
277
  return { kind: "present", value: await readDispatchesAt(observation) };
@@ -245,7 +280,7 @@ async function readDispatches(observation) {
245
280
  return { kind: "failed", failure: { message: diagnostic(error) } };
246
281
  }
247
282
  }
248
- export async function kanshi(input) {
283
+ export async function observeKanshi(input) {
249
284
  const observedAt = new Date().toISOString();
250
285
  const { world, repo } = coordinate(input);
251
286
  const branch = await readBranch(repo);
@@ -253,28 +288,35 @@ export async function kanshi(input) {
253
288
  const contracts = { kind: "absent" };
254
289
  const holders = { kind: "absent" };
255
290
  const observeContract = contractEndpointObserver(contracts);
256
- const tasks = world === null ? { kind: "absent" } : await readTasks(world, holders, observeContract);
257
- const akuma = world === null ? { kind: "absent" } : await joinAkuma(world, observeContract, []);
258
- return { root: world, observedAt, branch, contracts, tasks, akuma, ...(input.region === undefined ? {} : { region: { kind: "absent" } }) };
291
+ const board = await readTaskWorld(world);
292
+ const tasks = world === null ? { kind: "absent" } : readTasks(world, board, holders, observeContract);
293
+ const aliases = world === null ? { kind: "absent" } : await readAliasBindings(world);
294
+ const akuma = world === null ? { kind: "absent" } : await joinAkuma(world, observeContract, [], aliases);
295
+ return {
296
+ report: { root: world, observedAt, branch, contracts, tasks, akuma, ...(input.region === undefined ? {} : { region: { kind: "absent" } }) },
297
+ aliases,
298
+ };
259
299
  }
260
300
  try {
261
301
  const repository = scopeForRepo(repo);
262
302
  return await withGitDecodeChannel(repository, (channel) => withGitReadObservation(repository, channel, async (observation) => {
263
- const [contractSection, holders, dispatches, region] = await Promise.all([
264
- readContracts(observation),
303
+ const [contractSection, holders, dispatches, region, board] = await Promise.all([
304
+ readContracts(observation, input.contract),
265
305
  readHolders(observation),
266
306
  readDispatches(observation),
267
307
  input.region === undefined ? Promise.resolve(undefined) : readRegion(observation, input.region),
308
+ readTaskWorld(world),
268
309
  ]);
269
- const contracts = decorateContracts(contractSection, holders);
310
+ const contracts = decorateContracts(contractSection, holders, (id) => namespaceTaskSection(board, id));
270
311
  const observeContract = contractEndpointObserver(contracts);
271
- const tasks = world === null ? { kind: "absent" } : await readTasks(world, holders, observeContract);
312
+ const aliases = world === null ? { kind: "absent" } : await readAliasBindings(world);
313
+ const tasks = world === null ? { kind: "absent" } : readTasks(world, board, holders, observeContract);
272
314
  const akuma = world === null
273
315
  ? { kind: "absent" }
274
316
  : dispatches.kind === "failed"
275
317
  ? dispatches
276
- : await joinAkuma(world, observeContract, dispatches.value);
277
- return {
318
+ : await joinAkuma(world, observeContract, dispatches.value, aliases);
319
+ const report = {
278
320
  root: world,
279
321
  observedAt,
280
322
  branch,
@@ -283,18 +325,30 @@ export async function kanshi(input) {
283
325
  akuma,
284
326
  ...(region === undefined ? {} : { region }),
285
327
  };
328
+ return {
329
+ report: input.contract === undefined
330
+ ? report
331
+ : selectKanshi({ report, contract: input.contract }),
332
+ aliases,
333
+ };
286
334
  }));
287
335
  }
288
336
  catch (error) {
289
337
  const failure = { kind: "failed", failure: { message: diagnostic(error) } };
290
338
  return {
291
- root: world,
292
- observedAt,
293
- branch,
294
- contracts: failure,
295
- tasks: world === null ? { kind: "absent" } : failure,
296
- akuma: world === null ? { kind: "absent" } : failure,
297
- ...(input.region === undefined ? {} : { region: failure }),
339
+ report: {
340
+ root: world,
341
+ observedAt,
342
+ branch,
343
+ contracts: failure,
344
+ tasks: world === null ? { kind: "absent" } : failure,
345
+ akuma: world === null ? { kind: "absent" } : failure,
346
+ ...(input.region === undefined ? {} : { region: failure }),
347
+ },
348
+ aliases: world === null ? { kind: "absent" } : failure,
298
349
  };
299
350
  }
300
351
  }
352
+ export async function kanshi(input) {
353
+ return (await observeKanshi(input)).report;
354
+ }
@@ -31,6 +31,7 @@ export type ContractFleetAttachment = Readonly<{
31
31
  export type ContractKanshiRow = ContractBoard["rows"][number] & Readonly<{
32
32
  holder: ContractHolderObservation;
33
33
  fleet: readonly ContractFleetAttachment[];
34
+ namespaceTasks: Section<readonly TaskRow[]>;
34
35
  }>;
35
36
  export type ContractKanshiBoard = Omit<ContractBoard, "rows"> & Readonly<{
36
37
  rows: readonly ContractKanshiRow[];
@@ -1,20 +1,19 @@
1
+ import { type AliasBinding } from "../alias/index.js";
1
2
  import { type AkuId } from "../akuma/identity.js";
2
3
  import { type AkumaAlias, type AkumaGlob } from "../identity/selector.js";
3
- import type { Settings } from "../settings.js";
4
4
  import type { WorldRoot } from "../world.js";
5
+ import type { KanshiReport, Section } from "../kanshi/index.js";
5
6
  import { type Repo } from "./repo.js";
6
7
  export type DirectAkumaSelector = AkuId | AkumaAlias;
7
8
  export type SetAkumaSelector = DirectAkumaSelector | AkumaGlob | `kei/${string}`;
8
9
  export type AkumaAddressInput = Readonly<{
9
10
  path: WorldRoot;
10
11
  akuma: string;
11
- settings?: Settings;
12
12
  repo?: Repo;
13
13
  }>;
14
14
  export type AkumaSetAddressInput = Readonly<{
15
15
  path: WorldRoot;
16
16
  akuma: readonly string[];
17
- settings?: Settings;
18
17
  repo?: Repo;
19
18
  }>;
20
19
  export type AkumaWorldScopeRefusal = Readonly<{
@@ -26,12 +25,6 @@ export declare class AkumaWorldScopeError extends TypeError {
26
25
  readonly refusal: AkumaWorldScopeRefusal;
27
26
  constructor(refusal: AkumaWorldScopeRefusal);
28
27
  }
29
- export type NamedAddressContract = Readonly<{
30
- id: string;
31
- disposition: "active" | "terminal";
32
- workspace: "worktree" | "here";
33
- worktreePath: string | null;
34
- }>;
35
28
  export type NamedAddress = Readonly<{
36
29
  kind: "contract";
37
30
  id: string;
@@ -40,18 +33,16 @@ export type NamedAddress = Readonly<{
40
33
  id: AkuId;
41
34
  }>;
42
35
  export type NamedAddressInput = Readonly<{
43
- path: WorldRoot | null;
44
36
  selector: string;
45
- contracts: readonly NamedAddressContract[];
37
+ report: KanshiReport;
38
+ aliases: Section<readonly AliasBinding[]>;
46
39
  }>;
47
- export declare function resolveNamedAddress(input: NamedAddressInput): Promise<NamedAddress>;
40
+ export declare function resolveNamedAddress(input: NamedAddressInput): NamedAddress;
48
41
  export declare function addressAkuma(input: AkumaAddressInput): Promise<Readonly<{
49
42
  path: WorldRoot;
50
43
  id: AkuId;
51
- settings?: Settings;
52
44
  }>>;
53
45
  export declare function addressAkumaSet(input: AkumaSetAddressInput): Promise<Readonly<{
54
46
  path: WorldRoot;
55
47
  ids: readonly AkuId[];
56
- settings?: Settings;
57
48
  }>>;
@@ -20,16 +20,8 @@ function nonblank(value, label) {
20
20
  throw new TypeError(`${label} must be a nonblank string`);
21
21
  return value;
22
22
  }
23
- function settingsOption(value) {
24
- if (value === undefined)
25
- return undefined;
26
- if (typeof value !== "object" || value === null || typeof value.namespace !== "function") {
27
- throw new TypeError("settings must be a Settings");
28
- }
29
- return value;
30
- }
31
- function world(path, settings) {
32
- return Akuma.of(path, settings);
23
+ function world(path) {
24
+ return Akuma.of(path);
33
25
  }
34
26
  async function directId(path, selector) {
35
27
  if (selector.startsWith("@")) {
@@ -41,19 +33,25 @@ async function directId(path, selector) {
41
33
  }
42
34
  return parseAkuId(selector).id;
43
35
  }
44
- export async function resolveNamedAddress(input) {
36
+ export function resolveNamedAddress(input) {
45
37
  const selector = nonblank(input.selector, "selector");
46
38
  if (selector.startsWith("kei/"))
47
39
  return { kind: "contract", id: contractId(selector) };
48
40
  if (selector.startsWith("aku/"))
49
41
  return { kind: "akuma", id: parseAkuId(selector).id };
50
42
  const alias = parseAkumaAlias(selector);
51
- const contractMatches = input.contracts.filter((row) => row.disposition === "active"
43
+ if (input.report.contracts.kind === "failed") {
44
+ throw new TypeError("cannot resolve a named selector while the Contract world is failed");
45
+ }
46
+ if (input.aliases.kind === "failed") {
47
+ throw new TypeError("cannot resolve a named selector while Alias authority is failed");
48
+ }
49
+ const contractMatches = (input.report.contracts.kind === "present" ? input.report.contracts.value.rows : []).filter((row) => row.disposition === "active"
52
50
  && row.workspace === "worktree" && row.worktreePath !== null
53
51
  && `@${row.id.slice("kei/".length)}` === alias);
54
- const aliasId = input.path === null
55
- ? null
56
- : (await readAliases(input.path)).find((binding) => binding.alias === alias)?.akuId ?? null;
52
+ const aliasId = input.aliases.kind === "present"
53
+ ? input.aliases.value.find((binding) => binding.alias === alias)?.akuId ?? null
54
+ : null;
57
55
  if (contractMatches.length > 0 && aliasId !== null)
58
56
  throw new TypeError(`ambiguous selector matches Contract and Akuma: ${selector}`);
59
57
  if (contractMatches.length === 1)
@@ -67,14 +65,14 @@ export async function resolveNamedAddress(input) {
67
65
  export async function addressAkuma(input) {
68
66
  const values = requireInput(input, "Akuma address input");
69
67
  for (const key of Object.keys(values)) {
70
- if (!["path", "akuma", "settings", "repo"].includes(key))
68
+ if (!["path", "akuma", "repo"].includes(key)) {
71
69
  throw new TypeError(`Akuma address input has unknown field: ${key}`);
70
+ }
72
71
  }
73
72
  if (values.repo !== undefined)
74
73
  scopeForRepo(values.repo);
75
74
  const path = nonblank(values.path, "path");
76
- const settings = settingsOption(values.settings);
77
- return { path, id: await directId(path, nonblank(values.akuma, "akuma")), ...(settings === undefined ? {} : { settings }) };
75
+ return { path, id: await directId(path, nonblank(values.akuma, "akuma")) };
78
76
  }
79
77
  function idsFromFleet(fleet) {
80
78
  return fleet.rows.map((row) => row.id);
@@ -132,19 +130,19 @@ async function refuseForeignContractMembers(path, ids, contractMembers) {
132
130
  export async function addressAkumaSet(input) {
133
131
  const values = requireInput(input, "Akuma set address input");
134
132
  for (const key of Object.keys(values)) {
135
- if (!["path", "akuma", "settings", "repo"].includes(key))
133
+ if (!["path", "akuma", "repo"].includes(key)) {
136
134
  throw new TypeError(`Akuma set address input has unknown field: ${key}`);
135
+ }
137
136
  }
138
137
  if (!Array.isArray(values.akuma) || values.akuma.length === 0)
139
138
  throw new TypeError("akuma must be a nonempty selector array");
140
139
  const path = nonblank(values.path, "path");
141
- const settings = settingsOption(values.settings);
142
140
  const selectors = values.akuma.map(parseSetSelector);
143
141
  if (hasSelectorKind(selectors, "contract") && values.repo === undefined) {
144
142
  throw new TypeError("Contract Akuma selector requires repo");
145
143
  }
146
144
  const fleetIds = hasSelectorKind(selectors, "glob")
147
- ? idsFromFleet(await world(path, settings).list())
145
+ ? idsFromFleet(await world(path).list())
148
146
  : [];
149
147
  const aliases = hasSelectorKind(selectors, "alias")
150
148
  ? new Map((await readAliases(path)).map((binding) => [binding.alias, binding.akuId]))
@@ -161,5 +159,5 @@ export async function addressAkumaSet(input) {
161
159
  if (ids.length === 0)
162
160
  throw new TypeError("Akuma selector snapshot is empty");
163
161
  await refuseForeignContractMembers(path, ids, contractMembers);
164
- return { path, ids, ...(settings === undefined ? {} : { settings }) };
162
+ return { path, ids };
165
163
  }
@@ -5,6 +5,7 @@ import { type Dispatch, type DispatchFailure } from "../dispatch/index.js";
5
5
  import { type AkumaAlias } from "../identity/selector.js";
6
6
  import type { Settings } from "../settings.js";
7
7
  import type { WorldRoot } from "../world.js";
8
+ import type { AllowedAction } from "../akuma/allowed.js";
8
9
  import { type Keiyaku } from "./contract.js";
9
10
  import { type Repo } from "./repo.js";
10
11
  export type { AkumaStatus } from "../akuma/akuma.js";
@@ -41,9 +42,11 @@ export type CallInput = Readonly<{
41
42
  cwd?: string;
42
43
  mode?: "wait" | "detach";
43
44
  timeoutMs?: number;
45
+ home?: string;
44
46
  settings?: Settings;
45
47
  contract?: Keiyaku;
46
48
  alias?: AkumaAlias;
49
+ allowed?: readonly AllowedAction[];
47
50
  }>;
48
51
  export type CallObservation = Readonly<{
49
52
  kind: "detached";
@@ -60,7 +63,7 @@ export type CallResult = Readonly<{
60
63
  readonly?: ReadonlyRestraint;
61
64
  execution: Readonly<{
62
65
  cwd: string;
63
- source: "input" | "world" | "contract-worktree";
66
+ source: "input" | "contract-worktree" | "caller" | "process" | "world";
64
67
  }>;
65
68
  dispatch: DispatchStage;
66
69
  alias: AliasStage;
@@ -70,7 +73,6 @@ export type ForkInput = Readonly<{
70
73
  path: WorldRoot;
71
74
  akuma: string;
72
75
  at: string;
73
- settings?: Settings;
74
76
  repo?: Repo;
75
77
  }>;
76
78
  export type ForkResult = Readonly<{
@@ -1,6 +1,6 @@
1
1
  import { realpath, stat } from "node:fs/promises";
2
2
  import { moveAlias } from "../alias/index.js";
3
- import { Akuma, } from "../akuma/akuma.js";
3
+ import { Akuma, akumaCallExecution, callAkumaWithContext, } from "../akuma/akuma.js";
4
4
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
5
5
  import { publishDispatch, readDispatch, } from "../dispatch/index.js";
6
6
  import { parseAkumaAlias } from "../identity/selector.js";
@@ -28,6 +28,17 @@ function settingsOption(value) {
28
28
  }
29
29
  return value;
30
30
  }
31
+ function homeOption(value) {
32
+ if (value === undefined)
33
+ return undefined;
34
+ return nonblank(value, "home");
35
+ }
36
+ function akumaWorld(path, home, settings) {
37
+ return Akuma.of(path, {
38
+ ...(home === undefined ? {} : { home }),
39
+ ...(settings === undefined ? {} : { settings }),
40
+ });
41
+ }
31
42
  function onlyKeys(values, allowed, label) {
32
43
  const accepted = new Set(allowed);
33
44
  for (const key of Object.keys(values)) {
@@ -132,31 +143,33 @@ async function resolveCallExecution(input) {
132
143
  source: "input",
133
144
  };
134
145
  }
135
- if (input.contract === undefined)
136
- return { cwd: input.path, source: "world" };
137
- const seat = seatForKeiyaku(input.contract);
138
- const state = await currentManagedContract(input.contract, seat.id);
139
- const appointment = await readManagedWorktreeAppointment(seat.scope, state.id);
140
- if (appointment.kind === "unappointed") {
141
- throw unavailableWorkspace(state.id, "is unappointed; use reconcile");
142
- }
143
- if (appointment.kind === "failed") {
144
- throw new Error(`${appointment.diagnostic}; use reconcile`);
146
+ if (input.contract !== undefined) {
147
+ const seat = seatForKeiyaku(input.contract);
148
+ const state = await currentManagedContract(input.contract, seat.id);
149
+ const appointment = await readManagedWorktreeAppointment(seat.scope, state.id);
150
+ if (appointment.kind === "unappointed") {
151
+ throw unavailableWorkspace(state.id, "is unappointed; use reconcile");
152
+ }
153
+ if (appointment.kind === "failed") {
154
+ throw new Error(`${appointment.diagnostic}; use reconcile`);
155
+ }
156
+ return {
157
+ cwd: await canonicalizeExistingDirectory(appointment.path, `Contract workspace is unavailable: ${appointment.path}`),
158
+ source: "contract-worktree",
159
+ };
145
160
  }
146
- return {
147
- cwd: await canonicalizeExistingDirectory(appointment.path, `Contract workspace is unavailable: ${appointment.path}`),
148
- source: "contract-worktree",
149
- };
161
+ return undefined;
150
162
  }
151
163
  export async function callKeiyaku(input) {
152
164
  const values = requireInput(input, "Keiyaku.call input");
153
- onlyKeys(values, ["path", "archetype", "body", "cwd", "mode", "timeoutMs", "settings", "contract", "alias"], "Keiyaku.call input");
165
+ onlyKeys(values, ["path", "archetype", "body", "cwd", "mode", "timeoutMs", "home", "settings", "contract", "alias", "allowed"], "Keiyaku.call input");
154
166
  const path = nonblank(values.path, "path");
155
167
  const archetype = nonblank(values.archetype, "archetype");
156
168
  const body = text(values.body, "body");
157
169
  const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
158
170
  const mode = callMode(values.mode);
159
171
  const timeoutMs = callTimeout(values.timeoutMs, mode);
172
+ const home = homeOption(values.home);
160
173
  const settings = settingsOption(values.settings);
161
174
  const alias = values.alias === undefined
162
175
  ? undefined
@@ -167,11 +180,19 @@ export async function callKeiyaku(input) {
167
180
  ...(cwd === undefined ? {} : { cwd }),
168
181
  ...(seat === undefined ? {} : { contract: values.contract }),
169
182
  });
170
- const handle = await Akuma.of(path, settings).call({
183
+ const world = akumaWorld(path, home, settings);
184
+ const call = {
171
185
  archetype,
172
186
  body,
173
- cwd: execution.cwd,
174
- });
187
+ ...(values.allowed === undefined ? {} : { allowed: values.allowed }),
188
+ ...(execution === undefined ? {} : { cwd: execution.cwd }),
189
+ };
190
+ const handle = execution === undefined
191
+ ? await world.call(call)
192
+ : await callAkumaWithContext(world, call, { cwdCanonical: true });
193
+ const completedExecution = execution ?? akumaCallExecution(handle);
194
+ if (completedExecution === undefined)
195
+ throw new Error("Akuma call is missing its birth execution");
175
196
  const readonly = (await handle.status()).readonly;
176
197
  const dispatch = seat === undefined
177
198
  ? { kind: "none" }
@@ -195,7 +216,7 @@ export async function callKeiyaku(input) {
195
216
  kind: "called",
196
217
  akuma: handle.id,
197
218
  ...(readonly === undefined ? {} : { readonly }),
198
- execution,
219
+ execution: completedExecution,
199
220
  dispatch,
200
221
  alias: aliasStage,
201
222
  observation,
@@ -203,19 +224,15 @@ export async function callKeiyaku(input) {
203
224
  }
204
225
  export async function forkKeiyaku(input) {
205
226
  const values = requireInput(input, "Keiyaku.fork input");
206
- onlyKeys(values, ["path", "akuma", "at", "settings", "repo"], "Keiyaku.fork input");
227
+ onlyKeys(values, ["path", "akuma", "at", "repo"], "Keiyaku.fork input");
207
228
  const path = nonblank(values.path, "path");
208
229
  const at = nonblank(values.at, "at");
209
- const settings = settingsOption(values.settings);
210
230
  const akuma = (await addressAkuma({
211
231
  path,
212
232
  akuma: nonblank(values.akuma, "akuma"),
213
- ...(settings === undefined ? {} : { settings }),
214
233
  })).id;
215
234
  const repository = values.repo === undefined ? undefined : scopeForRepo(values.repo);
216
- const receipt = await Akuma.of(path, settings)
217
- .of({ id: akuma })
218
- .fork({ at });
235
+ const receipt = await Akuma.of(path).of({ id: akuma }).fork({ at });
219
236
  if (receipt.kind !== "forked")
220
237
  return { ...receipt, parent: akuma };
221
238
  const dispatch = repository === undefined
@@ -13,7 +13,6 @@ async function attempt(input, id) {
13
13
  return bindOperation({
14
14
  scope: input.scope,
15
15
  channel: input.channel,
16
- title: input.title,
17
16
  terms: input.terms,
18
17
  verification: input.verification,
19
18
  workspace: input.workspace,
@@ -26,6 +25,7 @@ async function attempt(input, id) {
26
25
  });
27
26
  }
28
27
  async function attemptCandidates(input) {
28
+ let result;
29
29
  for (let collision = 0; collision <= 3; collision += 1) {
30
30
  const id = candidateId(input.title, collision);
31
31
  let reserved = false;
@@ -40,7 +40,7 @@ async function attemptCandidates(input) {
40
40
  }
41
41
  reserved = true;
42
42
  }
43
- const result = await attempt(input, id);
43
+ result = await attempt(input, id);
44
44
  if (result.kind === "accepted")
45
45
  return result;
46
46
  if (reserved)
@@ -48,7 +48,7 @@ async function attemptCandidates(input) {
48
48
  if (result.kind !== "refused" || result.refusal.kind !== "contract-exists")
49
49
  return result;
50
50
  }
51
- throw new Error("contract identity collision attempts exhausted");
51
+ return result;
52
52
  }
53
53
  export async function admitBindWithAppointment(input) {
54
54
  const action = () => attemptCandidates(input);
@@ -1,6 +1,5 @@
1
1
  import { type AkumaList } from "../akuma/akuma.js";
2
2
  import { type ArchetypeCatalogRow } from "../akuma/archetype.js";
3
- import type { Settings } from "../settings.js";
4
3
  import type { TaskRow } from "../task/index.js";
5
4
  import { type ContractBoard } from "./contract.js";
6
5
  import { Repo } from "./repo.js";
@@ -29,7 +28,7 @@ export type CatalogInput = Readonly<{
29
28
  query: Extract<CatalogQuery, {
30
29
  kind: "archetypes";
31
30
  }>;
32
- settings: Settings;
31
+ home?: string;
33
32
  }> | Readonly<{
34
33
  query: Extract<CatalogQuery, {
35
34
  kind: "akuma";
@@ -4,10 +4,11 @@ import { observeTaskCatalogRows } from "../task/operations.js";
4
4
  import { listKeiyaku } from "./contract.js";
5
5
  import { requireInput } from "./input.js";
6
6
  import { Repo } from "./repo.js";
7
- function settingsValue(value) {
8
- if (typeof value !== "object" || value === null
9
- || typeof value.namespace !== "function") {
10
- throw new TypeError("settings must be a Settings");
7
+ function optionalHome(value) {
8
+ if (value === undefined)
9
+ return undefined;
10
+ if (typeof value !== "string" || value.trim().length === 0) {
11
+ throw new TypeError("home must be a nonblank string");
11
12
  }
12
13
  return value;
13
14
  }
@@ -40,7 +41,7 @@ export async function listCatalog(input) {
40
41
  const values = requireInput(input, "Keiyaku.ls input");
41
42
  const query = queryValue(values.query);
42
43
  const allowed = query.kind === "contracts" ? ["query", "repo"]
43
- : query.kind === "archetypes" ? ["query", "settings"] : ["query", "path"];
44
+ : query.kind === "archetypes" ? ["query", "home"] : ["query", "path"];
44
45
  for (const key of Object.keys(values)) {
45
46
  if (!allowed.includes(key))
46
47
  throw new TypeError(`Keiyaku.ls input has unknown field: ${key}`);
@@ -52,7 +53,11 @@ export async function listCatalog(input) {
52
53
  return { kind: "contracts", root: board.root, state: board.state, rows: board.rows };
53
54
  }
54
55
  if (query.kind === "archetypes") {
55
- return { kind: "archetypes", rows: await listArchetypeDefinitions({ settings: settingsValue(values.settings) }) };
56
+ const home = optionalHome(values.home);
57
+ return {
58
+ kind: "archetypes",
59
+ rows: await listArchetypeDefinitions(home === undefined ? {} : { home }),
60
+ };
56
61
  }
57
62
  const path = worldRoot(values.path);
58
63
  if (query.kind === "tasks") {