@astrosheep/keiyaku 4.5.19 → 4.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +23 -0
  14. package/build/src/akuma/akuma-product.d.ts +2 -0
  15. package/build/src/akuma/akuma-product.js +6 -0
  16. package/build/src/akuma/akuma.d.ts +20 -1
  17. package/build/src/akuma/akuma.js +2 -0
  18. package/build/src/akuma/allowed.d.ts +20 -0
  19. package/build/src/akuma/allowed.js +11 -0
  20. package/build/src/akuma/archetype.js +3 -0
  21. package/build/src/akuma/body.d.ts +25 -5
  22. package/build/src/akuma/body.js +70 -31
  23. package/build/src/akuma/call-request.d.ts +5 -0
  24. package/build/src/akuma/call-request.js +14 -4
  25. package/build/src/akuma/fleet-execution.d.ts +28 -0
  26. package/build/src/akuma/fleet-execution.js +36 -4
  27. package/build/src/akuma/fleet-observation.d.ts +54 -0
  28. package/build/src/akuma/fleet-request.d.ts +10 -0
  29. package/build/src/akuma/fleet-request.js +13 -3
  30. package/build/src/akuma/heart/facts.d.ts +2 -0
  31. package/build/src/akuma/heart/index.d.ts +1 -0
  32. package/build/src/akuma/heart/index.js +4 -1
  33. package/build/src/akuma/heart/rows.d.ts +2 -0
  34. package/build/src/akuma/heart/rows.js +7 -5
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +3 -1
  37. package/build/src/akuma/heart/tells.js +6 -5
  38. package/build/src/akuma/heart/timeline.js +1 -1
  39. package/build/src/akuma/provider-recipe.d.ts +1 -0
  40. package/build/src/akuma/provider-recipe.js +5 -0
  41. package/build/src/akuma/provider.d.ts +1 -1
  42. package/build/src/akuma/provider.js +1 -1
  43. package/build/src/akuma/providers/acp/core.js +3 -6
  44. package/build/src/akuma/providers/acp/index.js +3 -0
  45. package/build/src/akuma/providers/claude/index.js +10 -10
  46. package/build/src/akuma/providers/codex-app-server/index.js +12 -11
  47. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  48. package/build/src/akuma/providers/execution-environment.js +24 -0
  49. package/build/src/akuma/providers/grok-build/index.js +3 -0
  50. package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
  51. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  52. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  53. package/build/src/akuma/providers/pi/events.js +7 -2
  54. package/build/src/akuma/providers/pi/index.js +5 -2
  55. package/build/src/akuma/request-observation.d.ts +45 -0
  56. package/build/src/akuma/request-observation.js +162 -0
  57. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  58. package/build/src/akuma/request-rendezvous.js +80 -14
  59. package/build/src/akuma/request-serve.js +27 -4
  60. package/build/src/akuma/request-wire.d.ts +5 -0
  61. package/build/src/akuma/request-wire.js +2 -0
  62. package/build/src/akuma/turn-drive.d.ts +1 -0
  63. package/build/src/akuma/turn-drive.js +2 -0
  64. package/build/src/akuma-body.js +5 -0
  65. package/build/src/body/amend.js +6 -4
  66. package/build/src/body/decode.d.ts +3 -1
  67. package/build/src/body/decode.js +53 -17
  68. package/build/src/body/region.d.ts +9 -1
  69. package/build/src/body/region.js +87 -12
  70. package/build/src/body/render.js +1 -1
  71. package/build/src/body/verification.d.ts +3 -1
  72. package/build/src/body/verification.js +4 -1
  73. package/build/src/cli/accepted.js +19 -2
  74. package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
  75. package/build/src/cli/commands/akuma-invoke.js +134 -14
  76. package/build/src/cli/commands/akuma.js +24 -12
  77. package/build/src/cli/commands/contract-help.d.ts +15 -15
  78. package/build/src/cli/commands/contract-help.js +45 -19
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  80. package/build/src/cli/commands/contract-invoke.js +25 -11
  81. package/build/src/cli/commands/contract.js +6 -4
  82. package/build/src/cli/commands/install.d.ts +1 -1
  83. package/build/src/cli/commands/install.js +1 -1
  84. package/build/src/cli/commands/status-set.js +15 -24
  85. package/build/src/cli/commands/task.js +18 -18
  86. package/build/src/cli/invoke.d.ts +3 -0
  87. package/build/src/cli/invoke.js +58 -23
  88. package/build/src/cli/main.js +5 -0
  89. package/build/src/cli/parse.d.ts +3 -0
  90. package/build/src/cli/parse.js +26 -4
  91. package/build/src/cli/render/akuma-activity.d.ts +100 -2
  92. package/build/src/cli/render/akuma-activity.js +485 -40
  93. package/build/src/cli/render/akuma.d.ts +10 -0
  94. package/build/src/cli/render/akuma.js +31 -21
  95. package/build/src/cli/render/audit.js +1 -1
  96. package/build/src/cli/render/board.js +1 -1
  97. package/build/src/cli/render/catalog.js +16 -13
  98. package/build/src/cli/render/contract-history.js +39 -10
  99. package/build/src/cli/render/contract-observation.js +10 -6
  100. package/build/src/cli/render/contract.js +109 -106
  101. package/build/src/cli/render/execution-progress.d.ts +26 -0
  102. package/build/src/cli/render/execution-progress.js +178 -0
  103. package/build/src/cli/render/kanshi-akuma.js +27 -39
  104. package/build/src/cli/render/kanshi.js +33 -26
  105. package/build/src/cli/render/nuke.js +1 -1
  106. package/build/src/cli/render/receipt.d.ts +1 -1
  107. package/build/src/cli/render/receipt.js +6 -6
  108. package/build/src/cli/render/refusal.js +10 -7
  109. package/build/src/cli/render/region.js +1 -1
  110. package/build/src/cli/render/settings.js +12 -2
  111. package/build/src/cli/render/status-line.d.ts +30 -0
  112. package/build/src/cli/render/status-line.js +58 -0
  113. package/build/src/cli/render/status-set.js +1 -1
  114. package/build/src/cli/render/task.d.ts +2 -0
  115. package/build/src/cli/render/task.js +11 -7
  116. package/build/src/cli/render/terminal.d.ts +0 -1
  117. package/build/src/cli/render/terminal.js +47 -15
  118. package/build/src/cli/result.d.ts +1 -0
  119. package/build/src/cli/runtime.d.ts +2 -0
  120. package/build/src/cli/runtime.js +36 -4
  121. package/build/src/cli/usage.js +2 -2
  122. package/build/src/cli/version.d.ts +1 -0
  123. package/build/src/cli/version.js +25 -0
  124. package/build/src/git/hooks.d.ts +13 -1
  125. package/build/src/git/hooks.js +2 -1
  126. package/build/src/git/workspace.d.ts +1 -1
  127. package/build/src/git/workspace.js +1 -1
  128. package/build/src/identity/mint.d.ts +13 -0
  129. package/build/src/identity/mint.js +26 -0
  130. package/build/src/identity/normalize.d.ts +4 -0
  131. package/build/src/identity/normalize.js +30 -0
  132. package/build/src/index.d.ts +2 -0
  133. package/build/src/index.js +1 -0
  134. package/build/src/kanshi/read.js +17 -4
  135. package/build/src/kanshi/report.d.ts +1 -0
  136. package/build/src/library/akuma-creation.d.ts +2 -0
  137. package/build/src/library/akuma-creation.js +34 -18
  138. package/build/src/library/audit.d.ts +2 -0
  139. package/build/src/library/audit.js +1 -0
  140. package/build/src/library/bind.js +18 -16
  141. package/build/src/library/composition.d.ts +1 -0
  142. package/build/src/library/contract-bind.d.ts +3 -1
  143. package/build/src/library/contract-bind.js +5 -3
  144. package/build/src/library/contract-execution.d.ts +2 -0
  145. package/build/src/library/contract-execution.js +15 -1
  146. package/build/src/library/contract-forwarding.d.ts +4 -0
  147. package/build/src/library/contract-forwarding.js +3 -0
  148. package/build/src/library/contract-handle.d.ts +7 -0
  149. package/build/src/library/contract-handle.js +25 -0
  150. package/build/src/library/contract-operations.d.ts +7 -2
  151. package/build/src/library/contract-operations.js +21 -1
  152. package/build/src/library/contract-types.d.ts +1 -0
  153. package/build/src/library/execution-result.d.ts +2 -2
  154. package/build/src/library/execution.d.ts +8 -0
  155. package/build/src/library/execution.js +102 -0
  156. package/build/src/library/fleet.d.ts +4 -1
  157. package/build/src/library/fleet.js +32 -8
  158. package/build/src/library/keiyaku.d.ts +2 -0
  159. package/build/src/library/mutation.js +15 -7
  160. package/build/src/plugin/akuma-signals.d.ts +6 -0
  161. package/build/src/plugin/akuma-signals.js +9 -1
  162. package/build/src/plugin/public.d.ts +6 -2
  163. package/build/src/plugin/runtime.d.ts +1 -3
  164. package/build/src/plugin/runtime.js +95 -103
  165. package/build/src/protocol/amend.js +4 -3
  166. package/build/src/protocol/attempt.d.ts +2 -0
  167. package/build/src/protocol/bind.js +41 -14
  168. package/build/src/protocol/completion.d.ts +3 -0
  169. package/build/src/protocol/completion.js +35 -11
  170. package/build/src/protocol/deliver.js +165 -92
  171. package/build/src/protocol/execution-observation.d.ts +181 -0
  172. package/build/src/protocol/execution-observation.js +45 -0
  173. package/build/src/protocol/intent.d.ts +18 -22
  174. package/build/src/protocol/intent.js +63 -41
  175. package/build/src/protocol/operations.d.ts +2 -0
  176. package/build/src/protocol/placement.js +5 -1
  177. package/build/src/protocol/progress.d.ts +5 -1
  178. package/build/src/protocol/progress.js +10 -1
  179. package/build/src/protocol/read/status.d.ts +1 -0
  180. package/build/src/protocol/read/status.js +10 -2
  181. package/build/src/protocol/reintegrate.d.ts +1 -1
  182. package/build/src/protocol/reintegrate.js +70 -43
  183. package/build/src/protocol/result-codec.js +27 -11
  184. package/build/src/protocol/review.js +53 -30
  185. package/build/src/protocol/run.d.ts +83 -36
  186. package/build/src/protocol/run.js +90 -75
  187. package/build/src/runtime/proc/run.d.ts +16 -4
  188. package/build/src/runtime/proc/run.js +64 -22
  189. package/build/src/runtime/proc/windows-launch.exe +0 -0
  190. package/build/src/settings.js +5 -14
  191. package/build/src/task/document.d.ts +5 -1
  192. package/build/src/task/document.js +6 -1
  193. package/build/src/verification/execution.d.ts +10 -2
  194. package/build/src/verification/execution.js +143 -42
  195. package/build/src/verification/observation.d.ts +41 -0
  196. package/build/src/verification/observation.js +29 -0
  197. package/package.json +3 -1
@@ -5,6 +5,30 @@ import { safeText } from "./render/terminal.js";
5
5
  function writeCliStream(stream, body) {
6
6
  stream.write(body.endsWith("\n") ? body : `${body}\n`);
7
7
  }
8
+ export async function writeExecutionProgress(events, stream = process.stderr) {
9
+ const { renderExecutionProgress } = await import("./render/execution-progress.js");
10
+ const terminal = stream;
11
+ await renderExecutionProgress(events, {
12
+ stream: terminal,
13
+ context: {
14
+ columns: terminal.isTTY === true && Number.isInteger(terminal.columns) ? (terminal.columns ?? 80) : 80,
15
+ color: false,
16
+ },
17
+ });
18
+ }
19
+ function cliCancellation() {
20
+ const controller = new AbortController();
21
+ const cancel = () => controller.abort(new Error("CLI cancellation requested"));
22
+ process.once("SIGINT", cancel);
23
+ process.once("SIGTERM", cancel);
24
+ return {
25
+ signal: controller.signal,
26
+ close() {
27
+ process.removeListener("SIGINT", cancel);
28
+ process.removeListener("SIGTERM", cancel);
29
+ },
30
+ };
31
+ }
8
32
  export async function writeTask(command, result) {
9
33
  const { renderTaskIncompleteDiagnostic, renderTaskText, taskExitCode } = await import("./render/task.js");
10
34
  const context = {
@@ -114,7 +138,7 @@ function writeWorldScopeRefusal(error, command) {
114
138
  const body = command.output === "json"
115
139
  ? JSON.stringify(error.refusal)
116
140
  : [
117
- `✕ selector ${error.refusal.kind}`,
141
+ selector ${error.refusal.kind}`,
118
142
  ` world ${safeText(error.refusal.world)}`,
119
143
  ...error.refusal.ids.map((id) => ` given ${safeText(id)}`),
120
144
  ` accepts ${guide.accepts}`,
@@ -136,15 +160,20 @@ async function commandFailureText(error, command) {
136
160
  }, "selector");
137
161
  }
138
162
  const { renderRefusalFacts } = await import("./render/refusal.js");
139
- return [`✕ ${command.command} refused`, ...renderRefusalFacts(error.refusal, " ", displayContext().columns)].join("\n");
163
+ return [ ${command.command} refused`, ...renderRefusalFacts(error.refusal, " ", displayContext().columns)].join("\n");
140
164
  }
141
- return `✕ ${command.command} failed\n diagnostic ${safeText(diagnostic)}`;
165
+ return ${command.command} failed\n diagnostic ${safeText(diagnostic)}`;
142
166
  }
143
167
  export async function runCliCommand(invocation) {
144
168
  const command = invocation.command;
169
+ const cancellation = cliCancellation();
145
170
  try {
146
171
  const { invoke } = await import("./invoke.js");
147
- const result = await invoke(invocation, { cwd: process.cwd() });
172
+ const result = await invoke(invocation, {
173
+ cwd: process.cwd(),
174
+ signal: cancellation.signal,
175
+ progress: writeExecutionProgress,
176
+ });
148
177
  return await writeResult(command, result);
149
178
  }
150
179
  catch (error) {
@@ -177,4 +206,7 @@ export async function runCliCommand(invocation) {
177
206
  writeCliStream(process.stderr, await commandFailureText(error, command));
178
207
  return error instanceof CliUsageError ? 1 : 3;
179
208
  }
209
+ finally {
210
+ cancellation.close();
211
+ }
180
212
  }
@@ -34,9 +34,9 @@ export function unknownCommandGuide(guide, given) {
34
34
  }
35
35
  export function renderUsageMessage(diagnostic, guide, kind = "usage") {
36
36
  if (guide === undefined) {
37
- return `✕ ${kind}\n diagnostic ${diagnostic}`;
37
+ return ${kind}\n diagnostic ${diagnostic}`;
38
38
  }
39
- const lines = [`✕ ${kind} ${guide.scope}`];
39
+ const lines = [ ${kind} ${guide.scope}`];
40
40
  if (!guide.hideDiagnostic && diagnostic.length > 0) {
41
41
  lines.push(` diagnostic ${diagnostic}`);
42
42
  }
@@ -0,0 +1 @@
1
+ export declare function installedPackageVersion(): Promise<string>;
@@ -0,0 +1,25 @@
1
+ import { readFile } from "node:fs/promises";
2
+ const PACKAGE_NAME = "@astrosheep/keiyaku";
3
+ export async function installedPackageVersion() {
4
+ const manifest = JSON.parse(await readFile(owningManifestUrl(), "utf8"));
5
+ if (!isOwningManifest(manifest)) {
6
+ throw new Error(`Keiyaku package manifest must name ${PACKAGE_NAME} and contain a nonempty version`);
7
+ }
8
+ return manifest.version;
9
+ }
10
+ function owningManifestUrl() {
11
+ const relativePath = import.meta.url.endsWith("/src/cli/version.ts")
12
+ ? "../../package.json"
13
+ : import.meta.url.endsWith("/build/src/cli/version.js")
14
+ ? "../../../package.json"
15
+ : undefined;
16
+ if (relativePath === undefined)
17
+ throw new Error("Keiyaku package metadata has an unsupported module layout");
18
+ return new URL(relativePath, import.meta.url);
19
+ }
20
+ function isOwningManifest(value) {
21
+ if (typeof value !== "object" || value === null)
22
+ return false;
23
+ const manifest = value;
24
+ return manifest.name === PACKAGE_NAME && typeof manifest.version === "string" && manifest.version.trim().length > 0;
25
+ }
@@ -16,11 +16,17 @@ export type HookFailure = Readonly<{
16
16
  truncated: boolean;
17
17
  }> | Readonly<{
18
18
  kind: "timeout";
19
+ stdout?: string;
20
+ stderr?: string;
21
+ truncated?: boolean;
19
22
  }> | Readonly<{
20
23
  kind: "spawn-error";
21
24
  diagnostic: string;
22
25
  }> | Readonly<{
23
26
  kind: "unknown-exit";
27
+ stdout?: string;
28
+ stderr?: string;
29
+ truncated?: boolean;
24
30
  }>;
25
31
  export type WorktreeHookLag = Readonly<{
26
32
  kind: "worktree-hook-failed";
@@ -42,13 +48,19 @@ export type HookCommandRun = Readonly<{
42
48
  kind: "ok";
43
49
  }> | Readonly<{
44
50
  kind: "cancelled";
51
+ stdout?: string;
52
+ stderr?: string;
53
+ truncated?: boolean;
45
54
  }> | Readonly<{
46
55
  kind: "failed";
47
56
  name: string;
48
57
  failure: HookFailure;
49
58
  }>;
50
59
  /** Execute an ordered command list without lifecycle state. Scratch owns this policy. */
51
- export declare function runHookCommands(worktree: string, commands: readonly HookCommand[], signal?: AbortSignal, environment?: NodeJS.ProcessEnv): Promise<HookCommandRun>;
60
+ export declare function runHookCommands(worktree: string, commands: readonly HookCommand[], signal?: AbortSignal, environment?: NodeJS.ProcessEnv, onOutput?: (output: Readonly<{
61
+ stream: "stdout" | "stderr";
62
+ text: string;
63
+ }>) => void): Promise<HookCommandRun>;
52
64
  export type HookPhaseRun = Readonly<{
53
65
  lag: WorktreeHookLag | null;
54
66
  runs: readonly string[];
@@ -98,7 +98,7 @@ function failedOutcome(outcome) {
98
98
  return outcome;
99
99
  }
100
100
  /** Execute an ordered command list without lifecycle state. Scratch owns this policy. */
101
- export async function runHookCommands(worktree, commands, signal, environment) {
101
+ export async function runHookCommands(worktree, commands, signal, environment, onOutput) {
102
102
  for (const value of commands) {
103
103
  const outcome = await runProcess({
104
104
  argv: value.argv,
@@ -106,6 +106,7 @@ export async function runHookCommands(worktree, commands, signal, environment) {
106
106
  cwd: worktree,
107
107
  ...(signal === undefined ? {} : { signal }),
108
108
  ...(environment === undefined ? {} : { env: environment }),
109
+ ...(onOutput === undefined ? {} : { onOutput }),
109
110
  });
110
111
  if (outcome.kind === "cancelled")
111
112
  return outcome;
@@ -49,7 +49,7 @@ export type WorkspaceChangeCounts = Readonly<{
49
49
  }>;
50
50
  export type ConflictRecovery = Readonly<{
51
51
  materialize: "deliver --materialize-conflict --include-dirty";
52
- continue: "deliver --include-dirty";
52
+ deliver: "deliver --include-dirty";
53
53
  staging: "not-required";
54
54
  }>;
55
55
  export declare const conflictRecovery: ConflictRecovery;
@@ -195,7 +195,7 @@ export async function unmergedWorkspacePaths(repository, workspace) {
195
195
  }
196
196
  export const conflictRecovery = Object.freeze({
197
197
  materialize: "deliver --materialize-conflict --include-dirty",
198
- continue: "deliver --include-dirty",
198
+ deliver: "deliver --include-dirty",
199
199
  staging: "not-required",
200
200
  });
201
201
  function countsOf(changes) {
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Mint one identity segment from a fitted stem by drawing a fresh random suffix
3
+ * and letting the caller admit the candidate. A collision redraws the suffix
4
+ * within the attempt budget; exhaustion returns the last collision rather than
5
+ * lengthening the identity.
6
+ */
7
+ export declare function mintIdentitySegment<Value>(input: Readonly<{
8
+ stem: string;
9
+ attempts: number;
10
+ drawSuffix: () => string;
11
+ attempt: (segment: string) => Promise<Value>;
12
+ collision: (value: Value) => boolean;
13
+ }>): Promise<Value>;
@@ -0,0 +1,26 @@
1
+ const IDENTITY_SUFFIX_PATTERN = /^[0-9a-f]{4}$/u;
2
+ function requireIdentitySuffix(value) {
3
+ if (!IDENTITY_SUFFIX_PATTERN.test(value)) {
4
+ throw new Error("identity suffix must be four lowercase hexadecimal digits");
5
+ }
6
+ return value;
7
+ }
8
+ /**
9
+ * Mint one identity segment from a fitted stem by drawing a fresh random suffix
10
+ * and letting the caller admit the candidate. A collision redraws the suffix
11
+ * within the attempt budget; exhaustion returns the last collision rather than
12
+ * lengthening the identity.
13
+ */
14
+ export async function mintIdentitySegment(input) {
15
+ if (!Number.isSafeInteger(input.attempts) || input.attempts < 1) {
16
+ throw new Error("identity mint attempt budget must be a positive safe integer");
17
+ }
18
+ let result;
19
+ for (let index = 0; index < input.attempts; index += 1) {
20
+ const suffix = requireIdentitySuffix(input.drawSuffix());
21
+ result = await input.attempt(`${input.stem}-${suffix}`);
22
+ if (!input.collision(result))
23
+ return result;
24
+ }
25
+ return result;
26
+ }
@@ -1,6 +1,10 @@
1
1
  export declare function normalizeIdentityStem(input: Readonly<{
2
2
  source: string;
3
3
  }>): string;
4
+ export declare function fitIdentityStemWords(input: Readonly<{
5
+ stem: string;
6
+ maxCodePoints: number;
7
+ }>): string;
4
8
  export declare function fitIdentityStem(input: Readonly<{
5
9
  stem: string;
6
10
  maxBytes: number;
@@ -21,6 +21,36 @@ export function normalizeIdentityStem(input) {
21
21
  }
22
22
  return result;
23
23
  }
24
+ export function fitIdentityStemWords(input) {
25
+ if (!Number.isSafeInteger(input.maxCodePoints) || input.maxCodePoints < 1) {
26
+ throw new Error("identity stem code point budget must be a positive safe integer");
27
+ }
28
+ const words = input.stem.split("-");
29
+ let fitted = truncateGraphemes(words[0], input.maxCodePoints);
30
+ let count = [...fitted].length;
31
+ for (const word of words.slice(1)) {
32
+ const candidate = count + 1 + [...word].length;
33
+ if (candidate > input.maxCodePoints)
34
+ break;
35
+ fitted += `-${word}`;
36
+ count = candidate;
37
+ }
38
+ if (fitted.length === 0)
39
+ throw new Error("identity stem is empty after fitting");
40
+ return fitted;
41
+ }
42
+ function truncateGraphemes(value, maxCodePoints) {
43
+ let result = "";
44
+ let count = 0;
45
+ for (const { segment } of GRAPHEMES.segment(value)) {
46
+ const size = [...segment].length;
47
+ if (count + size > maxCodePoints)
48
+ break;
49
+ result += segment;
50
+ count += size;
51
+ }
52
+ return result;
53
+ }
24
54
  export function fitIdentityStem(input) {
25
55
  if (!Number.isSafeInteger(input.maxBytes) || input.maxBytes < 1) {
26
56
  throw new Error("identity stem byte budget must be a positive safe integer");
@@ -1,4 +1,6 @@
1
1
  export * from "./library/keiyaku.js";
2
+ export { Akuma, Schema, ALLOWED_ACTIONS, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, } from "./akuma/index.js";
3
+ export type { AkumaBirthInput, AkumaHistoryOptions, AkumaIdleOptions, AkumaSignalOptions, AkumaTellOptions, InterruptReceipt, KillEvidence, JsonSchema, JsonSchemaDocument, AkuId, AkumaStatus, ActivityHistory, ActivityRow, AllowedAction, AllowedActions, } from "./akuma/index.js";
2
4
  export { settings } from "./settings.js";
3
5
  export { World, WorldError } from "./world.js";
4
6
  export type { WorldResolution, WorldResolutionInput, WorldRoot } from "./world.js";
@@ -1,3 +1,4 @@
1
1
  export * from "./library/keiyaku.js";
2
+ export { Akuma, Schema, ALLOWED_ACTIONS, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, } from "./akuma/index.js";
2
3
  export { settings } from "./settings.js";
3
4
  export { World, WorldError } from "./world.js";
@@ -15,7 +15,7 @@ import { readDocuments } from "../protocol/read/documents.js";
15
15
  import { contractId } from "../core/facts/types.js";
16
16
  import { selectKanshi, selectRegion } from "./select.js";
17
17
  import { FLEET_SNAPSHOT_ROWS, FLEET_VISIBLE_ROWS } from "./fleet.js";
18
- import { observeRecentTaskStatus } from "../task/index.js";
18
+ import { observeRecentTaskStatus, TaskAuthorityCorruptionError } from "../task/index.js";
19
19
  function diagnostic(error) {
20
20
  let source;
21
21
  if (error instanceof Error)
@@ -224,6 +224,15 @@ function joinTasks(rows, holders, observeContract) {
224
224
  return { ...row, contract: { id: contractId, observed: observeContract(contractId) } };
225
225
  });
226
226
  }
227
+ /** Attribute a Task board failure to the malformed document that caused it, when one is known. */
228
+ function taskFailure(error) {
229
+ return {
230
+ message: diagnostic(error),
231
+ ...(error instanceof TaskAuthorityCorruptionError && error.coordinate !== undefined
232
+ ? { coordinate: error.coordinate }
233
+ : {}),
234
+ };
235
+ }
227
236
  async function readTaskWorld(path) {
228
237
  if (path === null)
229
238
  return { kind: "absent" };
@@ -231,14 +240,18 @@ async function readTaskWorld(path) {
231
240
  return { kind: "present", observation: await observeTaskBoard(path) };
232
241
  }
233
242
  catch (error) {
234
- return { kind: "failed", failure: { message: diagnostic(error) } };
243
+ return { kind: "failed", failure: taskFailure(error) };
235
244
  }
236
245
  }
246
+ /**
247
+ * A selected Contract's own Task namespace view. A failed Task board is reported once by the
248
+ * board's Task section, so the entity block omits it rather than repeating the same row.
249
+ */
237
250
  function namespaceTaskSection(board, id) {
238
251
  if (board.kind === "absent")
239
252
  return { kind: "absent" };
240
253
  if (board.kind === "failed")
241
- return { kind: "failed", failure: board.failure };
254
+ return undefined;
242
255
  return { kind: "present", value: board.observation.selectNamespace(contractNamespace(id)) };
243
256
  }
244
257
  async function readTasks(path, holders, observeContract) {
@@ -247,7 +260,7 @@ async function readTasks(path, holders, observeContract) {
247
260
  return observeRecentTaskStatus(path, { limit: 10 }).then((recent) => ({
248
261
  kind: "present",
249
262
  value: { root: path, hasMore: recent.hasMore, rows: joinTasks(recent.rows, holders, observeContract) },
250
- }), (error) => ({ kind: "failed", failure: { message: diagnostic(error) } }));
263
+ }), (error) => ({ kind: "failed", failure: taskFailure(error) }));
251
264
  }
252
265
  async function joinAkuma(path, observeContract, dispatches, aliases) {
253
266
  if (aliases.kind !== "present")
@@ -17,6 +17,7 @@ export type Section<Value> = Readonly<{
17
17
  kind: "failed";
18
18
  failure: Readonly<{
19
19
  message: string;
20
+ coordinate?: string;
20
21
  }>;
21
22
  }>;
22
23
  export type ContractEndpointObservation = ContractDisposition | "missing" | "unavailable";
@@ -54,6 +54,7 @@ export type CallInput = Readonly<{
54
54
  alias?: AkumaAlias;
55
55
  allowed?: readonly AllowedAction[];
56
56
  schema?: Schema<unknown>;
57
+ initiator?: string;
57
58
  }>;
58
59
  export type CallObservation = Readonly<{
59
60
  kind: "detached";
@@ -89,6 +90,7 @@ export type BornCall = Readonly<{
89
90
  schemaTell?: Readonly<{
90
91
  body: string;
91
92
  schema: Schema<unknown>;
93
+ initiator?: string;
92
94
  }>;
93
95
  }>;
94
96
  export type ForkInput = Readonly<{
@@ -148,7 +148,7 @@ async function emitCalledSignal(input) {
148
148
  async function admitCall(input) {
149
149
  const born = await input.world.admit(input.call, input.context);
150
150
  const akuma = born.kind === "requested" ? born.id : born.allocated.id;
151
- void emitCalledSignal({
151
+ await emitCalledSignal({
152
152
  path: input.path,
153
153
  ...(input.settings === undefined ? {} : { settings: input.settings }),
154
154
  born,
@@ -242,26 +242,29 @@ async function resolveAliasStage(path, alias, dispatch, akuma) {
242
242
  return { kind: "failed", failure: integrationFailure(error) };
243
243
  }
244
244
  }
245
+ const CALL_INPUT_KEYS = [
246
+ "path",
247
+ "archetype",
248
+ "body",
249
+ "cwd",
250
+ "readonly",
251
+ "mode",
252
+ "timeoutMs",
253
+ "home",
254
+ "settings",
255
+ "contract",
256
+ "alias",
257
+ "allowed",
258
+ "schema",
259
+ "initiator",
260
+ ];
245
261
  async function prepareCall(input, context) {
246
262
  const values = requireInput(input, "Keiyaku.call input");
247
- onlyKeys(values, [
248
- "path",
249
- "archetype",
250
- "body",
251
- "cwd",
252
- "readonly",
253
- "mode",
254
- "timeoutMs",
255
- "home",
256
- "settings",
257
- "contract",
258
- "alias",
259
- "allowed",
260
- "schema",
261
- ], "Keiyaku.call input");
263
+ onlyKeys(values, CALL_INPUT_KEYS, "Keiyaku.call input");
262
264
  const path = await World.prove(nonblank(values.path, "path"));
263
265
  const archetype = nonblank(values.archetype, "archetype");
264
266
  const body = text(values.body, "body");
267
+ const initiator = values.initiator === undefined ? undefined : text(values.initiator, "initiator");
265
268
  const readonlyRequested = callReadonly(values.readonly, "readonly must be true").readonly;
266
269
  const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
267
270
  const mode = callMode(values.mode);
@@ -279,6 +282,7 @@ async function prepareCall(input, context) {
279
282
  const call = {
280
283
  archetype,
281
284
  ...(values.schema === undefined ? { body } : {}),
285
+ ...(initiator === undefined ? {} : { initiator }),
282
286
  ...(readonlyRequested === undefined ? {} : { readonly: readonlyRequested }),
283
287
  ...(values.allowed === undefined ? {} : { allowed: values.allowed }),
284
288
  ...(values.schema === undefined ? {} : { schema: values.schema }),
@@ -305,7 +309,15 @@ async function prepareCall(input, context) {
305
309
  timeoutMs,
306
310
  dispatch,
307
311
  alias: aliasStage,
308
- ...(values.schema === undefined ? {} : { schemaTell: { body, schema: values.schema } }),
312
+ ...(values.schema === undefined
313
+ ? {}
314
+ : {
315
+ schemaTell: {
316
+ body,
317
+ schema: values.schema,
318
+ ...(initiator === undefined ? {} : { initiator }),
319
+ },
320
+ }),
309
321
  };
310
322
  }
311
323
  async function publishCall(born, execution) {
@@ -326,8 +338,12 @@ async function publishCall(born, execution) {
326
338
  target: handle.id,
327
339
  body: tell.body,
328
340
  schema: tell.schema,
341
+ ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
329
342
  })
330
- : PublicAkuma.select(born.path, handle.id).tell(tell.body, { schema: tell.schema }));
343
+ : PublicAkuma.select(born.path, handle.id).tell(tell.body, {
344
+ schema: tell.schema,
345
+ ...(tell.initiator === undefined ? {} : { initiator: tell.initiator }),
346
+ }));
331
347
  if (born.mode === "detach")
332
348
  void pending.catch(() => undefined);
333
349
  else
@@ -1,3 +1,4 @@
1
+ import type { ExecutionObserver } from "../protocol/execution-observation.js";
1
2
  import type { ActorId, ContractId } from "../core/facts/types.js";
2
3
  import { type AuditReport } from "../protocol/audit.js";
3
4
  import { type RepositoryScope } from "../protocol/operations.js";
@@ -24,4 +25,5 @@ export declare function auditContract(input: Readonly<{
24
25
  contractId: ContractId;
25
26
  input: AuditOptions;
26
27
  composition?: AuditComposition;
28
+ observe?: ExecutionObserver;
27
29
  }>): Promise<MutationResult<AuditReport>>;
@@ -11,6 +11,7 @@ export async function auditContract(input) {
11
11
  return withContractExecution({
12
12
  scope: input.scope,
13
13
  contractId: input.contractId,
14
+ ...(input.observe === undefined ? {} : { observe: input.observe }),
14
15
  hooks: composition?.hooks ?? worktreeHooksOption(undefined),
15
16
  ...(input.input.signal === undefined ? {} : { signal: input.input.signal }),
16
17
  }, "audit", async ({ scope, channel, progress }) => {
@@ -2,16 +2,17 @@ import { randomBytes } from "node:crypto";
2
2
  import { decodeContractDocument } from "../body/decode.js";
3
3
  import { contractIdFromSegment } from "../core/facts/types.js";
4
4
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
5
- import { fitIdentityStem, normalizeIdentityStem } from "../identity/normalize.js";
5
+ import { mintIdentitySegment } from "../identity/mint.js";
6
+ import { fitIdentityStemWords, normalizeIdentityStem } from "../identity/normalize.js";
6
7
  import { bindOperation } from "../protocol/bind.js";
7
8
  import { stateOperation } from "../protocol/operations.js";
8
9
  import { claimTaskHolder, claimTaskHolderWithFence } from "../settlement/holder.js";
9
10
  import { KeiyakuRefused } from "./refusal.js";
10
11
  import { contractTerms, documentDerivation } from "./input.js";
11
- function candidateId(title, collision) {
12
- const stem = fitIdentityStem({ stem: normalizeIdentityStem({ source: title }) || "contract", maxBytes: 48 });
13
- const suffix = collision === 0 ? "" : `-${randomBytes(8).toString("hex")}`;
14
- return contractIdFromSegment(`${stem}${suffix}`);
12
+ const CONTRACT_ID_CODE_POINTS = 32;
13
+ const CONTRACT_ID_ATTEMPTS = 4;
14
+ function drawContractSuffix() {
15
+ return randomBytes(2).toString("hex");
15
16
  }
16
17
  async function attempt(input, id) {
17
18
  return bindOperation({
@@ -33,16 +34,17 @@ async function attempt(input, id) {
33
34
  });
34
35
  }
35
36
  async function attemptCandidates(input) {
36
- let result;
37
- for (let collision = 0; collision <= 3; collision += 1) {
38
- const id = candidateId(input.title, collision);
39
- result = await attempt(input, id);
40
- if (result.kind === "accepted")
41
- return result;
42
- if (result.kind !== "refused" || result.refusal.kind !== "contract-exists")
43
- return result;
44
- }
45
- return result;
37
+ const stem = fitIdentityStemWords({
38
+ stem: normalizeIdentityStem({ source: input.title }) || "contract",
39
+ maxCodePoints: CONTRACT_ID_CODE_POINTS,
40
+ });
41
+ return await mintIdentitySegment({
42
+ stem,
43
+ attempts: CONTRACT_ID_ATTEMPTS,
44
+ drawSuffix: drawContractSuffix,
45
+ attempt: (segment) => attempt(input, contractIdFromSegment(segment)),
46
+ collision: (result) => result.kind === "refused" && result.refusal.kind === "contract-exists",
47
+ });
46
48
  }
47
49
  export async function admitBindWithAppointment(input) {
48
50
  return await attemptCandidates(input);
@@ -86,7 +88,7 @@ export async function admitForkBindWithAppointment(input) {
86
88
  throw error;
87
89
  }
88
90
  const markdown = sourceDocument.document.bytes.replace(/^\s*#\s*[^\r\n]*(?:\r?\n|$)/u, `# Fork · ${sourceDocument.title}${sourceDocument.document.bytes.includes("\r\n") ? "\r\n" : "\n"}`);
89
- const document = decodeContractDocument(markdown);
91
+ const document = decodeContractDocument(markdown, { requireTimeout: true });
90
92
  const terms = contractTerms(document, source.terms.gates, source.terms.after);
91
93
  const admission = await attemptCandidates({
92
94
  scope: input.scope,
@@ -90,6 +90,7 @@ export declare function composeLibrary(context?: ExecutionContext, composition?:
90
90
  status: {
91
91
  id: import("./keiyaku.js").AkuId;
92
92
  life: "killed" | "hung" | "untidy" | "running" | "asleep" | "stranded";
93
+ allowed: import("./keiyaku.js").AllowedActions;
93
94
  timeline: {
94
95
  reportedChanges: readonly {
95
96
  sequence: number;
@@ -20,7 +20,9 @@ type BindInput = Readonly<{
20
20
  type BindResult<Handle> = Readonly<Omit<MutationResult<Handle>, "value"> & {
21
21
  keiyaku: Handle;
22
22
  workspace?: ContractWorkspaceLocation;
23
- } & RegionObservation>;
23
+ } & RegionObservation & {
24
+ warnings?: readonly string[];
25
+ }>;
24
26
  type HandleFactory<Handle> = (contractId: ContractId, scope: RepositoryScope) => Handle;
25
27
  export declare function bindKeiyaku<Handle>(input: BindInput, createHandle: HandleFactory<Handle>): Promise<BindResult<Handle>>;
26
28
  export declare function parseMarkdownBindDocument(markdown: string): import("../body/types.js").DecodedContractDocument;
@@ -7,10 +7,11 @@ import { completionInput, completeHolderMutation, completeMutation } from "./mut
7
7
  import { reconcileLagScope } from "./reconcile.js";
8
8
  import { requireAccepted } from "./refusal.js";
9
9
  import { observeRegion } from "./region.js";
10
+ import { regionWarnings } from "../body/region.js";
10
11
  import { scopeForRepo } from "./repo.js";
11
12
  import { worktreeHooksOption } from "./configuration.js";
12
13
  import { placeRegisterPath, readManagedWorktreeAppointment, } from "../workspace-place.js";
13
- async function acceptedBindResult(result, region, scope, contractId) {
14
+ async function acceptedBindResult(result, region, scope, contractId, warnings = []) {
14
15
  const { value: keiyaku, ...base } = result;
15
16
  const appointment = await readManagedWorktreeAppointment(scope, contractId);
16
17
  const registerPath = placeRegisterPath(scope);
@@ -34,6 +35,7 @@ async function acceptedBindResult(result, region, scope, contractId) {
34
35
  pending: scopedAppointmentLag.length === 0
35
36
  ? base.pending
36
37
  : [...base.pending, { surface: "reconciliation", required: true }],
38
+ ...(warnings.length === 0 ? {} : { warnings }),
37
39
  ...region,
38
40
  };
39
41
  }
@@ -85,7 +87,7 @@ export async function bindKeiyaku(input, createHandle) {
85
87
  return bindMarkdownFromValues(values, "targetless", createHandle);
86
88
  }
87
89
  export function parseMarkdownBindDocument(markdown) {
88
- return decodeContractDocument(requireMarkdown(markdown));
90
+ return decodeContractDocument(requireMarkdown(markdown), { requireTimeout: true });
89
91
  }
90
92
  /** Internal CLI composition; not exported from the package root. */
91
93
  export async function bindFromCli(input, createHandle) {
@@ -133,6 +135,6 @@ async function bindMarkdownFromValues(values, omittedTarget, createHandle) {
133
135
  admission: admission.admission,
134
136
  requireAccepted,
135
137
  });
136
- return await acceptedBindResult(result, await observeRegion(scope, channel, id, document.region), scope, id);
138
+ return await acceptedBindResult(result, await observeRegion(scope, channel, id, document.region), scope, id, regionWarnings(document.region));
137
139
  });
138
140
  }
@@ -1,3 +1,4 @@
1
+ import type { ExecutionObserver } from "../protocol/execution-observation.js";
1
2
  import type { ActorId, ContractId } from "../core/facts/types.js";
2
3
  import { type GitDecodeChannel } from "../git/read-observation.js";
3
4
  import { type IntegrationConflictMaterialized } from "../protocol/deliver.js";
@@ -13,6 +14,7 @@ type CommonExecutionInput = Readonly<{
13
14
  actor?: ActorId;
14
15
  signal?: AbortSignal;
15
16
  hooks: WorktreeHooks;
17
+ observe?: ExecutionObserver;
16
18
  }>;
17
19
  export type DeliveryExecutionInput = CommonExecutionInput & Readonly<{
18
20
  message?: string;