@astrosheep/keiyaku 4.5.20 → 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 (154) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -8
  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 -3
  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 +22 -0
  14. package/build/src/akuma/akuma-product.d.ts +1 -0
  15. package/build/src/akuma/akuma-product.js +3 -0
  16. package/build/src/akuma/akuma.d.ts +1 -0
  17. package/build/src/akuma/body.d.ts +25 -5
  18. package/build/src/akuma/body.js +65 -8
  19. package/build/src/akuma/call-request.d.ts +2 -0
  20. package/build/src/akuma/call-request.js +2 -0
  21. package/build/src/akuma/fleet-execution.d.ts +28 -0
  22. package/build/src/akuma/fleet-execution.js +36 -4
  23. package/build/src/akuma/fleet-request.d.ts +10 -0
  24. package/build/src/akuma/fleet-request.js +11 -2
  25. package/build/src/akuma/heart/facts.d.ts +2 -0
  26. package/build/src/akuma/heart/index.d.ts +1 -0
  27. package/build/src/akuma/heart/index.js +4 -1
  28. package/build/src/akuma/heart/rows.d.ts +2 -0
  29. package/build/src/akuma/heart/rows.js +7 -5
  30. package/build/src/akuma/heart/schema.d.ts +1 -1
  31. package/build/src/akuma/heart/schema.js +3 -1
  32. package/build/src/akuma/heart/tells.js +6 -5
  33. package/build/src/akuma/heart/timeline.js +1 -1
  34. package/build/src/akuma/provider.d.ts +1 -1
  35. package/build/src/akuma/provider.js +1 -1
  36. package/build/src/akuma/providers/acp/core.js +3 -6
  37. package/build/src/akuma/providers/claude/index.js +9 -12
  38. package/build/src/akuma/providers/codex-app-server/index.js +4 -11
  39. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  40. package/build/src/akuma/providers/execution-environment.js +24 -0
  41. package/build/src/akuma/providers/opencode-sdk/index.js +30 -23
  42. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  43. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  44. package/build/src/akuma/providers/pi/events.js +7 -2
  45. package/build/src/akuma/providers/pi/index.js +3 -2
  46. package/build/src/akuma/request-rendezvous.js +8 -3
  47. package/build/src/akuma/turn-drive.d.ts +1 -0
  48. package/build/src/akuma/turn-drive.js +2 -0
  49. package/build/src/akuma-body.js +2 -0
  50. package/build/src/body/amend.js +6 -4
  51. package/build/src/body/decode.d.ts +3 -1
  52. package/build/src/body/decode.js +53 -17
  53. package/build/src/body/region.d.ts +9 -1
  54. package/build/src/body/region.js +87 -12
  55. package/build/src/body/render.js +1 -1
  56. package/build/src/body/verification.d.ts +3 -1
  57. package/build/src/body/verification.js +4 -1
  58. package/build/src/cli/accepted.js +19 -2
  59. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  60. package/build/src/cli/commands/akuma-invoke.js +133 -13
  61. package/build/src/cli/commands/akuma.js +11 -4
  62. package/build/src/cli/commands/contract-help.js +17 -4
  63. package/build/src/cli/commands/contract.js +6 -4
  64. package/build/src/cli/commands/status-set.js +15 -24
  65. package/build/src/cli/main.js +5 -0
  66. package/build/src/cli/parse.d.ts +2 -0
  67. package/build/src/cli/parse.js +5 -0
  68. package/build/src/cli/render/akuma-activity.d.ts +99 -2
  69. package/build/src/cli/render/akuma-activity.js +484 -40
  70. package/build/src/cli/render/akuma.d.ts +10 -0
  71. package/build/src/cli/render/akuma.js +28 -21
  72. package/build/src/cli/render/audit.js +1 -1
  73. package/build/src/cli/render/board.js +1 -1
  74. package/build/src/cli/render/catalog.js +16 -13
  75. package/build/src/cli/render/contract-history.js +39 -10
  76. package/build/src/cli/render/contract-observation.js +10 -6
  77. package/build/src/cli/render/contract.js +109 -106
  78. package/build/src/cli/render/execution-progress.d.ts +23 -1
  79. package/build/src/cli/render/execution-progress.js +151 -28
  80. package/build/src/cli/render/kanshi-akuma.js +27 -39
  81. package/build/src/cli/render/kanshi.js +33 -26
  82. package/build/src/cli/render/nuke.js +1 -1
  83. package/build/src/cli/render/receipt.d.ts +1 -1
  84. package/build/src/cli/render/receipt.js +5 -5
  85. package/build/src/cli/render/refusal.js +10 -7
  86. package/build/src/cli/render/region.js +1 -1
  87. package/build/src/cli/render/settings.js +12 -2
  88. package/build/src/cli/render/status-line.d.ts +30 -0
  89. package/build/src/cli/render/status-line.js +58 -0
  90. package/build/src/cli/render/status-set.js +1 -1
  91. package/build/src/cli/render/task.d.ts +2 -0
  92. package/build/src/cli/render/task.js +11 -7
  93. package/build/src/cli/render/terminal.d.ts +0 -1
  94. package/build/src/cli/render/terminal.js +47 -15
  95. package/build/src/cli/result.d.ts +1 -0
  96. package/build/src/cli/runtime.d.ts +2 -0
  97. package/build/src/cli/runtime.js +13 -7
  98. package/build/src/cli/usage.js +2 -2
  99. package/build/src/cli/version.d.ts +1 -0
  100. package/build/src/cli/version.js +25 -0
  101. package/build/src/git/workspace.d.ts +1 -1
  102. package/build/src/git/workspace.js +1 -1
  103. package/build/src/identity/mint.d.ts +13 -0
  104. package/build/src/identity/mint.js +26 -0
  105. package/build/src/identity/normalize.d.ts +4 -0
  106. package/build/src/identity/normalize.js +30 -0
  107. package/build/src/index.d.ts +2 -0
  108. package/build/src/index.js +1 -0
  109. package/build/src/kanshi/read.js +17 -4
  110. package/build/src/kanshi/report.d.ts +1 -0
  111. package/build/src/library/akuma-creation.d.ts +2 -0
  112. package/build/src/library/akuma-creation.js +33 -17
  113. package/build/src/library/bind.js +18 -16
  114. package/build/src/library/contract-bind.d.ts +3 -1
  115. package/build/src/library/contract-bind.js +5 -3
  116. package/build/src/library/contract-operations.d.ts +2 -2
  117. package/build/src/library/contract-operations.js +1 -1
  118. package/build/src/library/contract-types.d.ts +1 -0
  119. package/build/src/library/fleet.d.ts +4 -1
  120. package/build/src/library/fleet.js +32 -8
  121. package/build/src/plugin/akuma-signals.d.ts +6 -0
  122. package/build/src/plugin/akuma-signals.js +8 -0
  123. package/build/src/plugin/public.d.ts +4 -0
  124. package/build/src/plugin/runtime.js +4 -1
  125. package/build/src/protocol/amend.js +4 -3
  126. package/build/src/protocol/attempt.d.ts +2 -0
  127. package/build/src/protocol/bind.js +41 -14
  128. package/build/src/protocol/completion.d.ts +3 -0
  129. package/build/src/protocol/completion.js +7 -1
  130. package/build/src/protocol/deliver.js +165 -92
  131. package/build/src/protocol/execution-observation.d.ts +0 -4
  132. package/build/src/protocol/intent.d.ts +15 -20
  133. package/build/src/protocol/intent.js +29 -17
  134. package/build/src/protocol/operations.d.ts +2 -0
  135. package/build/src/protocol/placement.js +5 -1
  136. package/build/src/protocol/read/status.d.ts +1 -0
  137. package/build/src/protocol/read/status.js +10 -2
  138. package/build/src/protocol/reintegrate.d.ts +1 -1
  139. package/build/src/protocol/reintegrate.js +70 -43
  140. package/build/src/protocol/result-codec.js +9 -7
  141. package/build/src/protocol/review.js +53 -30
  142. package/build/src/protocol/run.d.ts +83 -36
  143. package/build/src/protocol/run.js +90 -75
  144. package/build/src/runtime/proc/windows-launch.exe +0 -0
  145. package/build/src/settings.js +5 -14
  146. package/build/src/task/document.d.ts +5 -1
  147. package/build/src/task/document.js +6 -1
  148. package/build/src/verification/execution.d.ts +0 -1
  149. package/build/src/verification/execution.js +0 -24
  150. package/build/src/verification/observation.d.ts +0 -4
  151. package/build/src/verification/observation.js +1 -2
  152. package/package.json +3 -1
  153. package/build/src/git/verification-environment.d.ts +0 -10
  154. package/build/src/git/verification-environment.js +0 -281
@@ -12,6 +12,16 @@ export function gitShortStat(stat) {
12
12
  return parts.join(", ");
13
13
  }
14
14
  const GRAPHEMES = new Intl.Segmenter("und", { granularity: "grapheme" });
15
+ const NON_ASCII = /[^\x20-\x7e]/u;
16
+ const MARK = /\p{Mark}/u;
17
+ const PICTOGRAPHIC = /\p{Extended_Pictographic}/u;
18
+ const EMOJI_DEFAULT = /\p{Emoji_Presentation}/u;
19
+ const ZWJ = 0x200d;
20
+ const TEXT_PRESENTATION = 0xfe0e;
21
+ const EMOJI_PRESENTATION = 0xfe0f;
22
+ const REGIONAL_FIRST = 0x1f1e6;
23
+ const REGIONAL_LAST = 0x1f1ff;
24
+ const ZERO_WIDTH = new Set([0x200b, 0x200c, 0x200d, 0x200e, 0x200f, 0x2060, 0xfeff]);
15
25
  const WIDE_RANGES = [
16
26
  [0x1100, 0x115f],
17
27
  [0x2329, 0x232a],
@@ -25,33 +35,59 @@ const WIDE_RANGES = [
25
35
  [0x1f300, 0x1faff],
26
36
  [0x20000, 0x3fffd],
27
37
  ];
28
- function characterColumns(character) {
29
- const point = character.codePointAt(0);
30
- if (point === undefined || point === 0 || /\p{Mark}/u.test(character) || point === 0xfe0f)
38
+ /** Columns one code point occupies when its grapheme cluster gives it no wider form. */
39
+ function baseColumns(point) {
40
+ if (point === 0 || point < 0x20 || (point >= 0x7f && point < 0xa0))
31
41
  return 0;
32
- if (point < 0x20 || (point >= 0x7f && point < 0xa0))
42
+ if (ZERO_WIDTH.has(point) || MARK.test(String.fromCodePoint(point)))
33
43
  return 0;
34
44
  if (point === 0x303f)
35
45
  return 1;
36
46
  return WIDE_RANGES.some(([first, last]) => point >= first && point <= last) ? 2 : 1;
37
47
  }
48
+ /**
49
+ * Columns one grapheme cluster occupies. A cluster is one terminal cell group even
50
+ * when it is built from many code points, so ZWJ sequences, flags, keycaps and
51
+ * variation selectors never count the width of their parts.
52
+ */
53
+ function graphemeColumns(cluster) {
54
+ const points = [...cluster].map((character) => character.codePointAt(0));
55
+ if (points.every((point) => point === EMOJI_PRESENTATION || point === TEXT_PRESENTATION))
56
+ return 0;
57
+ const first = points[0];
58
+ if (points.length > 1 && points.every((point) => point >= REGIONAL_FIRST && point <= REGIONAL_LAST))
59
+ return 2;
60
+ // A presentation selector decides a text-default symbol: text stays narrow, emoji widens it.
61
+ if (points.includes(TEXT_PRESENTATION))
62
+ return baseColumns(first);
63
+ if (points.includes(EMOJI_PRESENTATION))
64
+ return 2;
65
+ // A joined pictographic sequence is one wide cluster; a plain text symbol is not.
66
+ if (points.includes(ZWJ) && PICTOGRAPHIC.test(cluster))
67
+ return 2;
68
+ if (EMOJI_DEFAULT.test(cluster))
69
+ return 2;
70
+ return baseColumns(first);
71
+ }
38
72
  export function displayColumns(value) {
73
+ if (!NON_ASCII.test(value))
74
+ return value.length;
39
75
  let columns = 0;
40
- for (const character of value)
41
- columns += characterColumns(character);
76
+ for (const { segment } of GRAPHEMES.segment(value))
77
+ columns += graphemeColumns(segment);
42
78
  return columns;
43
79
  }
44
80
  export function takeDisplayColumns(value, maximum) {
45
- const characters = [...GRAPHEMES.segment(value)].map(({ segment }) => segment);
81
+ const clusters = [...GRAPHEMES.segment(value)].map(({ segment }) => segment);
46
82
  let columns = 0;
47
83
  let index = 0;
48
- for (; index < characters.length; index += 1) {
49
- const width = displayColumns(characters[index]);
84
+ for (; index < clusters.length; index += 1) {
85
+ const width = graphemeColumns(clusters[index]);
50
86
  if (columns + width > maximum)
51
87
  break;
52
88
  columns += width;
53
89
  }
54
- return { text: characters.slice(0, index).join(""), rest: characters.slice(index).join("") };
90
+ return { text: clusters.slice(0, index).join(""), rest: clusters.slice(index).join("") };
55
91
  }
56
92
  export function truncateDisplayText(value, maximum) {
57
93
  const clean = safeText(value);
@@ -210,10 +246,6 @@ export function tone(value, kind, color) {
210
246
  const code = kind === "dim" ? 2 : kind === "recent" ? 32 : kind === "attention" ? 33 : 31;
211
247
  return `\u001b[${code}m${value}\u001b[0m`;
212
248
  }
213
- export function boundedActivity(value, columns, prefix) {
214
- const budget = Math.max(1, columns - displayColumns(prefix));
215
- return `activity "${truncateDisplayText(value, Math.max(1, budget - displayColumns('activity ""')))}` + `"`;
216
- }
217
249
  export function plumbFacts(facts, columns) {
218
250
  const lines = [];
219
251
  let current = " ";
@@ -255,7 +287,7 @@ export function entityLines(entity) {
255
287
  ];
256
288
  }
257
289
  export function renderSectionBlock({ name, rows, hasMore = false, }) {
258
- const lines = [`[ ${name} ]`, "", ...rows.flat()];
290
+ const lines = [`${name}`, "", ...rows.flat()];
259
291
  if (hasMore)
260
292
  lines.push("…");
261
293
  return lines;
@@ -46,6 +46,7 @@ export type AcceptedBindResult = AcceptedEnvelope & Readonly<{
46
46
  path: string;
47
47
  }>;
48
48
  target: string | null;
49
+ warnings?: readonly string[];
49
50
  completion?: never;
50
51
  verification?: never;
51
52
  verificationReuse?: never;
@@ -1,5 +1,7 @@
1
1
  import type { TaskInvocationResult } from "./commands/task-invoke.js";
2
2
  import type { ParsedCommand, ParsedExecution } from "./parse.js";
3
+ import type { ExecutionEvent } from "../library/execution.js";
4
+ export declare function writeExecutionProgress(events: AsyncIterable<ExecutionEvent>, stream?: NodeJS.WritableStream): Promise<void>;
3
5
  export declare function writeTask(command: Extract<ParsedCommand, {
4
6
  command: "task";
5
7
  }>, result: TaskInvocationResult): Promise<number>;
@@ -5,10 +5,16 @@ 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
- async function writeExecutionProgress(events) {
9
- const { executionProgressLines } = await import("./render/execution-progress.js");
10
- for await (const event of events)
11
- writeCliStream(process.stderr, executionProgressLines(event, displayContext()).join("\n"));
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
+ });
12
18
  }
13
19
  function cliCancellation() {
14
20
  const controller = new AbortController();
@@ -132,7 +138,7 @@ function writeWorldScopeRefusal(error, command) {
132
138
  const body = command.output === "json"
133
139
  ? JSON.stringify(error.refusal)
134
140
  : [
135
- `✕ selector ${error.refusal.kind}`,
141
+ selector ${error.refusal.kind}`,
136
142
  ` world ${safeText(error.refusal.world)}`,
137
143
  ...error.refusal.ids.map((id) => ` given ${safeText(id)}`),
138
144
  ` accepts ${guide.accepts}`,
@@ -154,9 +160,9 @@ async function commandFailureText(error, command) {
154
160
  }, "selector");
155
161
  }
156
162
  const { renderRefusalFacts } = await import("./render/refusal.js");
157
- return [`✕ ${command.command} refused`, ...renderRefusalFacts(error.refusal, " ", displayContext().columns)].join("\n");
163
+ return [ ${command.command} refused`, ...renderRefusalFacts(error.refusal, " ", displayContext().columns)].join("\n");
158
164
  }
159
- return `✕ ${command.command} failed\n diagnostic ${safeText(diagnostic)}`;
165
+ return ${command.command} failed\n diagnostic ${safeText(diagnostic)}`;
160
166
  }
161
167
  export async function runCliCommand(invocation) {
162
168
  const command = invocation.command;
@@ -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
+ }
@@ -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<{
@@ -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
@@ -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,
@@ -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
  }
@@ -91,7 +91,7 @@ declare const materializedHandoffReferenceSchema: z.ZodObject<{
91
91
  handoffBase: z.ZodPipe<z.ZodString, z.ZodTransform<import("../core/facts/types.js").SnapshotId, string>>;
92
92
  recovery: z.ZodObject<{
93
93
  materialize: z.ZodLiteral<"deliver --materialize-conflict --include-dirty">;
94
- continue: z.ZodLiteral<"deliver --include-dirty">;
94
+ deliver: z.ZodLiteral<"deliver --include-dirty">;
95
95
  staging: z.ZodLiteral<"not-required">;
96
96
  }, z.core.$strict>;
97
97
  conflictPaths: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<readonly string[], string[]>>;
@@ -216,7 +216,7 @@ declare const contractServiceSchema: z.ZodUnion<readonly [z.ZodObject<{
216
216
  handoffBase: z.ZodPipe<z.ZodString, z.ZodTransform<import("../core/facts/types.js").SnapshotId, string>>;
217
217
  recovery: z.ZodObject<{
218
218
  materialize: z.ZodLiteral<"deliver --materialize-conflict --include-dirty">;
219
- continue: z.ZodLiteral<"deliver --include-dirty">;
219
+ deliver: z.ZodLiteral<"deliver --include-dirty">;
220
220
  staging: z.ZodLiteral<"not-required">;
221
221
  }, z.core.$strict>;
222
222
  conflictPaths: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<readonly string[], string[]>>;
@@ -31,7 +31,7 @@ const contractRequestBaseSchema = z.object({ repoRoot: absolutePathSchema, contr
31
31
  const conflictRecoverySchema = z
32
32
  .object({
33
33
  materialize: z.literal("deliver --materialize-conflict --include-dirty"),
34
- continue: z.literal("deliver --include-dirty"),
34
+ deliver: z.literal("deliver --include-dirty"),
35
35
  staging: z.literal("not-required"),
36
36
  })
37
37
  .strict();
@@ -13,6 +13,7 @@ import type { ContractWorkspaceLocation } from "../workspace-place.js";
13
13
  export type BindResult = Readonly<Omit<MutationResult<Keiyaku>, "value"> & {
14
14
  keiyaku: Keiyaku;
15
15
  workspace?: ContractWorkspaceLocation;
16
+ warnings?: readonly string[];
16
17
  } & RegionObservation>;
17
18
  export type Fact = JournalEntry;
18
19
  export type ContractHistoryEvent = Readonly<{