@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
@@ -0,0 +1,77 @@
1
+ import { repositoryCoordinateFromAddress } from "../commands/shared.js";
2
+ import { renderResolvedAddressReceipt } from "./address.js";
3
+ import { formatCallNextAction } from "./shared.js";
4
+ function responseText(response) {
5
+ return response.text;
6
+ }
7
+ function responseData(response) {
8
+ const payload = response.payload;
9
+ if (!payload || typeof payload !== "object" || Array.isArray(payload) || "error" in payload)
10
+ return {};
11
+ return payload;
12
+ }
13
+ function isCallProjectionFacts(value) {
14
+ if (value === null || typeof value !== "object" || Array.isArray(value))
15
+ return false;
16
+ const facts = value;
17
+ return typeof facts.id === "string"
18
+ && (facts.phase === "active" || facts.phase === "done" || facts.phase === "failed"
19
+ || facts.phase === "dismissed" || facts.phase === "lost" || facts.phase === "quiescent");
20
+ }
21
+ function conversationalFooter(response) {
22
+ const data = responseData(response);
23
+ const lines = [];
24
+ if (response.address) {
25
+ const projection = data.projection;
26
+ const projectionFacts = isCallProjectionFacts(projection) ? projection : undefined;
27
+ const receiptArtifactId = typeof data.revivedArtifactId === "string"
28
+ ? data.revivedArtifactId
29
+ : typeof data.artifactId === "string" ? data.artifactId : undefined;
30
+ lines.push(renderResolvedAddressReceipt(response.address, { artifactId: receiptArtifactId, projectionId: projectionFacts?.id }));
31
+ }
32
+ const responsePath = typeof data.responsePath === "string" ? data.responsePath : undefined;
33
+ const agent = typeof data.agent === "string" ? data.agent : undefined;
34
+ const meta = data.meta && typeof data.meta === "object" && !Array.isArray(data.meta) ? data.meta : undefined;
35
+ const warnings = Array.isArray(meta?.warnings) ? meta.warnings.filter((warning) => typeof warning === "string") : [];
36
+ if (responsePath) {
37
+ lines.push(`Saved to ${responsePath}.`);
38
+ }
39
+ if (agent) {
40
+ lines.push(`Akuma: ${agent}`);
41
+ }
42
+ if (warnings.length > 0) {
43
+ lines.push("Warnings:", ...warnings.map((warning) => `- ${warning}`));
44
+ }
45
+ if (typeof data.summary === "string") {
46
+ const projection = data.projection;
47
+ const projectionFacts = isCallProjectionFacts(projection) ? projection : undefined;
48
+ const artifactId = typeof data.artifactId === "string" ? data.artifactId : undefined;
49
+ lines.push(formatCallNextAction({
50
+ projection: projectionFacts,
51
+ artifactId,
52
+ repositoryCoordinate: repositoryCoordinateFromAddress(response.address),
53
+ }));
54
+ }
55
+ return lines.join("\n");
56
+ }
57
+ export function writeSuccessResponse(command, flags, response) {
58
+ // Plain tell is always silent; tell --wait owns the ordinary wait response.
59
+ if (command === "tell" && flags.timeoutMs === undefined)
60
+ return;
61
+ if (command === "bind" && flags.dryRun) {
62
+ // Canonical Markdown already ends with exactly one newline.
63
+ process.stdout.write(responseText(response));
64
+ return;
65
+ }
66
+ if (command !== "revive") {
67
+ process.stdout.write(`${responseText(response)}\n`);
68
+ return;
69
+ }
70
+ const data = responseData(response);
71
+ const answer = typeof data.summary === "string" ? data.summary.trim() : responseText(response).trim();
72
+ const footer = conversationalFooter(response);
73
+ process.stdout.write(`${answer}\n`);
74
+ if (footer) {
75
+ process.stderr.write(`${footer}\n`);
76
+ }
77
+ }
@@ -1,9 +1,6 @@
1
- export function shortTaskId(id) {
2
- return id.slice(0, 9);
3
- }
1
+ import { commissionSlug } from "../../core/ids.js";
4
2
  export function shortContractId(contractId) {
5
- const separator = contractId.indexOf(".");
6
- return separator > 0 ? contractId.slice(0, separator) : contractId;
3
+ return commissionSlug(contractId);
7
4
  }
8
5
  export function deriveTaskDisplay(task) {
9
6
  if (task.state === "done")
@@ -20,7 +17,7 @@ function renderTaskRowWithId(task, id) {
20
17
  return `${id} · P${task.pri} · ${deriveTaskDisplay(task)} · ${task.title}`;
21
18
  }
22
19
  export function renderTaskListRow(task) {
23
- return renderTaskRowWithId(task, shortTaskId(task.id));
20
+ return renderTaskRowWithId(task, task.id);
24
21
  }
25
22
  export function renderTaskMutationRow(task) {
26
23
  return renderTaskRowWithId(task, task.id);
@@ -67,27 +64,56 @@ export function renderTaskList(tasks, options = {}) {
67
64
  return groupAndSortTasks(tasks, options.selection).map(renderTaskListRow).join("\n");
68
65
  }
69
66
  function renderIds(ids) {
70
- return ids.length === 0 ? "(none)" : ids.map(shortTaskId).join(", ");
67
+ return ids.length === 0 ? "(none)" : ids.join(", ");
71
68
  }
72
- export function renderTaskShow(task) {
69
+ function renderTaskDetails(task) {
73
70
  const released = new Set(task.releasedNeeds);
74
71
  const needs = task.needs.length === 0
75
72
  ? "(none)"
76
73
  : task.needs
77
- .map((id) => `${shortTaskId(id)}${released.has(id) ? " (dropped)" : ""}`)
74
+ .map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`)
78
75
  .join(", ");
79
76
  return [
80
- `${shortTaskId(task.id)} · ${task.title}`,
77
+ `${task.id} · ${task.title}`,
81
78
  `state: ${deriveTaskDisplay(task)}`,
82
79
  `priority: ${task.pri}`,
83
80
  `needs: ${needs}`,
84
81
  `blocks: ${renderIds(task.blocks)}`,
85
- `parent: ${task.parent === null ? "(none)" : shortTaskId(task.parent)}`,
82
+ `parent: ${task.parent === null ? "(none)" : task.parent}`,
86
83
  `from: ${renderIds(task.from)}`,
87
84
  `created: ${task.createdAt} by ${task.creator}`,
88
85
  "",
89
86
  task.body,
90
- ].join("\n");
87
+ ];
88
+ }
89
+ function renderTreeNode(node, prefix, last) {
90
+ const branch = last ? "`- " : "|- ";
91
+ const nestedPrefix = `${prefix}${last ? " " : "| "}`;
92
+ const task = node.task;
93
+ const released = new Set(task.releasedNeeds);
94
+ const needs = task.needs.length === 0
95
+ ? "(none)"
96
+ : task.needs.map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`).join(", ");
97
+ const lines = [
98
+ `${prefix}${branch}${task.id} · ${deriveTaskDisplay(task)} · P${task.pri} · ${task.title}`,
99
+ `${nestedPrefix}needs: ${needs}`,
100
+ `${nestedPrefix}blocks: ${renderIds(task.blocks)}`,
101
+ `${nestedPrefix}from: ${renderIds(task.from)}`,
102
+ ];
103
+ node.children.forEach((child, index) => lines.push(...renderTreeNode(child, nestedPrefix, index === node.children.length - 1)));
104
+ return lines;
105
+ }
106
+ function treeDiagnostic(diagnostic) {
107
+ const id = diagnostic.id ? `${diagnostic.id} · ` : "";
108
+ return `! ${diagnostic.code}: ${id}${diagnostic.message}`;
109
+ }
110
+ export function renderTaskShow(task, tree) {
111
+ const treeLines = tree.descendants.length === 0
112
+ ? ["children tree: (empty)"]
113
+ : ["children tree:", ...tree.descendants.flatMap((node, index) => renderTreeNode(node, "", index === tree.descendants.length - 1))];
114
+ if (tree.diagnostics.length > 0)
115
+ treeLines.push("tree diagnostics:", ...tree.diagnostics.map(treeDiagnostic));
116
+ return [...renderTaskDetails(task), "", ...treeLines].join("\n");
91
117
  }
92
118
  export function taskJson(task) {
93
119
  return {
@@ -105,8 +131,22 @@ export function taskJson(task) {
105
131
  creator: task.creator,
106
132
  };
107
133
  }
108
- export function renderTaskShowJson(task) {
109
- return `${JSON.stringify({ ...taskJson(task), body: task.body })}\n`;
134
+ function taskTreeNodeJson(node) {
135
+ return { ...taskJson(node.task), children: node.children.map(taskTreeNodeJson) };
136
+ }
137
+ export function taskTreeJson(tree) {
138
+ return {
139
+ children: tree.descendants.map(taskTreeNodeJson),
140
+ diagnostics: tree.diagnostics.map((diagnostic) => ({
141
+ code: diagnostic.code,
142
+ message: diagnostic.message,
143
+ id: diagnostic.id ?? null,
144
+ relatedIds: [...(diagnostic.relatedIds ?? [])],
145
+ })),
146
+ };
147
+ }
148
+ export function renderTaskShowJson(task, tree) {
149
+ return `${JSON.stringify({ ...taskJson(task), body: task.body, descendants: taskTreeJson(tree) })}\n`;
110
150
  }
111
151
  export function renderTaskListJson(tasks, selection = "default") {
112
152
  return `${JSON.stringify(groupAndSortTasks(tasks, selection).map(taskJson))}\n`;
@@ -0,0 +1,5 @@
1
+ import { textResponse } from "./shared.js";
2
+ /** Accepted plain tell: zero stdout/stderr bytes. */
3
+ export function buildSilentTellResponse() {
4
+ return textResponse("");
5
+ }
@@ -0,0 +1,58 @@
1
+ /** Aggregate status strips the SUBAGENT_ prefix from the public code suffix. */
2
+ export function terminalFailureCodeSuffix(code) {
3
+ return code.startsWith("SUBAGENT_") ? code.slice("SUBAGENT_".length) : code;
4
+ }
5
+ export function terminalFailureColdStatement(failure) {
6
+ if (failure.publicCause?.kind === "rate_limit_exhausted") {
7
+ return "the provider rate limit is exhausted";
8
+ }
9
+ if (failure.class === "runner") {
10
+ return failure.code === "SUBAGENT_SPAWN_ERROR"
11
+ ? "the akuma never woke"
12
+ : "the akuma broke mid-flight";
13
+ }
14
+ switch (failure.code) {
15
+ case "SUBAGENT_TIMEOUT":
16
+ return "the akuma went silent";
17
+ case "SUBAGENT_PROVIDER_UNAVAILABLE":
18
+ return "the provider did not answer";
19
+ case "SUBAGENT_NON_ZERO_EXIT":
20
+ return failure.exitCode === undefined ? undefined : `the akuma died · exit ${failure.exitCode}`;
21
+ case "SUBAGENT_EXEC_ERROR":
22
+ return "the akuma broke mid-flight";
23
+ default:
24
+ return undefined;
25
+ }
26
+ }
27
+ function terminalFailureEvidence(failure, statement) {
28
+ const exitAlreadyInStatement = statement !== undefined
29
+ && failure.code === "SUBAGENT_NON_ZERO_EXIT"
30
+ && failure.exitCode !== undefined;
31
+ const parts = [];
32
+ if (failure.timeoutMs !== undefined)
33
+ parts.push(`idle ${failure.timeoutMs}ms`);
34
+ if (failure.signal)
35
+ parts.push(`signal ${failure.signal}`);
36
+ if (!exitAlreadyInStatement && failure.exitCode !== undefined)
37
+ parts.push(`exit ${failure.exitCode}`);
38
+ parts.push(`phase ${failure.phase}`);
39
+ return parts.length > 0 ? parts.join(" · ") : undefined;
40
+ }
41
+ /**
42
+ * Ordinary wait / single-projection status terminal face.
43
+ * Shape is the locked error-line stream: ✕ cold · CODE, then typed evidence.
44
+ */
45
+ export function renderTerminalFailureFace(_projectionId, failure) {
46
+ const lines = [];
47
+ if (failure) {
48
+ const statement = terminalFailureColdStatement(failure);
49
+ if (statement)
50
+ lines.push(`✕ ${statement} · ${failure.code}`);
51
+ const evidence = terminalFailureEvidence(failure, statement);
52
+ if (evidence)
53
+ lines.push(` ${evidence}`);
54
+ if (failure.artifactId)
55
+ lines.push(` record ${failure.artifactId}`);
56
+ }
57
+ return lines;
58
+ }
@@ -1 +1,2 @@
1
+ ;
1
2
  export {};
@@ -1,6 +1,7 @@
1
1
  import { formatTimelineDuration, renderProjectionActivity, } from "./projection-activity.js";
2
2
  import { displayColumns, resolveLineColumns, truncateColumns } from "./line-width.js";
3
- import { projectionRejoinHintLines, textResponse } from "./shared.js";
3
+ import { formatTellCommand, projectionRejoinHintLines, textResponse } from "./shared.js";
4
+ import { renderTerminalFailureFace } from "./terminal-failure.js";
4
5
  const WAIT_HEADER_MAX_COLUMNS = 60;
5
6
  function shortAddress(projectionId) {
6
7
  return /\/([0-9a-f]{8})$/.exec(projectionId)?.[1] ?? projectionId;
@@ -19,6 +20,11 @@ function phaseState(phase) {
19
20
  case "quiescent": return "running";
20
21
  }
21
22
  }
23
+ function isTerminalPhase(phase) {
24
+ // `lost` is intentionally absent: it can be an observation failure without
25
+ // a verdict. Core marks the excluded lost-runner-verdict case explicitly.
26
+ return ["done", "failed", "killed", "dismissed"].includes(phase);
27
+ }
22
28
  function waitStateText(snapshot) {
23
29
  switch (snapshot.phase) {
24
30
  case "active":
@@ -63,12 +69,6 @@ function waitHeader(projectionId, akuma, snapshot, outputColumns) {
63
69
  const dashCount = Math.max(1, maxColumns - displayColumns(`${prefix} ${state}`));
64
70
  return `${prefix} ${"─".repeat(dashCount)} ${state}`;
65
71
  }
66
- function terminalDiagnostic(result) {
67
- if (result.state !== "terminal" || result.verdict.state !== "failed")
68
- return undefined;
69
- const text = result.verdict.detail?.replace(/\s+/g, " ").trim();
70
- return text ? { at: result.verdict.at, text } : undefined;
71
- }
72
72
  function ledgerHint(projectionId, snapshot) {
73
73
  const file = snapshot.eventLedgerFile ?? "events.jsonl";
74
74
  return `· ledger: .keiyaku/projection/${projectionId}/${file}`;
@@ -91,33 +91,14 @@ function nextActionLines(projectionId, result, state) {
91
91
  "The akuma is back.",
92
92
  ...(snapshot.artifactId ? [`record: ${snapshot.artifactId}`] : []),
93
93
  "talk to it:",
94
- ` » keiyaku tell ${projectionId} "your words"`,
94
+ ` » ${formatTellCommand(projectionId, '"your words"')}`,
95
95
  ];
96
96
  }
97
- // Typed distinction: lost-runner → dead, failed → failed, observation lost → lost.
98
- if (result.state === "terminal" && result.verdict.state === "lost-runner") {
99
- return snapshot.artifactId
100
- ? ["The akuma dropped dead mid-turn.", ...recordReviveLines(snapshot.artifactId)]
101
- : ["The akuma dropped dead mid-turn.", "no record survived — there is nothing to revive."];
102
- }
103
- if (result.state === "terminal" && result.verdict.state === "failed") {
104
- if (snapshot.failureCode === "SUBAGENT_PROVIDER_UNAVAILABLE") {
105
- return snapshot.sourceArtifactId
106
- ? [
107
- "× provider unavailable — no turn accepted",
108
- `» nothing was consumed · retry from the same artifact: keiyaku revive ${snapshot.sourceArtifactId}`,
109
- ]
110
- : [
111
- "× provider unavailable — no turn accepted",
112
- "» nothing was consumed · safe to call again",
113
- ];
114
- }
115
- return [
116
- "The akuma failed and stopped.",
117
- ...(snapshot.artifactId ? [`record: ${snapshot.artifactId}`] : []),
118
- "talk to it:",
119
- ` » keiyaku tell ${projectionId} "your words"`,
120
- ];
97
+ if (result.state === "terminal" && (result.verdict.terminalFailure
98
+ || result.verdict.state === "failed"
99
+ || result.verdict.state === "launch-failed"
100
+ || result.verdict.state === "lost-runner")) {
101
+ return renderTerminalFailureFace(projectionId, result.verdict.terminalFailure);
121
102
  }
122
103
  if (snapshot.phase === "lost") {
123
104
  return snapshot.artifactId
@@ -142,7 +123,7 @@ function startupTimeoutResponse(projectionId, akuma) {
142
123
  "the launch was never adopted — no akuma exists",
143
124
  "",
144
125
  "the next wake retries the launch:",
145
- ` » keiyaku tell ${projectionId} "your words"`,
126
+ ` » ${formatTellCommand(projectionId, '"your words"')}`,
146
127
  ].join("\n")),
147
128
  exitCode: 1,
148
129
  };
@@ -198,7 +179,13 @@ export function buildWaitResponse(projectionId, result, options = {}) {
198
179
  if (result.state === "unknown") {
199
180
  return identityUnavailableResponse(projectionId, result.diagnostic);
200
181
  }
201
- const snapshot = result.snapshot;
182
+ // A plural deadline may observe a verdict whose timestamp is at or after
183
+ // the cutoff. The typed timeout result owns presentation in that case; the
184
+ // later terminal-looking snapshot must not turn it into a terminal face.
185
+ const snapshot = result.state === "timeout"
186
+ && (result.terminalSnapshot || isTerminalPhase(result.snapshot.phase))
187
+ ? { ...result.snapshot, phase: "active" }
188
+ : result.snapshot;
202
189
  const maxColumns = options.maxColumns ?? resolveLineColumns();
203
190
  if (result.state === "terminal" && result.verdict.state === "killed") {
204
191
  return {
@@ -216,13 +203,11 @@ export function buildWaitResponse(projectionId, result, options = {}) {
216
203
  if (state === "returned") {
217
204
  return returnedResponse(projectionId, result);
218
205
  }
219
- const diagnostic = terminalDiagnostic(result);
220
206
  const rendered = renderProjectionActivity(snapshot.activity, {
221
207
  anchorMs: snapshot.observedAtMs,
222
208
  state,
223
209
  showPlan: state === "running",
224
210
  ...(snapshot.workspaceRoot ? { workspaceRoot: snapshot.workspaceRoot } : {}),
225
- ...(diagnostic ? { terminalDiagnostic: diagnostic } : {}),
226
211
  maxColumns,
227
212
  });
228
213
  const storageDiagnostics = (snapshot.eventDiagnostics ?? []).map((diagnostic) => `‼ ${diagnostic.kind} · ${diagnostic.detail}`);
@@ -247,3 +232,20 @@ export function buildWaitResponse(projectionId, result, options = {}) {
247
232
  }
248
233
  return response;
249
234
  }
235
+ /** The plural face labels every member because a successful singleton face is answer-only. */
236
+ export function buildProjectionWaitSetResponse(result) {
237
+ const rendered = result.results
238
+ .slice()
239
+ .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
240
+ .map((member) => ({ id: member.id, response: buildWaitResponse(member.id, member.result) }));
241
+ const text = rendered.map(({ id, response }) => `${id}\n${response.text}`).join("\n\n");
242
+ const selectionSucceeded = result.selection === "all"
243
+ ? result.results.every((member) => member.result.state === "terminal" && member.result.verdict.state === "completed")
244
+ : result.selectedId !== undefined;
245
+ const exitCode = rendered.reduce((current, { response }) => {
246
+ if (response.exitCode === 130)
247
+ return 130;
248
+ return response.exitCode === 1 ? 1 : current;
249
+ }, selectionSucceeded ? undefined : 1);
250
+ return exitCode === undefined ? textResponse(text) : { ...textResponse(text), exitCode };
251
+ }
@@ -1 +1 @@
1
- export const TASK_SUBCOMMANDS = ["add", "view", "ls", "start", "stop", "done", "drop", "update", "doctor"];
1
+ export const TASK_SUBCOMMANDS = ["add", "show", "ls", "start", "stop", "done", "drop", "update", "doctor"];
@@ -200,11 +200,6 @@ export async function loadAkumaCatalog(cwd) {
200
200
  profile: freezeProfile({
201
201
  provider: profile.provider,
202
202
  ...(profile.description !== undefined ? { description: profile.description } : {}),
203
- ...("model" in profile && profile.model !== undefined ? { model: profile.model } : {}),
204
- ...("effort" in profile && profile.effort !== undefined ? { effort: profile.effort } : {}),
205
- ...("effortOptions" in profile && profile.effortOptions !== undefined
206
- ? { effortOptions: Object.freeze([...profile.effortOptions]) }
207
- : {}),
208
203
  }),
209
204
  });
210
205
  candidates.push(candidate);
@@ -25,7 +25,7 @@ export const ENV_KEYS = {
25
25
  const ENV_KEY_ENTRIES = [
26
26
  { key: ENV_KEYS.AGENT_OVERRIDE, defaultValue: "", description: "Override default agent name", category: "subagent" },
27
27
  { key: ENV_KEYS.DISABLE_SUBAGENT_SPAWN, defaultValue: "0", description: "Disable subprocess subagent spawning when set to 1", category: "subagent" },
28
- { key: ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS, defaultValue: "600000", description: "Subagent idle timeout in milliseconds", category: "subagent" },
28
+ { key: ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS, defaultValue: "300000", description: "Subagent provider-ingress idle timeout in milliseconds", category: "subagent" },
29
29
  { key: ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Codex runtime output/diagnostics capture cap", category: "subagent" },
30
30
  { key: ENV_KEYS.CLAUDE_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Claude runtime output/diagnostics capture cap", category: "subagent" },
31
31
  { key: ENV_KEYS.OPENCODE_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "OpenCode runtime output/diagnostics capture cap", category: "subagent" },
@@ -1,39 +1,10 @@
1
+ import { getProviderRegistration } from "../agents/providers/registry.js";
1
2
  const EMPTY_OWNERSHIP = Object.freeze({
2
3
  access: Object.freeze([]),
3
4
  network: Object.freeze([]),
4
5
  webSearch: Object.freeze([]),
5
6
  });
6
- function owns(config, key) {
7
- return Object.prototype.hasOwnProperty.call(config, key);
8
- }
9
7
  export function nativePolicyOwnership(instanceName, instance) {
10
- if (instance.kind !== "codex-sdk" && instance.kind !== "codex-app-server") {
11
- return EMPTY_OWNERSHIP;
12
- }
13
- const config = instance.config;
14
- if (!config)
15
- return EMPTY_OWNERSHIP;
16
- const access = [];
17
- const network = [];
18
- const webSearch = [];
19
- const add = (target, key) => {
20
- target.push({ path: `providers.${instanceName}.config.${key}`, value: config[key] });
21
- };
22
- if (owns(config, "sandbox_mode"))
23
- add(access, "sandbox_mode");
24
- if (owns(config, "approval_policy"))
25
- add(access, "approval_policy");
26
- for (const key of Object.keys(config)) {
27
- if (key !== "sandbox_workspace_write" && !key.startsWith("sandbox_workspace_write."))
28
- continue;
29
- add(access, key);
30
- add(network, key);
31
- }
32
- if (owns(config, "web_search"))
33
- add(webSearch, "web_search");
34
- return Object.freeze({
35
- access: Object.freeze(access),
36
- network: Object.freeze(network),
37
- webSearch: Object.freeze(webSearch),
38
- });
8
+ const owned = getProviderRegistration(instance.kind).nativePolicyOwnership(instanceName, instance);
9
+ return owned ?? EMPTY_OWNERSHIP;
39
10
  }
@@ -21,6 +21,13 @@ export function assertSettingsKnobUsable(loaded, knob) {
21
21
  if (hits.length === 0)
22
22
  return;
23
23
  throw new FlowError("INVALID_SETTINGS", `Invalid settings in ${SETTINGS_FILE} (diseased knob '${knob}').`, {
24
- hints: formatSettingsDiseases(hits),
24
+ facts: {
25
+ kind: "invalid_settings",
26
+ diseases: hits.map((disease) => ({
27
+ source: disease.source,
28
+ ...(disease.knob !== undefined ? { knob: disease.knob } : {}),
29
+ reason: disease.reason,
30
+ })),
31
+ },
25
32
  });
26
33
  }
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ import { getProviderRegistration } from "../../agents/providers/registry.js";
3
4
  import { isErrnoException } from "../../errno.js";
4
5
  import { SETTINGS_FILE } from "../../keiyaku.js";
5
6
  import { getKeiyakuHome } from "../env.js";
@@ -85,10 +86,17 @@ function collectProviderCandidates(source, rawProviders) {
85
86
  continue;
86
87
  }
87
88
  const instance = instanceResult.data;
88
- if (instance.kind === "codex-app-server" && instance.writableRoots !== undefined && source === "global") {
89
+ const registration = getProviderRegistration(instance.kind);
90
+ const writableRoots = registration.rejectsGlobalWritableRoots &&
91
+ instance &&
92
+ typeof instance === "object" &&
93
+ "writableRoots" in instance
94
+ ? instance.writableRoots
95
+ : undefined;
96
+ if (writableRoots !== undefined && source === "global") {
89
97
  let relativeIndex;
90
- for (let i = 0; i < instance.writableRoots.length; i++) {
91
- if (!path.posix.isAbsolute(instance.writableRoots[i])) {
98
+ for (let i = 0; i < writableRoots.length; i++) {
99
+ if (!path.posix.isAbsolute(writableRoots[i])) {
92
100
  relativeIndex = i;
93
101
  break;
94
102
  }
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ // Local leaf copy of the Claude setting-source vocabulary so this module never
3
+ // imports keiyaku.js (which re-exports the provider registry and would cycle).
4
+ const CLAUDE_SETTING_SOURCES = ["project", "user", "local"];
5
+ export const tomlValueSchema = z.lazy(() => z.union([
6
+ z.string(),
7
+ z.number(),
8
+ z.boolean(),
9
+ z.array(tomlValueSchema),
10
+ z.record(z.string(), tomlValueSchema),
11
+ ]));
12
+ export const structuredTomlValueSchema = z.record(z.string(), tomlValueSchema);
13
+ export const executableSchema = z.string().superRefine((value, ctx) => {
14
+ if (value.length === 0 || /\s/.test(value) || value.includes("\"") || value.includes("'")) {
15
+ ctx.addIssue({
16
+ code: z.ZodIssueCode.custom,
17
+ message: "must be a single program name token with no spaces or quotes.",
18
+ });
19
+ }
20
+ });
21
+ export const nonblankOptionalString = z
22
+ .string()
23
+ .refine((value) => value.trim().length > 0, { message: "must not be blank." })
24
+ .optional();
25
+ export const providerCommonFields = {
26
+ description: nonblankOptionalString,
27
+ };
28
+ export const providerEnvSchema = z.record(z.string(), z.string()).optional();
29
+ export const FORBIDDEN_CONFIG_KEYS = ["model", "model_reasoning_effort"];
30
+ export function isConfigKeyForbidden(key) {
31
+ return FORBIDDEN_CONFIG_KEYS.includes(key);
32
+ }
33
+ export const claudeSettingSourceSchema = z.enum(CLAUDE_SETTING_SOURCES);
34
+ export const writableRootItemSchema = z.string().superRefine((value, ctx) => {
35
+ if (value.length === 0) {
36
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not be empty." });
37
+ return;
38
+ }
39
+ if (value.startsWith("~")) {
40
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not begin with tilde." });
41
+ return;
42
+ }
43
+ if (value.includes("\0")) {
44
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain NUL byte." });
45
+ return;
46
+ }
47
+ if (/\$[A-Za-z_]/.test(value) || /\$\{/.test(value)) {
48
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain environment variable references." });
49
+ return;
50
+ }
51
+ });