@astrosheep/keiyaku 2.9.2 → 2.9.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 (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -6,6 +6,40 @@ import { appendDebugLog } from "../../telemetry/debug-log.js";
6
6
  import { ACTOR_IDENTITY, TOOL_NAMES, BUILTIN_AGENT_NAMES } from "../../keiyaku.js";
7
7
  import { buildErrorResponse } from "./misc.js";
8
8
  import { addressCarrierFromError } from "../../core/addressing.js";
9
+ function renderScopeViolation(evidence) {
10
+ if (evidence.outcome === "no-positive-match") {
11
+ return ` ${evidence.path}\n no positive pattern matched`;
12
+ }
13
+ const decisions = evidence.patterns.map((decision) => {
14
+ const isDecisive = evidence.decisivePattern?.index === decision.index
15
+ && evidence.decisivePattern?.pattern === decision.pattern;
16
+ const effectLabel = decision.effect === "exclude" ? "excluded" : "included";
17
+ if (isDecisive) {
18
+ return ` ${decision.pattern} → decisive (${effectLabel})`;
19
+ }
20
+ return ` ${decision.pattern} → ${effectLabel}`;
21
+ });
22
+ return ` ${evidence.path}\n${decisions.join("\n")}`;
23
+ }
24
+ /** CLI face for scope_violation facts. Preserves pre-migration multi-line bytes. */
25
+ export function renderScopeAuditMessage(scope, evidence) {
26
+ const count = evidence.length;
27
+ const fileLabel = count === 1 ? "file" : "files";
28
+ const body = evidence.map(renderScopeViolation).join("\n\n");
29
+ return [
30
+ `scope violation — ${count} ${fileLabel} outside declared scope:`,
31
+ "",
32
+ body,
33
+ "",
34
+ `declared scope: ${scope.join(" ")}`,
35
+ ].join("\n");
36
+ }
37
+ function renderErrorMessage(error) {
38
+ if (isFlowError(error) && error.facts?.kind === "scope_violation") {
39
+ return renderScopeAuditMessage(error.facts.scope, error.facts.violations);
40
+ }
41
+ return asMessage(error);
42
+ }
9
43
  export function classifyCliError(error) {
10
44
  if (isFlowError(error)) {
11
45
  return { errorType: "keiyaku_error", errorCode: error.code };
@@ -26,7 +60,7 @@ async function getAvailableActorNames(cwd, fallback) {
26
60
  }
27
61
  }
28
62
  export async function handleCliError(input) {
29
- const message = asMessage(input.error);
63
+ const message = renderErrorMessage(input.error);
30
64
  appendDebugLog(`${input.logLabel} error: ${message}`, { cwd: input.cwd, section: "script" });
31
65
  const availableActorNames = await getAvailableActorNames(input.cwd, BUILTIN_AGENT_NAMES);
32
66
  const hints = pickHintsFromError(input.error, {
@@ -42,6 +76,7 @@ export async function handleCliError(input) {
42
76
  message,
43
77
  hints,
44
78
  review: isFlowError(input.error) ? input.error.review : undefined,
79
+ facts: isFlowError(input.error) ? input.error.facts : undefined,
45
80
  errorType,
46
81
  errorCode,
47
82
  addressCarrier: addressCarrierFromError(input.error),
@@ -212,6 +212,10 @@ function truncateStructuredObject(data, parentKey) {
212
212
  }
213
213
  return truncated;
214
214
  }
215
+ /**
216
+ * Final serialization boundary for command payloads: optional responsePath stamp
217
+ * plus structured truncation. Callers supply a named *Payload that is JsonObject-compatible.
218
+ */
215
219
  export function buildPayload(data, responsePath) {
216
220
  const dataWithPath = responsePath ? { ...data, responsePath } : data;
217
221
  const truncatedData = truncateStructuredObject(dataWithPath);
@@ -4,9 +4,6 @@ function shortHead(hash) {
4
4
  return "????????";
5
5
  return hash.slice(0, 7);
6
6
  }
7
- function compactWhitespace(value) {
8
- return value.replace(/\s+/g, " ").trim();
9
- }
10
7
  function arcSparkline(arcs) {
11
8
  if (arcs <= 0)
12
9
  return "";
@@ -35,31 +32,28 @@ export function renderKanshiBoard(status) {
35
32
  const head = shortHead(status.defaultHead);
36
33
  if (status.contracts.length === 0) {
37
34
  if (status.targetFilter !== null) {
38
- return `現世 ${head} · target ${status.targetFilter}\nno commissions on target ${status.targetFilter}\n`;
35
+ return `現世 ${head} · target selected\nno commissions on selected target\n`;
39
36
  }
40
37
  return `現世 ${head} 无在飞契约\n`;
41
38
  }
42
39
  const header = status.targetFilter !== null
43
- ? `現世 ${head} · target ${status.targetFilter}`
40
+ ? `現世 ${head} · target selected`
44
41
  : `現世 ${head}`;
45
42
  const lines = [header, ""];
46
43
  for (const contract of status.contracts) {
47
44
  const glyph = kanshiGlyph(contract);
48
- const name = contract.slug;
45
+ const name = contract.place ? `${contract.slug} @${contract.place}` : contract.slug;
49
46
  const spark = arcSparkline(contract.arcs);
50
47
  const facts = [];
51
48
  const lifecycle = contract.state.state;
52
49
  if (lifecycle === "bound" || lifecycle === "active" || lifecycle === "petitioned") {
53
50
  facts.push(lifecycle);
54
51
  }
55
- if (contract.place) {
56
- facts.push(`@${contract.place}`);
57
- }
58
- const arcObjective = compactWhitespace(contract.currentArc.objective || "(no arc objective)");
59
52
  const arcMark = contract.currentArc.state === "open"
60
53
  ? `a${contract.currentArc.number}+`
61
54
  : `a${contract.currentArc.number}`;
62
- facts.push(`${arcMark} ${arcObjective}`);
55
+ facts.push(contract.title);
56
+ facts.push(arcMark);
63
57
  facts.push(`next ${contract.nextStep.detail}`);
64
58
  const age = formatAgeMs(contract.lastActiveMs);
65
59
  if (age)
@@ -99,7 +93,7 @@ export function renderKanshiBoard(status) {
99
93
  lines.push(` ! seat ${diagnostic.seat} duplicated with ${diagnostic.with} · ordered by contract id`);
100
94
  continue;
101
95
  }
102
- lines.push(` ! target mismatch · petitioned ${diagnostic.petitioned} · derived ${diagnostic.derived}`);
96
+ lines.push(" ! target mismatch");
103
97
  }
104
98
  }
105
99
  return `${lines.join("\n")}\n`;
@@ -0,0 +1,9 @@
1
+ import { textResponse } from "./shared.js";
2
+ /** Default kill acceptance: intent landed; wait is still required for the terminal face. */
3
+ export function buildKillAcceptedResponse(projectionId) {
4
+ return textResponse(`✓ kill ${projectionId}\n» keiyaku wait ${projectionId}`);
5
+ }
6
+ /** Kill against an already-terminal generation. */
7
+ export function buildKillAlreadyClosedResponse(projectionId) {
8
+ return textResponse(`· ${projectionId} already closed`);
9
+ }
@@ -2,19 +2,22 @@ import { assembleResponse, buildPayload, buildSection, colorizeErrorStatus, FORM
2
2
  import { RESPONSE_MARKERS } from "./response-style.js";
3
3
  import { formatReview, HINTS_LIST_MAX_ITEMS, REVIEW_SECTION_TITLE, toReviewPayload, } from "./shared.js";
4
4
  import { prependAddressReceipt, renderAddressCarrier } from "./address.js";
5
+ import { commissionSlug } from "../../core/ids.js";
5
6
  export function buildErrorResponse(input) {
6
7
  const shouldRaiseProtocolError = input.errorType === "runtime_error";
7
8
  const text = buildErrorText(input);
9
+ const payload = {
10
+ error: {
11
+ code: input.errorCode,
12
+ message: input.message,
13
+ hints: input.hints,
14
+ ...(input.review ? { review: toReviewPayload(input.review) } : {}),
15
+ ...(input.facts ? { facts: input.facts } : {}),
16
+ },
17
+ };
8
18
  const response = {
9
19
  text,
10
- payload: {
11
- error: {
12
- code: input.errorCode,
13
- message: input.message,
14
- hints: input.hints,
15
- ...(input.review ? { review: toReviewPayload(input.review) } : {}),
16
- },
17
- },
20
+ payload,
18
21
  };
19
22
  if (shouldRaiseProtocolError) {
20
23
  response.isError = true;
@@ -23,21 +26,61 @@ export function buildErrorResponse(input) {
23
26
  }
24
27
  function buildErrorText(input) {
25
28
  const errorSection = buildSection("Error", input.message);
29
+ const recoverySection = input.facts?.kind === "renew_conflict"
30
+ ? buildSection("Recovery", [
31
+ "Conflicting paths:",
32
+ ...input.facts.conflictPaths.map((filePath) => ` ${filePath}`),
33
+ ...(input.facts.conflictPathCapture.status === "failed"
34
+ ? [`Conflict-path capture failed: ${input.facts.conflictPathCapture.diagnostic}`]
35
+ : []),
36
+ "Delivery branch and worktree were restored; no ledger writes were appended.",
37
+ ].join("\n"))
38
+ : null;
26
39
  const reviewSection = input.review ? buildSection(REVIEW_SECTION_TITLE, formatReview(input.review)) : null;
27
- const hintsSection = buildSection("Hints", formatHints(input.hints, { maxItems: HINTS_LIST_MAX_ITEMS, maxItemChars: FORMAT_LIST_MAX_ITEM_CHARS }));
28
- const body = assembleResponse(colorizeErrorStatus(`${RESPONSE_MARKERS.failure} Failed [${input.errorCode}]`), "Request could not be completed.", [errorSection, reviewSection, hintsSection].filter((section) => section !== null));
40
+ const commissionedCarrier = input.addressCarrier && !("expected" in input.addressCarrier)
41
+ && input.addressCarrier.binding.kind === "commission"
42
+ ? input.addressCarrier
43
+ : undefined;
44
+ const recoveryAddress = commissionedCarrier
45
+ ? `@${commissionedCarrier.place.display ?? commissionSlug(commissionedCarrier.binding.commissionId)}`
46
+ : input.facts?.kind === "renew_session"
47
+ ? `@${commissionSlug(input.facts.commission)}`
48
+ : undefined;
49
+ const nextActionSection = input.facts?.kind === "renew_session" && recoveryAddress
50
+ ? buildSection("Next action", input.facts.nextAction === "resolve-conflicts"
51
+ ? [
52
+ "Resolve and stage the conflicting delivery with ordinary Git, then:",
53
+ `» keiyaku ${recoveryAddress} renew --continue`,
54
+ "Or restore the pre-renew delivery:",
55
+ `» keiyaku ${recoveryAddress} renew --abort`,
56
+ ]
57
+ : input.facts.nextAction === "continue-or-abort" ? [
58
+ `» keiyaku ${recoveryAddress} renew --continue`,
59
+ `» keiyaku ${recoveryAddress} renew --abort`,
60
+ ] : [`» keiyaku ${recoveryAddress} renew --abort`])
61
+ : null;
62
+ const hints = input.facts?.kind === "renew_session"
63
+ ? []
64
+ : formatHints(input.hints, { maxItems: HINTS_LIST_MAX_ITEMS, maxItemChars: FORMAT_LIST_MAX_ITEM_CHARS });
65
+ if (input.facts?.kind === "renew_conflict") {
66
+ hints.push("your old head survives:", `» git log ${input.facts.recoveryRef}`);
67
+ }
68
+ const hintsSection = buildSection("Hints", hints);
69
+ const body = assembleResponse(colorizeErrorStatus(`${RESPONSE_MARKERS.failure} Failed [${input.errorCode}]`), "Request could not be completed.", [errorSection, recoverySection, nextActionSection, reviewSection, hintsSection].filter((section) => section !== null));
29
70
  return input.addressCarrier ? `${renderAddressCarrier(input.addressCarrier)}\n${body}` : body;
30
71
  }
31
72
  export function buildHelpResponse(input) {
73
+ const payload = {};
32
74
  return {
33
75
  text: input.text,
34
- payload: buildPayload({}),
76
+ payload: buildPayload(payload),
35
77
  };
36
78
  }
37
79
  export function buildLogResponse(result) {
38
80
  const { address: _address, ...resultData } = result;
81
+ const payload = resultData;
39
82
  return prependAddressReceipt({
40
83
  text: result.markdown,
41
- payload: buildPayload(resultData),
84
+ payload: buildPayload(payload),
42
85
  }, result.address);
43
86
  }
@@ -1,16 +1,26 @@
1
+ import { commissionSlug } from "../../core/ids.js";
1
2
  import { shortCommitHash } from "../../git/commits.js";
2
3
  import { assembleResponse, buildPayload, buildSection, formatMaybe, formatWarnings, } from "./format.js";
3
4
  import { DIFF_OVERVIEW_SECTION_TITLE, RESPONSE_MARKERS, WARNING_SECTION_TITLE } from "./response-style.js";
4
- import { BRANCH_LABEL, INFO_BRANCH_MAX_CHARS, INFO_COMMIT_MAX_CHARS, INFO_PLEA_MAX_CHARS, } from "./shared.js";
5
+ import { INFO_COMMIT_MAX_CHARS, INFO_PLEA_MAX_CHARS, } from "./shared.js";
5
6
  import { prependAddressReceipt } from "./address.js";
7
+ function formatPetitionSealSource(source) {
8
+ return source.arc === null
9
+ ? `no open arc · fence ${source.fence}`
10
+ : `a${source.arc} · fence ${source.fence}▸HEAD`;
11
+ }
6
12
  export function buildPetitionPreviewResponse(preview, json) {
13
+ const source = formatPetitionSealSource(preview.arc.source);
14
+ const payload = {
15
+ ...preview,
16
+ arc: { source },
17
+ };
7
18
  const failed = preview.gates.filter((gate) => gate.result === "failed").length;
8
19
  const text = json
9
- ? JSON.stringify(preview)
20
+ ? JSON.stringify(payload)
10
21
  : [
11
- `contract ${preview.contract}`,
12
- `target ${preview.target}`,
13
- `arc ${preview.arc.source}`,
22
+ `contract ${commissionSlug(preview.contract)}`,
23
+ `arc ${source}`,
14
24
  ...preview.gates.map((gate) => {
15
25
  if (gate.result === "not_evaluated")
16
26
  return `gate ${gate.name} · not evaluated`;
@@ -22,35 +32,30 @@ export function buildPetitionPreviewResponse(preview, json) {
22
32
  ].join("\n");
23
33
  return {
24
34
  text,
25
- payload: preview,
35
+ payload,
26
36
  ...(failed > 0 ? { exitCode: 1 } : {}),
27
37
  };
28
38
  }
29
39
  export function buildPetitionClaimResponse(result, input) {
30
40
  const { status: _status, address: _address, ...resultData } = result;
41
+ const payload = resultData;
31
42
  const renderedCommit = result.commit ? shortCommitHash(result.commit) : undefined;
32
43
  const commitSuffix = renderedCommit ? ` [${renderedCommit}]` : "";
33
- const summary = result.deletedBranch
34
- ? `Merged '${result.deletedBranch}' into '${result.mergedInto}'${commitSuffix}. Deleted feature branch.`
35
- : `Merged contract into '${result.mergedInto}'${commitSuffix}.`;
44
+ const summary = `Claimed contract${commitSuffix}.`;
36
45
  const diffSection = result.diff ? buildSection(DIFF_OVERVIEW_SECTION_TITLE, result.diff) : null;
37
46
  const ledgerSection = result.ledgerView ? buildSection("Ledger", result.ledgerView) : null;
38
47
  const warningSection = buildSection(WARNING_SECTION_TITLE, formatWarnings(result.meta?.warnings ?? []));
39
48
  const infoLines = [
40
49
  ...formatMaybe("Result", result.result, INFO_COMMIT_MAX_CHARS),
41
- ...formatMaybe("Title", input.title, INFO_BRANCH_MAX_CHARS),
50
+ ...formatMaybe("Title", input.title),
42
51
  ...formatMaybe("Plea", input.plea, INFO_PLEA_MAX_CHARS),
43
52
  ...formatMaybe("Commit", renderedCommit, INFO_COMMIT_MAX_CHARS),
44
- ...formatMaybe("Merged Into", result.mergedInto, INFO_BRANCH_MAX_CHARS),
45
- ...formatMaybe("Deleted Branch", result.deletedBranch, INFO_BRANCH_MAX_CHARS),
46
- ...formatMaybe(BRANCH_LABEL, result.currentBranch, INFO_BRANCH_MAX_CHARS),
47
- ...formatMaybe("Base Branch", result.baseBranch, INFO_BRANCH_MAX_CHARS),
48
53
  "Claim Gates: settlement requirements satisfied.",
49
54
  ...formatMaybe("Oath", input.oath, INFO_PLEA_MAX_CHARS),
50
55
  ];
51
56
  const text = assembleResponse(`${RESPONSE_MARKERS.claim} Keiyaku Fulfilled (Claim)`, summary, [ledgerSection, diffSection, warningSection].filter((section) => section !== null), infoLines);
52
57
  return prependAddressReceipt({
53
58
  text,
54
- payload: buildPayload(resultData),
59
+ payload: buildPayload(payload),
55
60
  }, result.address);
56
61
  }
@@ -41,6 +41,9 @@ function compactTokens(value) {
41
41
  }
42
42
  function warningText(row) {
43
43
  const warning = row.warning;
44
+ if (warning.reason === "rate-limit") {
45
+ return `provider rate limit exhausted${row.count > 1 ? ` (${row.count})` : ""}`;
46
+ }
44
47
  if (warning.reason === "retry") {
45
48
  const status = warning.errorStatus === null ? "no response" : String(warning.errorStatus);
46
49
  return `provider retrying ${warning.attempt}/${warning.maxRetries} · retry in ${formatTimelineDuration(warning.retryDelayMs)} · status ${status}${row.count > 1 ? ` (${row.count})` : ""}`;
@@ -1,15 +1,16 @@
1
- import * as path from "node:path";
2
- import * as fs from "node:fs";
3
1
  import { KEIYAKU_FILE } from "../../keiyaku.js";
2
+ import { commissionSlug, formatCommissionCoordinate } from "../../core/ids.js";
4
3
  import { assembleResponse, buildSection, DISPLAY_TEXT_MAX_CHARS, FORMAT_LIST_MAX_ITEM_CHARS, FORMAT_LIST_MAX_ITEMS, formatMaybe, formatWarnings, shellQuote, truncateForDisplay, } from "./format.js";
5
4
  import { WARNING_SECTION_TITLE } from "./response-style.js";
6
5
  import { prependAddressReceipt } from "./address.js";
7
6
  export function textResponse(text) {
7
+ const payload = {};
8
8
  return {
9
9
  text,
10
- payload: {},
10
+ payload,
11
11
  };
12
12
  }
13
+ /** Carrier boundary: data must already be a named command payload assignable to JsonObject. */
13
14
  export function coreTextResponse(text, data) {
14
15
  return {
15
16
  text,
@@ -20,30 +21,61 @@ export async function renderCoreResult(work, render) {
20
21
  return render(await work);
21
22
  }
22
23
  export function buildBindDraftResponse(draftPath) {
23
- return coreTextResponse(`Draft saved to ${draftPath}.`, { draftPath });
24
+ const payload = { draftPath };
25
+ return coreTextResponse(`Draft saved to ${draftPath}.`, payload);
24
26
  }
25
27
  export function buildBindResponse(result, cwd) {
26
- const displayRoot = result.workspace === "worktree" && fs.existsSync(cwd) ? fs.realpathSync.native(cwd) : cwd;
27
- return coreTextResponse([
28
- `Bound ${result.id}.`,
29
- `workspace: ${result.workspace}`,
30
- `path: ${result.worktreePath}`,
31
- `ref: ${result.ref}`,
32
- ...(result.warnings.length > 0 ? ["Warnings:", ...result.warnings.map((warning) => `- ${warning}`)] : []),
33
- ...(result.workspace === "worktree" ? [`next: cd ${path.relative(displayRoot, result.worktreePath) || "."}`] : []),
34
- ].join("\n"), {
28
+ void cwd;
29
+ const overlapsByCounterparty = new Map();
30
+ for (const overlap of result.scopeOverlaps) {
31
+ const counterparty = overlap.contracts[1];
32
+ const group = overlapsByCounterparty.get(counterparty);
33
+ if (group)
34
+ group.push(overlap);
35
+ else
36
+ overlapsByCounterparty.set(counterparty, [overlap]);
37
+ }
38
+ const overlapWarnings = [...overlapsByCounterparty.entries()].map(([contractId, overlaps]) => renderScopeOverlapGroup(commissionSlug(contractId), overlaps));
39
+ const place = result.place;
40
+ const payload = {
35
41
  id: result.id,
36
42
  ref: result.ref,
37
43
  worktreePath: result.worktreePath,
38
44
  workspace: result.workspace,
45
+ place: result.place,
46
+ target: result.target,
39
47
  ledgerHead: result.ledgerHead,
40
- warnings: result.warnings,
48
+ warnings: overlapWarnings,
49
+ scopeOverlaps: result.scopeOverlaps,
41
50
  ...(result.branch ? { branch: result.branch } : {}),
51
+ };
52
+ return coreTextResponse([
53
+ `Bound${place ? ` @${place}` : ""} · commission ${result.id} · target ${result.target}`,
54
+ ...(overlapWarnings.length > 0 ? ["Warnings:", ...overlapWarnings] : []),
55
+ ].join("\n"), payload);
56
+ }
57
+ function renderScopeOverlapGroup(contractAddr, overlaps) {
58
+ const pairs = overlaps.map((overlap) => {
59
+ const [nextPattern, existingPattern] = overlap.patterns;
60
+ const evidence = overlap.kind === "potential"
61
+ ? "potential; no current tracked path proves it"
62
+ : `concrete; ${overlap.paths.count} current tracked path${overlap.paths.count === 1 ? "" : "s"}: ${overlap.paths.preview.join(", ")}${overlap.paths.truncated ? ", …" : ""}`;
63
+ return ` ${nextPattern} × ${existingPattern} (${evidence})`;
42
64
  });
65
+ return [
66
+ `▲ scope overlap with ${contractAddr}:`,
67
+ ...pairs,
68
+ ` consider --after ${contractAddr}`,
69
+ ].join("\n");
43
70
  }
44
71
  export function buildForfeitResponse(result) {
45
72
  const { address: _address, ...resultData } = result;
46
- const base = `Forfeited ${result.contractId}.`;
73
+ const payload = resultData;
74
+ const place = result.address.binding.kind === "commission"
75
+ ? (result.address.place?.display ?? null)
76
+ : null;
77
+ const coordinate = formatCommissionCoordinate({ commissionId: result.contractId, place });
78
+ const base = `Forfeited ${coordinate}.`;
47
79
  const warnings = [
48
80
  ...(result.cleanup.worktreeRemoveFailure
49
81
  ? [
@@ -52,18 +84,22 @@ export function buildForfeitResponse(result) {
52
84
  : []),
53
85
  ...(result.cleanup.branchDeleteFailure
54
86
  ? [
55
- `Could not delete contract branch ${result.cleanup.branchDeleteFailure.name}: ${result.cleanup.branchDeleteFailure.reason}`,
87
+ `Could not delete contract branch: ${redactBranchReference(result.cleanup.branchDeleteFailure.reason, result.cleanup.branchDeleteFailure.name)}`,
56
88
  ]
57
89
  : []),
58
90
  ];
59
91
  if (warnings.length === 0) {
60
- return prependAddressReceipt(coreTextResponse(base, resultData), result.address);
92
+ return prependAddressReceipt(coreTextResponse(base, payload), result.address);
61
93
  }
62
94
  const warningSection = buildSection(WARNING_SECTION_TITLE, formatWarnings(warnings));
63
- return prependAddressReceipt(coreTextResponse(assembleResponse(base, "", [warningSection].filter((section) => section !== null)), resultData), result.address);
95
+ return prependAddressReceipt(coreTextResponse(assembleResponse(base, "", [warningSection].filter((section) => section !== null)), payload), result.address);
96
+ }
97
+ function redactBranchReference(message, branch) {
98
+ return message
99
+ .replaceAll(`refs/heads/${branch}`, "contract branch")
100
+ .replaceAll(branch, "contract branch");
64
101
  }
65
102
  export const RULES_SECTION_MAX_CHARS = 5000;
66
- export const BRANCH_LABEL = "Workspace";
67
103
  export const INFO_SECTION_TITLE = "Info";
68
104
  export const HINTS_SECTION_TITLE = "Hints";
69
105
  export const REVIEW_SECTION_TITLE = "Review";
@@ -125,12 +161,15 @@ export function formatDurableReviveCommand(input) {
125
161
  ? `keiyaku revive ${input.artifactId} --repo ${shellQuote(input.repositoryCoordinate)}`
126
162
  : `keiyaku revive ${input.artifactId}`;
127
163
  }
164
+ export function formatTellCommand(projectionId, body) {
165
+ return `keiyaku tell ${projectionId} [--wait DURATION] ${body}`;
166
+ }
128
167
  export function projectionRejoinHintLines(projectionId) {
129
168
  return [
130
169
  "Come back for the result:",
131
170
  ` » keiyaku wait ${projectionId} [--timeout DURATION]`,
132
171
  "Steer it while it works:",
133
- ` » keiyaku tell ${projectionId} "your words"`,
172
+ ` » ${formatTellCommand(projectionId, '"your words"')}`,
134
173
  ];
135
174
  }
136
175
  export function formatCallNextAction(input) {
@@ -142,7 +181,7 @@ export function formatCallNextAction(input) {
142
181
  "The akuma is back.",
143
182
  ...(input.artifactId ? [`record: ${input.artifactId}`] : []),
144
183
  "talk to it:",
145
- ` » keiyaku tell ${input.projection.alias ?? input.projection.id} "your words"`,
184
+ ` » ${formatTellCommand(input.projection.alias ?? input.projection.id, '"your words"')}`,
146
185
  ].join("\n");
147
186
  }
148
187
  return "» keiyaku status";
@@ -0,0 +1,10 @@
1
+ /** Skills install success receipt: target rows plus optional warning block. */
2
+ export function renderSkillsInstallReceipt(result) {
3
+ return [
4
+ "Installed official Keiyaku skills:",
5
+ ...result.targets.map((target) => `- ${target.path} -> ${target.source} (${target.status})`),
6
+ ...(result.warnings.length > 0
7
+ ? ["", "## Warning", ...result.warnings.map((warning) => `- ${warning}`)]
8
+ : []),
9
+ ].join("\n");
10
+ }
@@ -3,6 +3,8 @@ import { shortCommitHash } from "../../git/commits.js";
3
3
  import { formatAgeMs } from "./format.js";
4
4
  import { renderKanshiBoard } from "./kanshi.js";
5
5
  import { renderProjectionActivitySummary, } from "./projection-activity.js";
6
+ import { renderTerminalFailureFace, terminalFailureCodeSuffix } from "./terminal-failure.js";
7
+ import { formatTellCommand } from "./shared.js";
6
8
  export const STATUS_DISEASES_SECTION_TITLE = "Diseases";
7
9
  export const STATUS_PROJECTION_SECTION_TITLE = "akuma";
8
10
  export const STATUS_CLAIM_RECEIPTS_SECTION_TITLE = "claim receipts";
@@ -42,13 +44,26 @@ function renderTaskStatusSection(tasks) {
42
44
  tasks.blocked > 0 ? `${tasks.blocked} blocked` : undefined,
43
45
  ].filter((segment) => segment !== undefined);
44
46
  const lines = [`tasks · ${segments.join(" / ")}`];
45
- lines.push(...tasks.topTasks.map((task) => ` ${task.id.slice(0, 9)} · P${task.pri} · ${task.state === "in_progress" ? "in_progress" : "ready"} · ${task.title}`));
47
+ lines.push(...tasks.topTasks.map((task) => ` ${task.id} · P${task.pri} · ${task.state === "in_progress" ? "in_progress" : "ready"} · ${task.title}`));
48
+ if (tasks.omitted > 0) {
49
+ lines.push(` · +${tasks.omitted} more · shown in_progress then ready; each by priority, created, id`);
50
+ }
46
51
  return `${lines.join("\n")}\n`;
47
52
  }
53
+ function projectionDiagnosticDetail(diagnostic) {
54
+ if (diagnostic.kind === "oversized-projection-file" && diagnostic.fileName) {
55
+ return `${diagnostic.fileName} exceeds status read limit · newest complete events retained`;
56
+ }
57
+ return diagnostic.detail ?? diagnostic.kind;
58
+ }
48
59
  function renderProjectionDiagnostics(row) {
49
- if (row.diagnostics.length === 0)
50
- return "";
51
- return ` · diag ${row.diagnostics.map((diagnostic) => diagnostic.detail).join("; ")}`;
60
+ const details = row.diagnostics.map(projectionDiagnosticDetail);
61
+ return details.length > 0 ? ` · diag ${details.join("; ")}` : "";
62
+ }
63
+ function terminalFailureSuffix(row) {
64
+ return row.terminalFailure
65
+ ? ` · ${terminalFailureCodeSuffix(row.terminalFailure.code)}`
66
+ : "";
52
67
  }
53
68
  function renderProjectionOmission(board) {
54
69
  if (!board.omitted)
@@ -61,14 +76,17 @@ function renderProjectionOmission(board) {
61
76
  return ` · +${board.omitted.total} not shown (${counts.join(" · ")}) — keiyaku status --all`;
62
77
  }
63
78
  function renderProjectionAge(row) {
79
+ const age = formatAgeMs(row.activityAgeMs);
64
80
  if (row.state === "done")
65
- return "done";
81
+ return age ? `done ${age}` : "done";
66
82
  if (row.state === "killed")
67
- return "killed";
83
+ return age ? `killed ${age}` : "killed";
68
84
  if (row.state === "dismissed")
69
- return "dismissed";
85
+ return age ? `dismissed ${age}` : "dismissed";
70
86
  if (row.state === "failed")
71
- return "failed";
87
+ return age ? `failed ${age}` : "failed";
88
+ if (row.state === "dead")
89
+ return age ? `dead ${age}` : "dead";
72
90
  if (row.state === "startup-timeout")
73
91
  return "startup timed out";
74
92
  if (row.state === "unknown")
@@ -117,18 +135,19 @@ function renderProjectionRow(row) {
117
135
  let heading;
118
136
  if (row.state === "startup-timeout" || row.state === "unknown") {
119
137
  const akuma = (row.akuma ?? "(unknown)").padEnd(18);
120
- heading = ` ${glyph} ${identity} ${akuma} ${renderProjectionAge(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
138
+ heading = ` ${glyph} ${identity} ${akuma} ${renderProjectionAge(row)}${terminalFailureSuffix(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
121
139
  }
122
140
  else if (row.state === "minting") {
123
- heading = ` ${glyph} ${identity.padEnd(18)} (minting)${renderProjectionDiagnostics(row)}`.trimEnd();
141
+ heading = ` ${glyph} ${identity.padEnd(18)} (minting)${terminalFailureSuffix(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
124
142
  }
125
143
  else {
126
144
  const age = renderProjectionAge(row).padEnd(9);
127
145
  const akuma = (row.akuma ?? "(unknown)").padEnd(18);
128
- heading = ` ${glyph} ${identity} ${akuma} ${age}${renderProjectionTellCounts(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
146
+ heading = ` ${glyph} ${identity} ${akuma} ${age}${renderProjectionTellCounts(row)}${terminalFailureSuffix(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
129
147
  }
130
148
  const activity = renderProjectionActivityLine(row);
131
- return activity ? [heading, activity] : [heading];
149
+ const drillDown = row.terminalFailure ? [` » keiyaku wait ${row.alias ?? row.id}`] : [];
150
+ return activity ? [heading, activity, ...drillDown] : [heading, ...drillDown];
132
151
  }
133
152
  function isTerminalProjectionStatus(row) {
134
153
  return row.state === "done"
@@ -143,14 +162,18 @@ export function renderProjectionFilteredStatus(board, projectionId) {
143
162
  const row = board.projections?.rows.find((candidate) => candidate.id === projectionId);
144
163
  if (!row)
145
164
  return `${heading}\nno projections on projection ${projectionId}\n`;
165
+ if (row.state === "failed" || row.terminalFailure) {
166
+ // Same face bytes as wait; CLI success write supplies the final newline.
167
+ return renderTerminalFailureFace(projectionId, row.terminalFailure).join("\n");
168
+ }
146
169
  const lines = [heading, "", ...renderProjectionRow(row), ""];
147
170
  if (!isTerminalProjectionStatus(row))
148
- lines.push(`» keiyaku tell ${projectionId} "..."`);
171
+ lines.push(`» ${formatTellCommand(projectionId, '"..."')}`);
149
172
  lines.push(`» keiyaku wait ${projectionId}`);
150
173
  return `${lines.join("\n")}\n`;
151
174
  }
152
175
  export function renderProjectionStatusSection(board) {
153
- if (!board || board.rows.length === 0)
176
+ if (!board || (board.rows.length === 0 && !board.omitted))
154
177
  return "";
155
178
  const lines = board.rows.flatMap(renderProjectionRow);
156
179
  const omission = renderProjectionOmission(board);