@astrosheep/keiyaku 2.9.8 → 2.9.10

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 (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +10 -7
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +242 -79
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +65 -29
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +13 -35
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +35 -337
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +125 -127
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -1,11 +1,31 @@
1
1
  import { compactText } from "./compact-text.js";
2
- import { displayColumns, fitVariableLine, resolveLineColumns, truncateMiddleColumns } from "./line-width.js";
2
+ import { displayColumns, fitVariableLine, resolveLineColumns, truncateMiddleColumns, truncateColumns } from "./line-width.js";
3
3
  import { presentFoldedToolActivity, } from "./tool-presentation.js";
4
4
  import { presentLedgerPath, summarizeToolLedger } from "./tool-ledger-rollup.js";
5
5
  const TIME_GUTTER_WIDTH = 5;
6
6
  const VERB_WIDTH = 7;
7
7
  const SILENCE_THRESHOLD_MS = 60_000;
8
8
  const TIMELINE_GAP_MARKER = "⋮";
9
+ /** Keep the renderer and its gap grammar while giving Kanshi its smaller live window. */
10
+ export function selectProjectionActivityWindow(activity, ordinaryRows) {
11
+ // A Kanshi flow narrows only the settled history window. Unfinished activity
12
+ // remains pinned in its timeline position; it is not another history row.
13
+ const selected = activity.window.slice(-ordinaryRows);
14
+ const selectedOrders = new Set([
15
+ ...selected.map((row) => row.order),
16
+ ...activity.pinned.map((row) => row.order),
17
+ ]);
18
+ return {
19
+ activity: {
20
+ ...activity,
21
+ window: selected,
22
+ // Turn seals are a wait concern. Kanshi's narrow operational flow contains
23
+ // only ordinary activity plus durable tells.
24
+ timeline: activity.timeline.filter((row) => selectedOrders.has(row.order)),
25
+ },
26
+ leadingOmittedCount: activity.omittedSettledCount + activity.window.length - selected.length,
27
+ };
28
+ }
9
29
  export function formatTimelineDuration(ms) {
10
30
  const totalSeconds = Math.max(0, Math.floor(ms / 1_000));
11
31
  if (totalSeconds < 60)
@@ -40,6 +60,14 @@ export function formatWallClock(at) {
40
60
  return " : │";
41
61
  return `${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}│`;
42
62
  }
63
+ /** One durable-tell line dialect shared by wait and the embedded Kanshi flow. */
64
+ export function renderPendingProjectionTell(tell, input) {
65
+ const createdAtMs = Date.parse(tell.createdAt);
66
+ const age = Math.max(0, input.anchorMs - (Number.isFinite(createdAtMs) ? createdAtMs : input.anchorMs));
67
+ const fixed = `${formatWallClock(tell.createdAt)} ${"told".padEnd(VERB_WIDTH)} `;
68
+ const tail = ` — ${formatTimelineDuration(age)}…`;
69
+ return `${fixed}${truncateColumns(`“${tell.text}”`, Math.max(0, input.maxColumns - displayColumns(fixed) - displayColumns(tail)))}${input.renderUnconsumedTail?.(tail) ?? tail}`;
70
+ }
43
71
  function commonPrefix(left, right) {
44
72
  const a = [...left];
45
73
  const b = [...right];
@@ -73,8 +101,8 @@ function compressedPrefix(prefix) {
73
101
  function timelineGutter(label, spine) {
74
102
  return label || `${"".padStart(TIME_GUTTER_WIDTH)}${spine}`;
75
103
  }
76
- function renderTimelineGap(omittedEventCount, maxColumns) {
77
- return fitVariableLine(`${timelineGutter("", TIMELINE_GAP_MARKER)} `, `${omittedEventCount} more`, maxColumns);
104
+ function renderTimelineGap(omittedRowCount, maxColumns) {
105
+ return fitVariableLine(`${timelineGutter("", TIMELINE_GAP_MARKER)} `, `${omittedRowCount} more`, maxColumns);
78
106
  }
79
107
  function compactTokens(value) {
80
108
  if (value < 1_000)
@@ -148,13 +176,15 @@ function renderTerminalRollup(activity, input) {
148
176
  ? fitVariableLine(`${endPrefix} `, segments.join(" · "), input.maxColumns)
149
177
  : endPrefix,
150
178
  });
151
- for (const file of summary.files) {
152
- const repeat = file.count > 1 ? ` ×${file.count}` : "";
153
- const diffstat = file.diffstat ? ` +${file.diffstat.additions} −${file.diffstat.deletions}` : "";
154
- rows.push({ kind: "file", line: fitVariableLine("", `${file.path}${repeat}${diffstat}`, input.maxColumns) });
155
- }
156
- if (summary.hiddenFileCount > 0) {
157
- rows.push({ kind: "file", line: fitVariableLine("", `… ${summary.hiddenFileCount} more files`, input.maxColumns) });
179
+ if (input.includeFiles !== false) {
180
+ for (const file of summary.files) {
181
+ const repeat = file.count > 1 ? ` ×${file.count}` : "";
182
+ const diffstat = file.diffstat ? ` +${file.diffstat.additions} −${file.diffstat.deletions}` : "";
183
+ rows.push({ kind: "file", line: fitVariableLine("", `${file.path}${repeat}${diffstat}`, input.maxColumns) });
184
+ }
185
+ if (summary.hiddenFileCount > 0) {
186
+ rows.push({ kind: "file", line: fitVariableLine("", `… ${summary.hiddenFileCount} more files`, input.maxColumns) });
187
+ }
158
188
  }
159
189
  return { rows, hasMutationFiles: summary.files.length > 0 };
160
190
  }
@@ -174,33 +204,37 @@ function timelineEntries(activity) {
174
204
  export function renderProjectionActivity(activity, input) {
175
205
  const lines = [];
176
206
  const rowKinds = [];
207
+ const rowAts = [];
177
208
  let priorAtMs;
178
- let priorOrder;
179
209
  let priorMinute;
180
210
  let priorCompletedRan;
181
211
  let hasEndAnchor = false;
182
212
  let lastRunningTool;
213
+ const interrupted = input.state === "dead" || input.state === "lost" || input.state === "dismissed";
183
214
  const maxColumns = input.maxColumns ?? resolveLineColumns();
184
215
  const entries = timelineEntries(activity);
185
- const pushRow = (line, kind) => {
216
+ const gapBeforeOrder = input.leadingOmittedCount ? activity.window[0]?.order : undefined;
217
+ let gapEmitted = false;
218
+ const pushRow = (line, kind, at) => {
186
219
  lines.push(line);
187
220
  rowKinds.push(kind);
221
+ rowAts.push(at);
188
222
  return true;
189
223
  };
190
224
  for (let index = 0; index < entries.length; index += 1) {
191
225
  const entry = entries[index];
192
226
  const atMs = Date.parse(entry.at);
193
- if (priorOrder !== undefined && entry.order > priorOrder + 1) {
194
- pushRow(renderTimelineGap(entry.order - priorOrder - 1, maxColumns), "activity");
195
- }
196
- priorOrder = entry.order;
197
227
  if (entry.kind === "clock") {
198
228
  if (Number.isFinite(atMs))
199
229
  priorAtMs = atMs;
200
230
  continue;
201
231
  }
202
- if (priorAtMs !== undefined && Number.isFinite(atMs) && atMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
203
- pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(atMs - priorAtMs)} pass in silence`, maxColumns), "silence");
232
+ if (!gapEmitted && input.leadingOmittedCount && gapBeforeOrder !== undefined && entry.row.order >= gapBeforeOrder) {
233
+ pushRow(renderTimelineGap(input.leadingOmittedCount, maxColumns), "activity", entry.at);
234
+ gapEmitted = true;
235
+ }
236
+ if (input.showSilence !== false && priorAtMs !== undefined && Number.isFinite(atMs) && atMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
237
+ pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(atMs - priorAtMs)} pass in silence`, maxColumns), "silence", entry.at);
204
238
  }
205
239
  if (Number.isFinite(atMs))
206
240
  priorAtMs = atMs;
@@ -211,14 +245,14 @@ export function renderProjectionActivity(activity, input) {
211
245
  priorMinute = minute;
212
246
  const spine = row.kind === "turn"
213
247
  ? "├"
214
- : input.state === "dead" || input.state === "lost"
248
+ : interrupted
215
249
  ? "┆"
216
250
  : "│";
217
251
  if (row.kind === "ran") {
218
252
  const duration = formatActionDuration(row.durationMs);
219
253
  const activityState = row.state === "completed"
220
254
  ? "completed"
221
- : input.state === "dead" || input.state === "lost"
255
+ : interrupted
222
256
  ? "interrupted"
223
257
  : input.state === "stalled" || input.state === "unknown"
224
258
  ? "unresolved"
@@ -235,22 +269,22 @@ export function renderProjectionActivity(activity, input) {
235
269
  }
236
270
  priorCompletedRan = row.state === "completed" ? presentation.variableText : undefined;
237
271
  const budget = Math.max(0, maxColumns - displayColumns(fixed) - displayColumns(presentation.fixedSuffix));
238
- pushRow(`${fixed}${truncateMiddleColumns(variable, budget)}${presentation.fixedSuffix}`, "activity");
272
+ pushRow(`${fixed}${truncateMiddleColumns(variable, budget)}${presentation.fixedSuffix}`, "activity", entry.at);
239
273
  continue;
240
274
  }
241
275
  if (row.kind === "said") {
242
276
  priorCompletedRan = undefined;
243
- pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"said".padEnd(VERB_WIDTH)} `, `“${compactText(row.text)}”`, maxColumns), "activity");
277
+ pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"said".padEnd(VERB_WIDTH)} `, `“${compactText(row.text)}”`, maxColumns), "activity", entry.at);
244
278
  }
245
279
  else if (row.kind === "thought") {
246
280
  priorCompletedRan = undefined;
247
281
  const text = `${compactText(row.text)}${row.truncated && !row.text.endsWith("…") ? "…" : ""}`;
248
282
  const duration = formatActionDuration(row.durationMs);
249
- pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"thought".padEnd(VERB_WIDTH)} `, `${duration ? `${duration} — ` : ""}“${text}”`, maxColumns), "activity");
283
+ pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"thought".padEnd(VERB_WIDTH)} `, `${duration ? `${duration} — ` : ""}“${text}”`, maxColumns), "activity", entry.at);
250
284
  }
251
285
  else if (row.kind === "warning") {
252
286
  priorCompletedRan = undefined;
253
- pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"warning".padEnd(VERB_WIDTH)} `, warningText(row), maxColumns), "activity");
287
+ pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"warning".padEnd(VERB_WIDTH)} `, warningText(row), maxColumns), "activity", entry.at);
254
288
  }
255
289
  else {
256
290
  priorCompletedRan = undefined;
@@ -258,7 +292,7 @@ export function renderProjectionActivity(activity, input) {
258
292
  ? ` · ${compactTokens(row.inputTokens)}▸${compactTokens(row.outputTokens)} tok`
259
293
  : "";
260
294
  const cost = row.costUsd !== undefined ? ` · $${row.costUsd.toFixed(2)}` : "";
261
- pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}├ `, `turn ${row.turn}${usage}${cost}`, maxColumns), "activity");
295
+ pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}├ `, `turn ${row.turn}${usage}${cost}`, maxColumns), "activity", entry.at);
262
296
  }
263
297
  }
264
298
  for (const row of activity.pinned) {
@@ -267,10 +301,10 @@ export function renderProjectionActivity(activity, input) {
267
301
  }
268
302
  if (input.showPlan && activity.plan) {
269
303
  const planAtMs = Date.parse(activity.plan.at);
270
- if (priorAtMs !== undefined && Number.isFinite(planAtMs) && planAtMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
271
- pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(planAtMs - priorAtMs)} pass in silence`, maxColumns), "silence");
304
+ if (input.showSilence !== false && priorAtMs !== undefined && Number.isFinite(planAtMs) && planAtMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
305
+ pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(planAtMs - priorAtMs)} pass in silence`, maxColumns), "silence", activity.plan.at);
272
306
  }
273
- pushRow(renderPlan(activity.plan, maxColumns), "plan");
307
+ pushRow(renderPlan(activity.plan, maxColumns), "plan", activity.plan.at);
274
308
  }
275
309
  let hasMutationFiles = false;
276
310
  if (["returned", "dead", "lost", "dismissed"].includes(input.state)) {
@@ -278,10 +312,11 @@ export function renderProjectionActivity(activity, input) {
278
312
  anchorMs: input.anchorMs,
279
313
  ...(input.workspaceRoot ? { workspaceRoot: input.workspaceRoot } : {}),
280
314
  ...(input.terminalDiagnostic ? { diagnostic: input.terminalDiagnostic } : {}),
315
+ includeFiles: input.showTerminalFiles !== false,
281
316
  maxColumns,
282
317
  });
283
318
  for (const row of rollup.rows) {
284
- pushRow(row.line, row.kind);
319
+ pushRow(row.line, row.kind, new Date(input.anchorMs).toISOString());
285
320
  }
286
321
  hasEndAnchor = true;
287
322
  hasMutationFiles = rollup.hasMutationFiles;
@@ -289,6 +324,7 @@ export function renderProjectionActivity(activity, input) {
289
324
  return {
290
325
  lines,
291
326
  rowKinds,
327
+ rowAts,
292
328
  ...(lastRunningTool ? { lastRunningTool } : {}),
293
329
  hasEndAnchor,
294
330
  hasMutationFiles,
@@ -0,0 +1,64 @@
1
+ import { renderProjectionActivity } from "./projection-activity.js";
2
+ function fact(value) {
3
+ return value ?? "unavailable";
4
+ }
5
+ function consequenceLines(run) {
6
+ const consequences = run.consequences;
7
+ if (consequences === null)
8
+ return [];
9
+ if (consequences.retention === "unavailable") {
10
+ return [`consequences: unavailable · ${consequences.reason}`];
11
+ }
12
+ const retained = (name, value, detail) => value.retention === "retained" ? `${name}: retained · ${detail}` : `${name}: unavailable · ${value.reason}`;
13
+ return [
14
+ `review: unavailable · ${consequences.review.reason}`,
15
+ retained("petition", consequences.petition, consequences.petition.retention === "retained" ? `${consequences.petition.intent} · ${consequences.petition.objectId}` : ""),
16
+ retained("verdict", consequences.verdict, consequences.verdict.retention === "retained" ? `${consequences.verdict.result} · ${consequences.verdict.objectId}` : ""),
17
+ retained("claim", consequences.claim, consequences.claim.retention === "retained" ? `${consequences.claim.objectId} · ${consequences.claim.mergeCommit}` : ""),
18
+ retained("landed commit", consequences.landedCommit, consequences.landedCommit.retention === "retained" ? consequences.landedCommit.commit : ""),
19
+ ];
20
+ }
21
+ function runLines(run) {
22
+ return [
23
+ `${run.id}${run.alias ? ` (${run.alias})` : ""} · ${run.outcome}`,
24
+ `akuma: ${fact(run.akuma)} · provider: ${fact(run.provider)}${run.pid === null ? "" : ` · pid: ${run.pid}`} · role: ${run.role}`,
25
+ `contract: ${fact(run.contractId)}`,
26
+ `created: ${fact(run.mintedAt)}${run.terminalAt ? ` · terminal: ${run.terminalAt}` : ""}`,
27
+ ...(run.generation ? [`generation: ${run.generation.executionId} · created: ${run.generation.createdAt}${run.generation.adoptedAt ? ` · adopted: ${run.generation.adoptedAt}` : ""}`] : []),
28
+ `activity: ${run.activityRetention} · ${run.activityCount} retained${run.activityRange ? ` · ${run.activityRange.first}-${run.activityRange.last}` : ""}`,
29
+ ...(run.telemetry ? [`telemetry: ${run.telemetry.inputTokens} in · ${run.telemetry.outputTokens} out${run.telemetry.cachedInputTokens !== undefined ? ` · ${run.telemetry.cachedInputTokens} cached` : ""}${run.telemetry.costUsd !== undefined ? ` · $${run.telemetry.costUsd.toFixed(2)}` : ""}`] : []),
30
+ ...(run.artifact ? [`artifact: ${fact(run.artifact.id)} · ${run.artifact.retention}${run.artifact.path ? ` · path: ${run.artifact.path}` : ""}`] : []),
31
+ ...consequenceLines(run),
32
+ ...run.diagnostics.map((diagnostic) => `diagnostic: ${diagnostic}`),
33
+ ];
34
+ }
35
+ export function renderProjectionHistoryList(runs) {
36
+ return runs.flatMap((run, index) => index === 0 ? runLines(run) : ["", ...runLines(run)]).join("\n");
37
+ }
38
+ export function renderProjectionHistoryDetail(detail) {
39
+ const lines = runLines(detail);
40
+ if (!detail.activity || detail.activity.retainedTimeline.length === 0)
41
+ return lines.join("\n");
42
+ const state = detail.outcome === "done" ? "returned"
43
+ : detail.outcome === "out" ? "running"
44
+ : detail.outcome === "failed" || detail.outcome === "dead" ? "dead"
45
+ : detail.outcome === "lost" ? "lost"
46
+ : detail.outcome === "killed" || detail.outcome === "dismissed" ? "dismissed"
47
+ : detail.outcome === "startup-timeout" ? "stalled"
48
+ : "unknown";
49
+ const activity = renderProjectionActivity({
50
+ ...detail.activity,
51
+ timeline: detail.activity.retainedTimeline,
52
+ omittedSettledCount: 0,
53
+ }, {
54
+ anchorMs: Date.now(),
55
+ state,
56
+ showPlan: false,
57
+ showSilence: false,
58
+ showTerminalFiles: false,
59
+ });
60
+ return [...lines, "", ...activity.lines].join("\n");
61
+ }
62
+ export function projectionHistoryJson(value) {
63
+ return `${JSON.stringify(value)}\n`;
64
+ }
@@ -1,4 +1,5 @@
1
1
  import { KEIYAKU_FILE } from "../../keiyaku.js";
2
+ import { isCompleteLifecycleOutcome } from "../../core/outcome-base.js";
2
3
  import { commissionSlug, formatCommissionCoordinate } from "../../core/ids.js";
3
4
  import { assembleResponse, buildSection, DISPLAY_TEXT_MAX_CHARS, FORMAT_LIST_MAX_ITEM_CHARS, FORMAT_LIST_MAX_ITEMS, formatMaybe, formatWarnings, shellQuote, truncateForDisplay, } from "./format.js";
4
5
  import { WARNING_SECTION_TITLE } from "./response-style.js";
@@ -63,6 +64,26 @@ export function buildBindResponse(result, cwd) {
63
64
  ...(overlapWarnings.length > 0 ? ["Warnings:", ...overlapWarnings] : []),
64
65
  ].join("\n"), payload);
65
66
  }
67
+ export function buildContainedBindResponse(result) {
68
+ const payload = {
69
+ family: result.family,
70
+ completion: result.completion,
71
+ committed: result.committed,
72
+ heads: result.heads,
73
+ reconciliationKey: result.reconciliationKey,
74
+ prerequisites: result.prerequisites,
75
+ downstream: result.downstream,
76
+ replay: result.replay,
77
+ carrier: result.carrier,
78
+ cancellationObserved: result.cancellationObserved,
79
+ };
80
+ return coreTextResponse(`Bind committed with incomplete replay · head ${result.committed.head} · reconcile ${result.reconciliationKey}`, payload);
81
+ }
82
+ export function buildBindOutcomeResponse(result, cwd) {
83
+ return isCompleteLifecycleOutcome(result)
84
+ ? buildBindResponse(result, cwd)
85
+ : buildContainedBindResponse(result);
86
+ }
66
87
  function renderScopeOverlapWarnings(overlaps) {
67
88
  const overlapsByCounterparty = new Map();
68
89
  for (const overlap of overlaps) {
@@ -105,10 +126,8 @@ export function buildForfeitResponse(result) {
105
126
  `Could not remove contract worktree ${result.cleanup.worktreeRemoveFailure.path}: ${result.cleanup.worktreeRemoveFailure.reason}`,
106
127
  ]
107
128
  : []),
108
- ...(result.cleanup.branchDeleteFailure
109
- ? [
110
- `Could not delete contract branch: ${redactBranchReference(result.cleanup.branchDeleteFailure.reason, result.cleanup.branchDeleteFailure.name)}`,
111
- ]
129
+ ...(result.cleanup.deliveryRef.status === "mismatch"
130
+ ? [`Delivery ref ${result.cleanup.deliveryRef.mismatch.ref} remains present at ${result.cleanup.deliveryRef.mismatch.observed.head}; terminal replay preserved it as a projected-surface mismatch.`]
112
131
  : []),
113
132
  ];
114
133
  if (warnings.length === 0) {
@@ -117,11 +136,6 @@ export function buildForfeitResponse(result) {
117
136
  const warningSection = buildSection(WARNING_SECTION_TITLE, formatWarnings(warnings));
118
137
  return prependAddressReceipt(coreTextResponse(assembleResponse(base, "", [warningSection].filter((section) => section !== null)), payload), result.address);
119
138
  }
120
- function redactBranchReference(message, branch) {
121
- return message
122
- .replaceAll(`refs/heads/${branch}`, "contract branch")
123
- .replaceAll(branch, "contract branch");
124
- }
125
139
  export const RULES_SECTION_MAX_CHARS = 5000;
126
140
  export const INFO_SECTION_TITLE = "Info";
127
141
  export const HINTS_SECTION_TITLE = "Hints";
@@ -7,7 +7,6 @@ import { renderTerminalFailureFace, terminalFailureCodeSuffix } from "./terminal
7
7
  import { formatTellCommand } from "./shared.js";
8
8
  export const STATUS_DISEASES_SECTION_TITLE = "Diseases";
9
9
  export const STATUS_PROJECTION_SECTION_TITLE = "akuma";
10
- export const STATUS_CLAIM_RECEIPTS_SECTION_TITLE = "claim receipts";
11
10
  function projectionGlyph(state) {
12
11
  switch (state) {
13
12
  case "out":
@@ -37,15 +36,19 @@ export function renderSettingsDiseasesSection(diseases, taskDiseases = []) {
37
36
  return `${STATUS_DISEASES_SECTION_TITLE}\n${lines.join("\n")}\n`;
38
37
  }
39
38
  function renderTaskStatusSection(tasks) {
40
- if (!tasks || tasks.inProgress + tasks.ready + tasks.blocked === 0)
39
+ if (!tasks || tasks.inProgress + tasks.onHold + tasks.ready + tasks.blocked === 0)
41
40
  return "";
42
41
  const segments = [
43
42
  tasks.inProgress > 0 ? `${tasks.inProgress} in_progress` : undefined,
43
+ tasks.onHold > 0 ? `${tasks.onHold} on hold` : undefined,
44
44
  tasks.ready > 0 ? `${tasks.ready} ready` : undefined,
45
45
  tasks.blocked > 0 ? `${tasks.blocked} blocked` : undefined,
46
46
  ].filter((segment) => segment !== undefined);
47
47
  const lines = [`tasks · ${segments.join(" / ")}`];
48
- lines.push(...tasks.topTasks.map((task) => ` ${task.id} · P${task.pri} · ${task.state === "in_progress" ? "in_progress" : "ready"} · age ${formatAgeMs(task.ageMs)} · active ${formatAgeMs(task.activityAgeMs)} · ${task.title}`));
48
+ lines.push(...tasks.topTasks.map((task) => {
49
+ const onHold = task.state === "on_hold";
50
+ return ` ${task.id} · P${task.pri} · ${onHold ? "on hold" : "in_progress"} · age ${formatAgeMs(task.ageMs)} · ${onHold ? "updated" : "active"} ${formatAgeMs(task.activityAgeMs)} · ${task.title}`;
51
+ }));
49
52
  if (tasks.omitted > 0) {
50
53
  lines.push(` · +${tasks.omitted} more — keiyaku task ls`);
51
54
  }
@@ -69,12 +72,12 @@ function terminalFailureSuffix(row) {
69
72
  function renderProjectionOmission(board) {
70
73
  if (!board.omitted)
71
74
  return undefined;
72
- const counts = ["failed", "dead", "done", "killed", "dismissed", "incompatible"]
75
+ const counts = ["failed", "dead", "done", "killed", "dismissed"]
73
76
  .flatMap((state) => {
74
77
  const count = board.omitted?.byState[state] ?? 0;
75
78
  return count > 0 ? [`${count} ${state}`] : [];
76
79
  });
77
- return ` · +${board.omitted.total} historical projections not shown (${counts.join(" · ")})`;
80
+ return counts.length > 0 ? `+ ${counts.join(" · ")}` : undefined;
78
81
  }
79
82
  function renderProjectionAge(row) {
80
83
  const duration = formatAgeMs(row.durationMs) ?? "?";
@@ -99,10 +102,9 @@ function renderProjectionAge(row) {
99
102
  return facts;
100
103
  }
101
104
  function renderProjectionTellCounts(row) {
102
- const { inbox, inflight, submitted, consumed } = row.tellCounts;
103
- if (inbox === 0 && inflight === 0 && submitted === 0 && consumed === 0)
104
- return "";
105
- return ` tell ${inbox}▸${inflight}▸${submitted}▸${consumed}`;
105
+ const { inbox, inflight, submitted, seen, consumed } = row.tellCounts;
106
+ const total = inbox + inflight + submitted + seen + consumed;
107
+ return total === 0 ? "" : ` tell ${total} told`;
106
108
  }
107
109
  function projectionActivityState(row) {
108
110
  switch (row.state) {
@@ -194,42 +196,18 @@ export function renderProjectionStatusSection(board) {
194
196
  lines.push(omission);
195
197
  return `${STATUS_PROJECTION_SECTION_TITLE}\n${lines.join("\n")}\n`;
196
198
  }
197
- function renderClaimReconciliationSection(recon) {
198
- if (!recon)
199
- return "";
200
- const lines = recon.unclaimedSegments.map((seg) => `${recon.defaultBranch} ${shortCommitHash(seg.oldHead)}▸${shortCommitHash(seg.newHead)} · ${seg.commitCount} commits · 无 claim 收据`);
201
- lines.push(...recon.receiptIssues.map((issue) => `receipt line ${issue.line} · ${issue.reason}`));
202
- if (lines.length === 0)
203
- return "";
204
- return `${STATUS_CLAIM_RECEIPTS_SECTION_TITLE}\n${lines.join("\n")}\n`;
205
- }
206
199
  /**
207
200
  * Status mouth: kanshi board plus optional diseases section.
208
201
  * Healthy boards (no diseases) are byte-identical to renderKanshiBoard alone.
209
202
  */
210
203
  export function renderStatusBoard(board) {
211
204
  const boardText = renderKanshiBoard(board);
212
- const projectionText = renderProjectionStatusSection(board.projections);
213
- const receiptsText = renderClaimReconciliationSection(board.claimReconciliation);
214
- const taskText = renderTaskStatusSection(board.tasks);
215
205
  const taskDiseaseText = [
216
206
  ...(board.tasks?.diseases ?? []).map((disease) => disease.message),
217
207
  ...(board.tasks?.diagnostic ? [board.tasks.diagnostic] : []),
218
208
  ];
219
209
  const diseasesText = renderSettingsDiseasesSection(board.diseases, taskDiseaseText);
220
- if (!projectionText && !receiptsText && !taskText && diseasesText === "")
210
+ if (diseasesText === "")
221
211
  return boardText;
222
- const sections = [boardText.trimEnd()];
223
- if (board.targetFilter !== null && (projectionText || receiptsText || diseasesText)) {
224
- sections.push("repository-wide — not filtered by target");
225
- }
226
- if (projectionText)
227
- sections.push(projectionText.trimEnd());
228
- if (receiptsText)
229
- sections.push(receiptsText.trimEnd());
230
- if (taskText)
231
- sections.push(taskText.trimEnd());
232
- if (diseasesText)
233
- sections.push(diseasesText.trimEnd());
234
- return `${sections.join("\n\n")}\n`;
212
+ return `${[boardText.trimEnd(), diseasesText.trimEnd()].join("\n\n")}\n`;
235
213
  }
@@ -8,13 +8,29 @@ export function deriveTaskDisplay(task) {
8
8
  return display === "bound" ? `bound(${shortContractId(task.contractId)})` : display;
9
9
  }
10
10
  function renderTaskRowWithId(task, id) {
11
- return `${id} · P${task.pri} · ${deriveTaskDisplay(task)} · ${task.title}`;
11
+ return `${id} · ${task.namespace ?? "hub"} · P${task.pri} · ${deriveTaskDisplay(task)} · ${task.title}`;
12
12
  }
13
13
  export function renderTaskListRow(task) {
14
- return renderTaskRowWithId(task, task.id);
14
+ return renderTaskRowWithId(task, task.coordinate);
15
15
  }
16
16
  export function renderTaskMutationRow(task) {
17
- return renderTaskRowWithId(task, task.id);
17
+ return renderTaskRowWithId(task, task.coordinate);
18
+ }
19
+ export function renderTaskUpdate(result) {
20
+ const lines = [renderTaskMutationRow(result.task)];
21
+ if (result.bodyChange) {
22
+ lines.push(`body · +${result.bodyChange.diffstat.additions} −${result.bodyChange.diffstat.deletions} lines`);
23
+ for (const section of result.bodyChange.removedSections) {
24
+ lines.push(`▲ section removed: ${section}`);
25
+ }
26
+ }
27
+ return lines.join("\n");
28
+ }
29
+ export function renderTaskUpdateJson(result) {
30
+ return JSON.stringify({
31
+ ...taskJson(result.task),
32
+ ...(result.bodyChange === undefined ? {} : { bodyChange: result.bodyChange }),
33
+ });
18
34
  }
19
35
  export function renderTaskRow(task, completeId = false) {
20
36
  return completeId ? renderTaskMutationRow(task) : renderTaskListRow(task);
@@ -28,24 +44,20 @@ export function renderTaskList(tasks, options = {}) {
28
44
  export function renderSelectedTaskList(tasks) {
29
45
  return tasks.map(renderTaskListRow).join("\n");
30
46
  }
31
- function renderIds(ids) {
32
- return ids.length === 0 ? "(none)" : ids.join(", ");
33
- }
34
47
  function renderTaskDetails(task) {
35
48
  const released = new Set(task.releasedNeeds);
36
- const needs = task.needs.length === 0
37
- ? "(none)"
38
- : task.needs
39
- .map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`)
40
- .join(", ");
41
49
  return [
42
- `${task.id} · ${task.title}`,
50
+ `${task.coordinate} · ${task.title}`,
51
+ `namespace: ${task.namespace ?? "(hub)"}`,
43
52
  `state: ${deriveTaskDisplay(task)}`,
44
53
  `priority: ${task.pri}`,
45
- `needs: ${needs}`,
46
- `blocks: ${renderIds(task.blocks)}`,
47
- `parent: ${task.parent === null ? "(none)" : task.parent}`,
48
- `from: ${renderIds(task.from)}`,
54
+ ...(task.needs.length === 0 ? [] : [`needs: ${task.needs.map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`).join(", ")}`]),
55
+ ...(task.blocks.length === 0 ? [] : [`blocks: ${task.blocks.join(", ")}`]),
56
+ ...(task.parent === null ? [] : [`parent: ${task.parent}`]),
57
+ ...(task.from.length === 0 ? [] : [`from: ${task.from.join(", ")}`]),
58
+ ...(task.supersedes.length === 0 ? [] : [`supersedes: ${task.supersedes.join(", ")}`]),
59
+ ...(task.supersededBy.length === 0 ? [] : [`superseded by: ${task.supersededBy.join(", ")}`]),
60
+ ...(task.relatedTasks.length === 0 ? [] : [`relates to: ${task.relatedTasks.join(", ")}`]),
49
61
  `created: ${task.createdAt} by ${task.creator}`,
50
62
  "",
51
63
  task.body,
@@ -55,16 +67,24 @@ function renderTreeNode(node, prefix, last) {
55
67
  const branch = last ? "`- " : "|- ";
56
68
  const nestedPrefix = `${prefix}${last ? " " : "| "}`;
57
69
  const task = node.task;
58
- const released = new Set(task.releasedNeeds);
59
- const needs = task.needs.length === 0
60
- ? "(none)"
61
- : task.needs.map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`).join(", ");
62
70
  const lines = [
63
- `${prefix}${branch}${task.id} · ${deriveTaskDisplay(task)} · P${task.pri} · ${task.title}`,
64
- `${nestedPrefix}needs: ${needs}`,
65
- `${nestedPrefix}blocks: ${renderIds(task.blocks)}`,
66
- `${nestedPrefix}from: ${renderIds(task.from)}`,
71
+ `${prefix}${branch}${task.coordinate} · ${deriveTaskDisplay(task)} · P${task.pri} · ${task.title}`,
67
72
  ];
73
+ const released = new Set(task.releasedNeeds);
74
+ if (task.needs.length > 0)
75
+ lines.push(`${nestedPrefix}needs: ${task.needs.map((id) => `${id}${released.has(id) ? " (dropped)" : ""}`).join(", ")}`);
76
+ if (task.blocks.length > 0)
77
+ lines.push(`${nestedPrefix}blocks: ${task.blocks.join(", ")}`);
78
+ if (task.parent !== null)
79
+ lines.push(`${nestedPrefix}parent: ${task.parent}`);
80
+ if (task.from.length > 0)
81
+ lines.push(`${nestedPrefix}from: ${task.from.join(", ")}`);
82
+ if (task.supersedes.length > 0)
83
+ lines.push(`${nestedPrefix}supersedes: ${task.supersedes.join(", ")}`);
84
+ if (task.supersededBy.length > 0)
85
+ lines.push(`${nestedPrefix}superseded by: ${task.supersededBy.join(", ")}`);
86
+ if (task.relatedTasks.length > 0)
87
+ lines.push(`${nestedPrefix}relates to: ${task.relatedTasks.join(", ")}`);
68
88
  node.children.forEach((child, index) => lines.push(...renderTreeNode(child, nestedPrefix, index === node.children.length - 1)));
69
89
  return lines;
70
90
  }
@@ -74,15 +94,17 @@ function treeDiagnostic(diagnostic) {
74
94
  }
75
95
  export function renderTaskShow(task, tree) {
76
96
  const treeLines = tree.descendants.length === 0
77
- ? ["children tree: (empty)"]
97
+ ? []
78
98
  : ["children tree:", ...tree.descendants.flatMap((node, index) => renderTreeNode(node, "", index === tree.descendants.length - 1))];
79
99
  if (tree.diagnostics.length > 0)
80
100
  treeLines.push("tree diagnostics:", ...tree.diagnostics.map(treeDiagnostic));
81
- return [...renderTaskDetails(task), "", ...treeLines].join("\n");
101
+ return [...renderTaskDetails(task), ...(treeLines.length === 0 ? [] : ["", ...treeLines])].join("\n");
82
102
  }
83
103
  export function taskJson(task) {
84
104
  return {
85
105
  id: task.id,
106
+ namespace: task.namespace,
107
+ coordinate: task.coordinate,
86
108
  title: task.title,
87
109
  state: task.state,
88
110
  display: deriveTaskDisplay(task),
@@ -91,6 +113,9 @@ export function taskJson(task) {
91
113
  blocks: [...task.blocks],
92
114
  parent: task.parent,
93
115
  from: [...task.from],
116
+ supersedes: [...task.supersedes],
117
+ supersededBy: [...task.supersededBy],
118
+ relatesTo: [...task.relatedTasks],
94
119
  contractId: task.contractId ?? null,
95
120
  createdAt: task.createdAt,
96
121
  updatedAt: task.updatedAt ?? task.createdAt,
@@ -3,7 +3,7 @@ export function terminalFailureCodeSuffix(code) {
3
3
  return code.startsWith("SUBAGENT_") ? code.slice("SUBAGENT_".length) : code;
4
4
  }
5
5
  export function terminalFailureColdStatement(failure) {
6
- if (failure.publicCause?.kind === "rate_limit_exhausted") {
6
+ if (failure.publicCause?.code === "rate_limit_exhausted") {
7
7
  return "the provider rate limit is exhausted";
8
8
  }
9
9
  if (failure.class === "runner") {
@@ -33,6 +33,8 @@ function terminalFailureEvidence(failure, statement) {
33
33
  && failure.code === "SUBAGENT_NON_ZERO_EXIT"
34
34
  && failure.exitCode !== undefined;
35
35
  const parts = [];
36
+ if (failure.message)
37
+ parts.push(failure.message);
36
38
  if (failure.timeoutMs !== undefined) {
37
39
  parts.push(failure.code === "SUBAGENT_POLICY_BUDGET" ? `budget ${failure.timeoutMs}ms` : `idle ${failure.timeoutMs}ms`);
38
40
  }