harnery 0.36.0 → 0.37.0

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 (311) hide show
  1. package/dist/commander.js +4 -0
  2. package/dist/commands/agents.d.ts.map +1 -1
  3. package/dist/commands/agents.js +87 -46
  4. package/dist/commands/artifacts.d.ts.map +1 -1
  5. package/dist/commands/artifacts.js +26 -1
  6. package/dist/commands/browse.d.ts.map +1 -1
  7. package/dist/commands/browse.js +11 -0
  8. package/dist/commands/diagnostics.d.ts +4 -0
  9. package/dist/commands/diagnostics.d.ts.map +1 -0
  10. package/dist/commands/diagnostics.js +229 -0
  11. package/dist/commands/files.d.ts +12 -0
  12. package/dist/commands/files.d.ts.map +1 -1
  13. package/dist/commands/files.js +54 -1
  14. package/dist/commands/governor.d.ts.map +1 -1
  15. package/dist/commands/governor.js +4 -0
  16. package/dist/commands/ledger-v3.d.ts.map +1 -1
  17. package/dist/commands/ledger-v3.js +71 -2
  18. package/dist/commands/qa-run.d.ts +4 -0
  19. package/dist/commands/qa-run.d.ts.map +1 -0
  20. package/dist/commands/qa-run.js +141 -0
  21. package/dist/commands/resources.d.ts +4 -0
  22. package/dist/commands/resources.d.ts.map +1 -0
  23. package/dist/commands/resources.js +29 -0
  24. package/dist/commands/supervisor.d.ts +4 -0
  25. package/dist/commands/supervisor.d.ts.map +1 -0
  26. package/dist/commands/supervisor.js +101 -0
  27. package/dist/commands/web.d.ts.map +1 -1
  28. package/dist/commands/web.js +17 -0
  29. package/dist/commands/workflow.d.ts.map +1 -1
  30. package/dist/commands/workflow.js +4 -0
  31. package/dist/core/agents/cli.js +7 -3
  32. package/dist/core/agents/health.d.ts +17 -0
  33. package/dist/core/agents/health.d.ts.map +1 -0
  34. package/dist/core/agents/health.js +109 -0
  35. package/dist/core/agents/mailbox.d.ts +92 -0
  36. package/dist/core/agents/mailbox.d.ts.map +1 -0
  37. package/dist/core/agents/mailbox.js +325 -0
  38. package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
  39. package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
  40. package/dist/core/agents/reconcile-coordination-v3.js +58 -0
  41. package/dist/core/agents/render/prompt-context.d.ts +5 -0
  42. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  43. package/dist/core/agents/render/prompt-context.js +34 -13
  44. package/dist/core/agents/render/session-context.d.ts +8 -0
  45. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  46. package/dist/core/agents/render/session-context.js +34 -3
  47. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  48. package/dist/core/agents/rules/commit-conflict.js +50 -1
  49. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  50. package/dist/core/agents/rules/stop-hook.js +6 -10
  51. package/dist/core/agents/session-finalizer-v3.js +4 -1
  52. package/dist/core/artifacts/index.d.ts +26 -1
  53. package/dist/core/artifacts/index.d.ts.map +1 -1
  54. package/dist/core/artifacts/index.js +146 -6
  55. package/dist/core/config.d.ts +12 -0
  56. package/dist/core/config.d.ts.map +1 -1
  57. package/dist/core/config.js +43 -0
  58. package/dist/core/diagnostics/advice.d.ts +9 -0
  59. package/dist/core/diagnostics/advice.d.ts.map +1 -0
  60. package/dist/core/diagnostics/advice.js +112 -0
  61. package/dist/core/diagnostics/bundle.d.ts +35 -0
  62. package/dist/core/diagnostics/bundle.d.ts.map +1 -0
  63. package/dist/core/diagnostics/bundle.js +874 -0
  64. package/dist/core/diagnostics/comparison.d.ts +5 -0
  65. package/dist/core/diagnostics/comparison.d.ts.map +1 -0
  66. package/dist/core/diagnostics/comparison.js +444 -0
  67. package/dist/core/diagnostics/contract.d.ts +245 -0
  68. package/dist/core/diagnostics/contract.d.ts.map +1 -0
  69. package/dist/core/diagnostics/contract.js +18 -0
  70. package/dist/core/diagnostics/identity.d.ts +3 -0
  71. package/dist/core/diagnostics/identity.d.ts.map +1 -0
  72. package/dist/core/diagnostics/identity.js +10 -0
  73. package/dist/core/diagnostics/index.d.ts +8 -0
  74. package/dist/core/diagnostics/index.d.ts.map +1 -0
  75. package/dist/core/diagnostics/index.js +7 -0
  76. package/dist/core/diagnostics/replay.d.ts +8 -0
  77. package/dist/core/diagnostics/replay.d.ts.map +1 -0
  78. package/dist/core/diagnostics/replay.js +213 -0
  79. package/dist/core/diagnostics/sanitize.d.ts +9 -0
  80. package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
  81. package/dist/core/diagnostics/sanitize.js +84 -0
  82. package/dist/core/events/legacy-storage/compression.d.ts +13 -0
  83. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
  84. package/dist/core/events/legacy-storage/compression.js +107 -0
  85. package/dist/core/events/legacy-storage/index.d.ts +1 -0
  86. package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
  87. package/dist/core/events/legacy-storage/index.js +1 -0
  88. package/dist/core/events/v3/archive-retention.d.ts +33 -0
  89. package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
  90. package/dist/core/events/v3/archive-retention.js +195 -0
  91. package/dist/core/events/v3/capabilities.d.ts +1 -1
  92. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  93. package/dist/core/events/v3/capabilities.js +1 -0
  94. package/dist/core/events/v3/index.d.ts +1 -0
  95. package/dist/core/events/v3/index.d.ts.map +1 -1
  96. package/dist/core/events/v3/index.js +1 -0
  97. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  98. package/dist/core/events/v3/live-routing.js +1 -0
  99. package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
  100. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  101. package/dist/core/events/v3/producers/hook-base.js +13 -2
  102. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  103. package/dist/core/events/v3/producers/intake.js +1 -0
  104. package/dist/core/events/v3/producers/recorder.d.ts +16 -0
  105. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  106. package/dist/core/events/v3/producers/recorder.js +88 -1
  107. package/dist/core/hooks/adapter/output.d.ts +8 -0
  108. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  109. package/dist/core/hooks/adapter/output.js +11 -1
  110. package/dist/core/hooks/cli.js +183 -192
  111. package/dist/core/hooks/health.d.ts +59 -0
  112. package/dist/core/hooks/health.d.ts.map +1 -0
  113. package/dist/core/hooks/health.js +84 -0
  114. package/dist/core/resources/contract.d.ts +90 -0
  115. package/dist/core/resources/contract.d.ts.map +1 -0
  116. package/dist/core/resources/contract.js +2 -0
  117. package/dist/core/resources/index.d.ts +6 -0
  118. package/dist/core/resources/index.d.ts.map +1 -0
  119. package/dist/core/resources/index.js +5 -0
  120. package/dist/core/resources/sampler.d.ts +27 -0
  121. package/dist/core/resources/sampler.d.ts.map +1 -0
  122. package/dist/core/resources/sampler.js +357 -0
  123. package/dist/core/resources/service-status.d.ts +3 -0
  124. package/dist/core/resources/service-status.d.ts.map +1 -0
  125. package/dist/core/resources/service-status.js +48 -0
  126. package/dist/core/resources/service.d.ts +26 -0
  127. package/dist/core/resources/service.d.ts.map +1 -0
  128. package/dist/core/resources/service.js +292 -0
  129. package/dist/core/resources/storage.d.ts +13 -0
  130. package/dist/core/resources/storage.d.ts.map +1 -0
  131. package/dist/core/resources/storage.js +30 -0
  132. package/dist/core/storage/atomic-json.d.ts +3 -0
  133. package/dist/core/storage/atomic-json.d.ts.map +1 -0
  134. package/dist/core/storage/atomic-json.js +15 -0
  135. package/dist/core/storage/builtins.d.ts.map +1 -1
  136. package/dist/core/storage/builtins.js +45 -6
  137. package/dist/core/storage/logger.d.ts +2 -0
  138. package/dist/core/storage/logger.d.ts.map +1 -1
  139. package/dist/core/storage/logger.js +2 -0
  140. package/dist/core/storage/query.d.ts +14 -0
  141. package/dist/core/storage/query.d.ts.map +1 -1
  142. package/dist/core/storage/query.js +51 -0
  143. package/dist/core/supervisor/activity.d.ts +4 -0
  144. package/dist/core/supervisor/activity.d.ts.map +1 -0
  145. package/dist/core/supervisor/activity.js +141 -0
  146. package/dist/core/supervisor/contract.d.ts +268 -0
  147. package/dist/core/supervisor/contract.d.ts.map +1 -0
  148. package/dist/core/supervisor/contract.js +35 -0
  149. package/dist/core/supervisor/explanations.d.ts +4 -0
  150. package/dist/core/supervisor/explanations.d.ts.map +1 -0
  151. package/dist/core/supervisor/explanations.js +71 -0
  152. package/dist/core/supervisor/findings.d.ts +20 -0
  153. package/dist/core/supervisor/findings.d.ts.map +1 -0
  154. package/dist/core/supervisor/findings.js +399 -0
  155. package/dist/core/supervisor/history.d.ts +9 -0
  156. package/dist/core/supervisor/history.d.ts.map +1 -0
  157. package/dist/core/supervisor/history.js +46 -0
  158. package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
  159. package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
  160. package/dist/core/supervisor/hook-health-alerts.js +70 -0
  161. package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
  162. package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
  163. package/dist/core/supervisor/hook-health-storage.js +24 -0
  164. package/dist/core/supervisor/hook-health.d.ts +72 -0
  165. package/dist/core/supervisor/hook-health.d.ts.map +1 -0
  166. package/dist/core/supervisor/hook-health.js +203 -0
  167. package/dist/core/supervisor/hooks.d.ts +10 -0
  168. package/dist/core/supervisor/hooks.d.ts.map +1 -0
  169. package/dist/core/supervisor/hooks.js +28 -0
  170. package/dist/core/supervisor/index.d.ts +15 -0
  171. package/dist/core/supervisor/index.d.ts.map +1 -0
  172. package/dist/core/supervisor/index.js +14 -0
  173. package/dist/core/supervisor/log-feed.d.ts +9 -0
  174. package/dist/core/supervisor/log-feed.d.ts.map +1 -0
  175. package/dist/core/supervisor/log-feed.js +103 -0
  176. package/dist/core/supervisor/service.d.ts +29 -0
  177. package/dist/core/supervisor/service.d.ts.map +1 -0
  178. package/dist/core/supervisor/service.js +450 -0
  179. package/dist/core/supervisor/services.d.ts +11 -0
  180. package/dist/core/supervisor/services.d.ts.map +1 -0
  181. package/dist/core/supervisor/services.js +155 -0
  182. package/dist/core/supervisor/status.d.ts +3 -0
  183. package/dist/core/supervisor/status.d.ts.map +1 -0
  184. package/dist/core/supervisor/status.js +48 -0
  185. package/dist/core/supervisor/storage.d.ts +30 -0
  186. package/dist/core/supervisor/storage.d.ts.map +1 -0
  187. package/dist/core/supervisor/storage.js +73 -0
  188. package/dist/core/supervisor/timeline.d.ts +3 -0
  189. package/dist/core/supervisor/timeline.d.ts.map +1 -0
  190. package/dist/core/supervisor/timeline.js +87 -0
  191. package/dist/core/workflow/admission.d.ts +23 -0
  192. package/dist/core/workflow/admission.d.ts.map +1 -0
  193. package/dist/core/workflow/admission.js +136 -0
  194. package/dist/core/workflow/engine.d.ts.map +1 -1
  195. package/dist/core/workflow/engine.js +77 -2
  196. package/dist/core/workflow/index.d.ts +3 -2
  197. package/dist/core/workflow/index.d.ts.map +1 -1
  198. package/dist/core/workflow/index.js +2 -1
  199. package/dist/core/workflow/proof.d.ts +2 -1
  200. package/dist/core/workflow/proof.d.ts.map +1 -1
  201. package/dist/core/workflow/proof.js +39 -1
  202. package/dist/core/workflow/run-state.d.ts +3 -2
  203. package/dist/core/workflow/run-state.d.ts.map +1 -1
  204. package/dist/core/workflow/run-state.js +8 -1
  205. package/dist/core/workflow/types.d.ts +33 -1
  206. package/dist/core/workflow/types.d.ts.map +1 -1
  207. package/dist/core/workflow/types.js +2 -1
  208. package/dist/lib/browser/client.d.ts +5 -0
  209. package/dist/lib/browser/client.d.ts.map +1 -1
  210. package/dist/lib/browser/client.js +1 -0
  211. package/dist/lib/browser/index.d.ts +2 -0
  212. package/dist/lib/browser/index.d.ts.map +1 -1
  213. package/dist/lib/browser/index.js +2 -0
  214. package/dist/lib/browser/qa-run-contracts.d.ts +173 -0
  215. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
  216. package/dist/lib/browser/qa-run-contracts.js +226 -0
  217. package/dist/lib/browser/qa-run.d.ts +51 -0
  218. package/dist/lib/browser/qa-run.d.ts.map +1 -0
  219. package/dist/lib/browser/qa-run.js +684 -0
  220. package/dist/lib/coord-root-id.d.ts +9 -0
  221. package/dist/lib/coord-root-id.d.ts.map +1 -0
  222. package/dist/lib/coord-root-id.js +17 -0
  223. package/dist/lib/instructions/apply.d.ts +3 -1
  224. package/dist/lib/instructions/apply.d.ts.map +1 -1
  225. package/dist/lib/instructions/apply.js +74 -3
  226. package/dist/lib/instructions/templates.d.ts.map +1 -1
  227. package/dist/lib/instructions/templates.js +6 -0
  228. package/package.json +12 -1
  229. package/src/commander.ts +26 -0
  230. package/src/commands/agents.ts +102 -49
  231. package/src/commands/artifacts.ts +40 -2
  232. package/src/commands/browse.ts +15 -0
  233. package/src/commands/diagnostics.ts +271 -0
  234. package/src/commands/files.ts +102 -2
  235. package/src/commands/governor.ts +8 -0
  236. package/src/commands/ledger-v3.ts +82 -0
  237. package/src/commands/qa-run.ts +184 -0
  238. package/src/commands/resources.ts +38 -0
  239. package/src/commands/supervisor.ts +141 -0
  240. package/src/commands/web.ts +19 -0
  241. package/src/commands/workflow.ts +8 -0
  242. package/src/core/agents/cli.ts +7 -3
  243. package/src/core/agents/health.ts +142 -0
  244. package/src/core/agents/mailbox.ts +373 -0
  245. package/src/core/agents/reconcile-coordination-v3.ts +77 -0
  246. package/src/core/agents/render/prompt-context.ts +42 -12
  247. package/src/core/agents/render/session-context.ts +37 -3
  248. package/src/core/agents/rules/commit-conflict.ts +58 -1
  249. package/src/core/agents/rules/stop-hook.ts +6 -12
  250. package/src/core/agents/session-finalizer-v3.ts +4 -1
  251. package/src/core/artifacts/index.ts +196 -10
  252. package/src/core/config.ts +111 -2
  253. package/src/core/diagnostics/advice.ts +147 -0
  254. package/src/core/diagnostics/bundle.ts +1142 -0
  255. package/src/core/diagnostics/comparison.ts +565 -0
  256. package/src/core/diagnostics/contract.ts +311 -0
  257. package/src/core/diagnostics/identity.ts +11 -0
  258. package/src/core/diagnostics/index.ts +7 -0
  259. package/src/core/diagnostics/replay.ts +314 -0
  260. package/src/core/diagnostics/sanitize.ts +97 -0
  261. package/src/core/events/legacy-storage/compression.ts +130 -0
  262. package/src/core/events/legacy-storage/index.ts +1 -0
  263. package/src/core/events/v3/archive-retention.ts +251 -0
  264. package/src/core/events/v3/capabilities.ts +2 -0
  265. package/src/core/events/v3/index.ts +9 -0
  266. package/src/core/events/v3/live-routing.ts +1 -0
  267. package/src/core/events/v3/producers/hook-base.ts +14 -2
  268. package/src/core/events/v3/producers/intake.ts +1 -0
  269. package/src/core/events/v3/producers/recorder.ts +110 -1
  270. package/src/core/hooks/adapter/output.ts +12 -1
  271. package/src/core/hooks/cli.ts +192 -215
  272. package/src/core/hooks/health.ts +146 -0
  273. package/src/core/resources/contract.ts +98 -0
  274. package/src/core/resources/index.ts +5 -0
  275. package/src/core/resources/sampler.ts +458 -0
  276. package/src/core/resources/service-status.ts +62 -0
  277. package/src/core/resources/service.ts +337 -0
  278. package/src/core/resources/storage.ts +44 -0
  279. package/src/core/storage/atomic-json.ts +15 -0
  280. package/src/core/storage/builtins.ts +62 -6
  281. package/src/core/storage/logger.ts +2 -0
  282. package/src/core/storage/query.ts +58 -0
  283. package/src/core/supervisor/activity.ts +193 -0
  284. package/src/core/supervisor/contract.ts +312 -0
  285. package/src/core/supervisor/explanations.ts +104 -0
  286. package/src/core/supervisor/findings.ts +788 -0
  287. package/src/core/supervisor/history.ts +61 -0
  288. package/src/core/supervisor/hook-health-alerts.ts +108 -0
  289. package/src/core/supervisor/hook-health-storage.ts +25 -0
  290. package/src/core/supervisor/hook-health.ts +301 -0
  291. package/src/core/supervisor/hooks.ts +35 -0
  292. package/src/core/supervisor/index.ts +14 -0
  293. package/src/core/supervisor/log-feed.ts +131 -0
  294. package/src/core/supervisor/service.ts +511 -0
  295. package/src/core/supervisor/services.ts +203 -0
  296. package/src/core/supervisor/status.ts +59 -0
  297. package/src/core/supervisor/storage.ts +126 -0
  298. package/src/core/supervisor/timeline.ts +133 -0
  299. package/src/core/workflow/admission.ts +215 -0
  300. package/src/core/workflow/engine.ts +93 -1
  301. package/src/core/workflow/index.ts +10 -0
  302. package/src/core/workflow/proof.ts +54 -1
  303. package/src/core/workflow/run-state.ts +13 -1
  304. package/src/core/workflow/types.ts +39 -1
  305. package/src/lib/browser/client.ts +6 -0
  306. package/src/lib/browser/index.ts +29 -0
  307. package/src/lib/browser/qa-run-contracts.ts +380 -0
  308. package/src/lib/browser/qa-run.ts +784 -0
  309. package/src/lib/coord-root-id.ts +23 -0
  310. package/src/lib/instructions/apply.ts +81 -2
  311. package/src/lib/instructions/templates.ts +6 -0
@@ -4,6 +4,7 @@ import { monorepoRoot, resolveOwner } from "../core/agents/index.ts";
4
4
  import { readLiveCoordinationRow } from "../core/agents/state/live-coordination-view.ts";
5
5
  import {
6
6
  type ArtifactActor,
7
+ adoptUnmanagedArtifactFiles,
7
8
  cleanArtifacts,
8
9
  createArtifact,
9
10
  inventoryArtifacts,
@@ -28,7 +29,8 @@ export function registerArtifactsCommand(
28
29
  .description("Create one managed artifact workspace and print its path.")
29
30
  .requiredOption("--purpose <text>", "What the files are for")
30
31
  .option("--days <n>", "Retention in days (default from artifacts.default_retention_days)")
31
- .action((slug: string, opts: { purpose: string; days?: string }) => {
32
+ .option("--big", "Acknowledge that this workspace may exceed the per-bundle size ceiling")
33
+ .action((slug: string, opts: { purpose: string; days?: string; big?: boolean }) => {
32
34
  run(emit, () => {
33
35
  const repoRoot = requireRepoRoot(context);
34
36
  const actor = currentActor(repoRoot);
@@ -40,6 +42,7 @@ export function registerArtifactsCommand(
40
42
  purpose: opts.purpose,
41
43
  retentionDays,
42
44
  actor,
45
+ big: opts.big,
43
46
  });
44
47
  emit.data({
45
48
  path: created.path,
@@ -50,6 +53,34 @@ export function registerArtifactsCommand(
50
53
  });
51
54
  });
52
55
 
56
+ root
57
+ .command("adopt-unmanaged")
58
+ .description("Preview loose direct-child files, or move them into one managed workspace.")
59
+ .option(
60
+ "--purpose <text>",
61
+ "Why the adopted files are being retained",
62
+ "Adopt legacy loose artifact files",
63
+ )
64
+ .option("--days <n>", "Retention in days (default from artifacts.default_retention_days)")
65
+ .option("--big", "Acknowledge an adopted bundle above the per-bundle size ceiling")
66
+ .option("--yes", "Move the exact previewed regular files into a managed workspace")
67
+ .action((opts: { purpose: string; days?: string; big?: boolean; yes?: boolean }) => {
68
+ run(emit, () => {
69
+ const repoRoot = requireRepoRoot(context);
70
+ emit.data(
71
+ adoptUnmanagedArtifactFiles(repoRoot, {
72
+ yes: opts.yes,
73
+ big: opts.big,
74
+ purpose: opts.purpose,
75
+ retentionDays: opts.days
76
+ ? parseDays(opts.days)
77
+ : artifactDefaultRetentionDays(repoRoot),
78
+ actor: currentActor(repoRoot),
79
+ }),
80
+ );
81
+ });
82
+ });
83
+
53
84
  root
54
85
  .command("list")
55
86
  .description("Inventory every artifact workspace with its cleanup classification.")
@@ -143,16 +174,23 @@ function parseDays(value: string): number {
143
174
  return parsed;
144
175
  }
145
176
 
146
- function summarize(rows: { classification: string; action: string }[]): Record<string, unknown> {
177
+ function summarize(
178
+ rows: { classification: string; action: string; bytes?: number | null }[],
179
+ ): Record<string, unknown> {
147
180
  const classifications: Record<string, number> = {};
148
181
  for (const row of rows) {
149
182
  classifications[row.classification] = (classifications[row.classification] ?? 0) + 1;
150
183
  }
151
184
  return {
152
185
  total: rows.length,
186
+ bytes: rows.reduce((sum, row) => sum + (row.bytes ?? 0), 0),
153
187
  classifications,
154
188
  would_delete: rows.filter((row) => row.action === "would-delete").length,
155
189
  deleted: rows.filter((row) => row.action === "deleted").length,
190
+ would_delete_bytes: rows.reduce(
191
+ (sum, row) => sum + (row.action === "would-delete" ? (row.bytes ?? 0) : 0),
192
+ 0,
193
+ ),
156
194
  };
157
195
  }
158
196
 
@@ -117,6 +117,7 @@ interface BrowseOpts {
117
117
  store?: string;
118
118
  profile?: string;
119
119
  viewport?: string;
120
+ colorScheme?: string;
120
121
  waitUntil: string;
121
122
  timeout: string;
122
123
  json?: boolean;
@@ -275,6 +276,12 @@ export function registerBrowseCommand(
275
276
  "Viewport: mobile (390x844), tablet (820x1180), desktop (1280x800), hd (1920x1080), or explicit '1920x1080'",
276
277
  "desktop",
277
278
  )
279
+ .option(
280
+ "--color-scheme <scheme>",
281
+ "Emulate prefers-color-scheme for the whole session: light | dark. " +
282
+ "Renders theme-aware pages in that scheme without page-specific toggles. " +
283
+ "Absent = browser default (unchanged behavior).",
284
+ )
278
285
  .option("--login", "Headed mode for one-time auth flow (cookies persist in profile)")
279
286
  .option(
280
287
  "--login-close-file <path>",
@@ -734,6 +741,13 @@ async function runBrowse(
734
741
  : new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "harn-browse" });
735
742
  const headed = opts.login || opts.headed;
736
743
  const viewport = parseViewport(opts.viewport ?? "desktop");
744
+ let colorScheme: "light" | "dark" | undefined;
745
+ if (opts.colorScheme !== undefined) {
746
+ if (opts.colorScheme !== "light" && opts.colorScheme !== "dark") {
747
+ throw new Error(`--color-scheme must be light or dark (got: ${opts.colorScheme}).`);
748
+ }
749
+ colorScheme = opts.colorScheme;
750
+ }
737
751
  const proxy = opts.proxyFromEnv ? browserProxyFromEnv() : undefined;
738
752
  const proxyGate = opts.proxyFromEnv ? browserProxyGateFromEnv() : null;
739
753
  if (opts.loginCloseFile && !opts.login) {
@@ -754,6 +768,7 @@ async function runBrowse(
754
768
  headed,
755
769
  jar,
756
770
  viewport,
771
+ ...(colorScheme ? { colorScheme } : {}),
757
772
  navigationTimeout: Number.parseInt(opts.timeout, 10),
758
773
  waitUntil: opts.waitUntil as BrowseOpts["waitUntil"] as never,
759
774
  recordHarPath: opts.networkHar ? resolve(opts.networkHar) : undefined,
@@ -0,0 +1,271 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext, HarneryProgramContext } from "../commander.ts";
3
+ import { monorepoRoot, resolveOwner } from "../core/agents/index.ts";
4
+ import { readLiveCoordinationRow } from "../core/agents/state/live-coordination-view.ts";
5
+ import type { ArtifactActor } from "../core/artifacts/index.ts";
6
+ import {
7
+ buildDiagnosticAdvice,
8
+ captureDiagnosticBundle,
9
+ compareDiagnosticBundles,
10
+ DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
11
+ listDiagnosticBundles,
12
+ renderDiagnosticBundleComparison,
13
+ replayDiagnosticBundle,
14
+ showDiagnosticBundle,
15
+ } from "../core/diagnostics/index.ts";
16
+ import {
17
+ buildSupervisorTimeline,
18
+ explainSupervisorFinding,
19
+ readSupervisorExplanation,
20
+ readSupervisorFindings,
21
+ readSupervisorStatus,
22
+ readSupervisorTimeline,
23
+ type SupervisorCapability,
24
+ type SupervisorFinding,
25
+ } from "../core/supervisor/index.ts";
26
+
27
+ export function registerDiagnosticsCommand(
28
+ program: Command,
29
+ emit: EmitContext,
30
+ context?: HarneryProgramContext,
31
+ ): void {
32
+ const root = program
33
+ .command("diagnostics")
34
+ .description("Capture, inspect, and replay sanitized local diagnostic bundles.");
35
+
36
+ root
37
+ .command("list")
38
+ .description("List live findings and managed diagnostic bundles.")
39
+ .action(() => {
40
+ run(emit, () => {
41
+ const repoRoot = requireRepoRoot(context);
42
+ const report = readSupervisorFindings(repoRoot);
43
+ const findings = mergeFindings(report?.active ?? [], report?.transitions ?? []);
44
+ const bundles = listDiagnosticBundles(repoRoot);
45
+ const capability = liveFindingsCapability(repoRoot, report !== undefined);
46
+ emit.data({
47
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
48
+ kind: "diagnostic_list",
49
+ findings,
50
+ bundles,
51
+ capability,
52
+ total_findings: findings.length,
53
+ total_bundles: bundles.length,
54
+ });
55
+ });
56
+ });
57
+
58
+ root
59
+ .command("show <ref>")
60
+ .description("Show one finding or validate one diagnostic bundle.")
61
+ .action((ref: string) => {
62
+ run(emit, () => {
63
+ const repoRoot = requireRepoRoot(context);
64
+ const finding = findFinding(repoRoot, ref);
65
+ if (finding) {
66
+ emit.data({
67
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
68
+ kind: "diagnostic_finding",
69
+ finding,
70
+ timeline:
71
+ readSupervisorTimeline(repoRoot, finding.id) ?? buildSupervisorTimeline(finding),
72
+ explanation:
73
+ readSupervisorExplanation(repoRoot, finding.id) ?? explainSupervisorFinding(finding),
74
+ });
75
+ return;
76
+ }
77
+ const bundle = showDiagnosticBundle(repoRoot, ref);
78
+ emit.data({
79
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
80
+ kind: "diagnostic_bundle",
81
+ bundle: {
82
+ path: bundle.path,
83
+ manifest: bundle.manifest,
84
+ summary: bundle.summary,
85
+ },
86
+ });
87
+ });
88
+ });
89
+
90
+ root
91
+ .command("explain")
92
+ .description("Report bounded local pressure advice without taking action.")
93
+ .option("--bundle <artifact-ref>", "Use a frozen diagnostic bundle instead of live state")
94
+ .option("--json", "Emit structured JSON")
95
+ .action((opts: { bundle?: string; json?: boolean }) => {
96
+ run(emit, () => {
97
+ if (opts.json) emit.config({ format: "json" });
98
+ const repoRoot = requireRepoRoot(context);
99
+ if (opts.bundle) {
100
+ const bundle = showDiagnosticBundle(repoRoot, opts.bundle);
101
+ emit.data({
102
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
103
+ kind: "diagnostic_advice",
104
+ source: {
105
+ mode: "frozen",
106
+ artifact_id: bundle.manifest.artifact_id,
107
+ captured_at: bundle.manifest.captured_at,
108
+ },
109
+ advice: bundle.expected.advice,
110
+ });
111
+ return;
112
+ }
113
+ const report = readSupervisorFindings(repoRoot);
114
+ const findings = mergeFindings(report?.active ?? [], report?.transitions ?? []);
115
+ const sourceCapability = liveFindingsCapability(repoRoot, report !== undefined);
116
+ emit.data({
117
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
118
+ kind: "diagnostic_advice",
119
+ source: { mode: "live" },
120
+ advice: buildDiagnosticAdvice({
121
+ findings,
122
+ sourceCapability,
123
+ evaluatedAt: new Date().toISOString(),
124
+ }),
125
+ });
126
+ });
127
+ });
128
+
129
+ root
130
+ .command("capture")
131
+ .description("Capture sanitized supervisor inputs into a frozen managed bundle.")
132
+ .option("--finding <id>", "Capture the time range for one finding")
133
+ .option("--from <timestamp>", "Capture observations from this ISO timestamp")
134
+ .option("--to <timestamp>", "Capture observations through this ISO timestamp")
135
+ .option("--days <n>", "Managed artifact retention in days")
136
+ .action((opts: { finding?: string; from?: string; to?: string; days?: string }) => {
137
+ run(emit, () => {
138
+ const repoRoot = requireRepoRoot(context);
139
+ const captured = captureDiagnosticBundle(repoRoot, {
140
+ findingId: opts.finding,
141
+ startAt: opts.from,
142
+ endAt: opts.to,
143
+ retentionDays: opts.days === undefined ? undefined : parseDays(opts.days),
144
+ actor: currentActor(repoRoot),
145
+ });
146
+ emit.data({
147
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
148
+ kind: "diagnostic_bundle_capture",
149
+ bundle: {
150
+ path: captured.path,
151
+ artifact_id: captured.manifest.artifact_id,
152
+ captured_at: captured.manifest.captured_at,
153
+ finding_id: captured.manifest.finding_id,
154
+ summary: captured.summary,
155
+ },
156
+ });
157
+ });
158
+ });
159
+
160
+ root
161
+ .command("replay <artifact-ref>")
162
+ .description("Replay a frozen bundle without reading live supervisor state.")
163
+ .action((ref: string) => {
164
+ run(emit, () => {
165
+ emit.data({
166
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
167
+ kind: "diagnostic_bundle_replay",
168
+ replay: replayDiagnosticBundle(requireRepoRoot(context), ref),
169
+ });
170
+ });
171
+ });
172
+
173
+ root
174
+ .command("compare <before> <after>")
175
+ .description("Compare two frozen bundles without reading live supervisor state.")
176
+ .option("--json", "Emit structured JSON")
177
+ .action((before: string, after: string, opts: { json?: boolean }) => {
178
+ run(emit, () => {
179
+ const comparison = compareDiagnosticBundles(requireRepoRoot(context), before, after);
180
+ if (opts.json) {
181
+ emit.config({ format: "json" });
182
+ emit.data({
183
+ schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
184
+ kind: "diagnostic_bundle_comparison",
185
+ comparison,
186
+ });
187
+ return;
188
+ }
189
+ emit.text(renderDiagnosticBundleComparison(comparison));
190
+ });
191
+ });
192
+ }
193
+
194
+ function liveFindingsCapability(repoRoot: string, reportPresent: boolean): SupervisorCapability {
195
+ if (!reportPresent) {
196
+ return {
197
+ source_kind: "supervisor.findings",
198
+ state: "unsupported",
199
+ reason_code: "source_missing",
200
+ };
201
+ }
202
+ const status = readSupervisorStatus(repoRoot);
203
+ if (status.running) return { source_kind: "supervisor.findings", state: "supported" };
204
+ return {
205
+ source_kind: "supervisor.findings",
206
+ state: "expired",
207
+ reason_code: status.stale
208
+ ? "supervisor_status_stale"
209
+ : status.record
210
+ ? "supervisor_not_running"
211
+ : "supervisor_status_missing",
212
+ };
213
+ }
214
+
215
+ function findFinding(repoRoot: string, id: string): SupervisorFinding | undefined {
216
+ const report = readSupervisorFindings(repoRoot);
217
+ return mergeFindings(report?.active ?? [], report?.transitions ?? []).find(
218
+ (finding) => finding.id === id,
219
+ );
220
+ }
221
+
222
+ function mergeFindings(
223
+ active: readonly SupervisorFinding[],
224
+ transitions: readonly SupervisorFinding[],
225
+ ): SupervisorFinding[] {
226
+ const byId = new Map<string, SupervisorFinding>();
227
+ for (const finding of transitions) byId.set(finding.id, finding);
228
+ for (const finding of active) byId.set(finding.id, finding);
229
+ return [...byId.values()].sort(
230
+ (left, right) =>
231
+ Date.parse(right.observed_at) - Date.parse(left.observed_at) ||
232
+ left.id.localeCompare(right.id),
233
+ );
234
+ }
235
+
236
+ function currentActor(repoRoot: string): ArtifactActor | undefined {
237
+ const instanceId = resolveOwner();
238
+ if (!instanceId) return undefined;
239
+ const heartbeat = readLiveCoordinationRow(repoRoot, instanceId);
240
+ return {
241
+ instance_id: instanceId,
242
+ session_id: heartbeat?.session_id,
243
+ name: heartbeat?.name,
244
+ };
245
+ }
246
+
247
+ function requireRepoRoot(context?: HarneryProgramContext): string {
248
+ const root = context?.repoRoot ?? monorepoRoot();
249
+ if (!root) throw new Error("not inside a Git repository");
250
+ return root;
251
+ }
252
+
253
+ function parseDays(value: string): number {
254
+ const parsed = Number(value);
255
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 3_650) {
256
+ throw new Error("--days must be between 1 and 3650");
257
+ }
258
+ return parsed;
259
+ }
260
+
261
+ function run(emit: EmitContext, fn: () => void): void {
262
+ try {
263
+ fn();
264
+ } catch (error) {
265
+ emit.error({
266
+ code: "diagnostics_failed",
267
+ message: error instanceof Error ? error.message : String(error),
268
+ });
269
+ emit.setExitCode(1);
270
+ }
271
+ }
@@ -4,6 +4,7 @@ import type { Command } from "commander";
4
4
  import type { EmitContext, HarneryProgramContext } from "../commander.ts";
5
5
  import { resolveCoordRoot } from "../core/agents/coord-client.ts";
6
6
  import { DEFAULT_WEB_PORT, resolveWebPort } from "../core/config.ts";
7
+ import { coordRootId, localCoordRootIdUrl } from "../lib/coord-root-id.ts";
7
8
  import { localFilesOriginUrl, localFileViewerUrl } from "../lib/local-file-url.ts";
8
9
 
9
10
  export { FILES_ORIGIN_HOST } from "../lib/local-file-url.ts";
@@ -22,6 +23,92 @@ export interface LocalFileUrl {
22
23
  mode: Exclude<LocalFileUrlMode, "auto">;
23
24
  }
24
25
 
26
+ export type DashboardRootErrorCode =
27
+ | "dashboard_unreachable"
28
+ | "dashboard_identity_unavailable"
29
+ | "dashboard_root_mismatch";
30
+
31
+ export class DashboardRootError extends Error {
32
+ constructor(
33
+ readonly code: DashboardRootErrorCode,
34
+ message: string,
35
+ readonly hint: string,
36
+ ) {
37
+ super(message);
38
+ this.name = "DashboardRootError";
39
+ }
40
+ }
41
+
42
+ interface DashboardRootPayload {
43
+ root_id?: unknown;
44
+ }
45
+
46
+ export type DashboardRootFetch = (
47
+ input: string | URL | Request,
48
+ init?: RequestInit,
49
+ ) => Promise<Response>;
50
+
51
+ /**
52
+ * Prove that the dashboard on the selected port serves the same canonical
53
+ * coordination root used to mint the file URL.
54
+ */
55
+ export async function verifyDashboardRoot(
56
+ coordRoot: string,
57
+ port: number,
58
+ fetchImpl: DashboardRootFetch = fetch,
59
+ ): Promise<void> {
60
+ const url = localCoordRootIdUrl(port);
61
+ let response: Response;
62
+ try {
63
+ response = await fetchImpl(url, {
64
+ headers: { accept: "application/json" },
65
+ signal: AbortSignal.timeout(5_000),
66
+ });
67
+ } catch {
68
+ throw new DashboardRootError(
69
+ "dashboard_unreachable",
70
+ `dashboard is not reachable at http://localhost:${port}`,
71
+ "Start the dashboard for this repository, then retry.",
72
+ );
73
+ }
74
+
75
+ if (!response.ok) {
76
+ throw new DashboardRootError(
77
+ "dashboard_identity_unavailable",
78
+ `dashboard at http://localhost:${port} cannot report its repository identity (HTTP ${response.status})`,
79
+ "Update and restart the dashboard, then retry.",
80
+ );
81
+ }
82
+
83
+ let payload: DashboardRootPayload;
84
+ try {
85
+ payload = (await response.json()) as DashboardRootPayload;
86
+ } catch {
87
+ throw new DashboardRootError(
88
+ "dashboard_identity_unavailable",
89
+ `dashboard at http://localhost:${port} returned an invalid repository identity`,
90
+ "Update and restart the dashboard, then retry.",
91
+ );
92
+ }
93
+
94
+ if (typeof payload.root_id !== "string") {
95
+ throw new DashboardRootError(
96
+ "dashboard_identity_unavailable",
97
+ `dashboard at http://localhost:${port} returned an invalid repository identity`,
98
+ "Update and restart the dashboard, then retry.",
99
+ );
100
+ }
101
+
102
+ const expected = coordRootId(coordRoot);
103
+ if (payload.root_id !== expected) {
104
+ throw new DashboardRootError(
105
+ "dashboard_root_mismatch",
106
+ `dashboard at http://localhost:${port} serves a different repository`,
107
+ "Stop that dashboard and start this repository's dashboard, then retry.",
108
+ );
109
+ }
110
+ }
111
+
25
112
  function isOutsideRoot(relPath: string): boolean {
26
113
  return relPath === "" || relPath === ".." || relPath.startsWith(`..${path.sep}`);
27
114
  }
@@ -92,10 +179,17 @@ export function registerFilesCommand(
92
179
  .option("--coord-root <dir>", "Override the coord root")
93
180
  .option("--raw", "Use the isolated files origin, regardless of extension")
94
181
  .option("--viewer", "Use the dashboard file viewer, regardless of extension")
182
+ .option("--no-verify", "Mint without checking the running dashboard's repository identity")
95
183
  .action(
96
- (
184
+ async (
97
185
  filePath: string,
98
- opts: { port?: string; coordRoot?: string; raw?: boolean; viewer?: boolean },
186
+ opts: {
187
+ port?: string;
188
+ coordRoot?: string;
189
+ raw?: boolean;
190
+ viewer?: boolean;
191
+ verify?: boolean;
192
+ },
99
193
  ) => {
100
194
  if (opts.raw && opts.viewer) {
101
195
  emit.error({
@@ -129,8 +223,14 @@ export function registerFilesCommand(
129
223
  port,
130
224
  mode: opts.raw ? "raw" : opts.viewer ? "viewer" : "auto",
131
225
  });
226
+ if (opts.verify !== false) await verifyDashboardRoot(coordRoot, port);
132
227
  emit.text(`${result.url}\n`);
133
228
  } catch (error) {
229
+ if (error instanceof DashboardRootError) {
230
+ emit.error({ code: error.code, message: error.message, hint: error.hint });
231
+ emit.setExitCode(1);
232
+ return;
233
+ }
134
234
  emit.error({
135
235
  code: "invalid_local_file",
136
236
  message: error instanceof Error ? error.message : "invalid local file path",
@@ -62,6 +62,7 @@ interface CreateOpts {
62
62
  interface RunOpts {
63
63
  adapter?: string;
64
64
  cwd?: string;
65
+ observePressure?: boolean;
65
66
  subscriptionOnly?: boolean;
66
67
  allowApiBilling?: boolean;
67
68
  policy?: string;
@@ -438,6 +439,10 @@ function addServiceOptions(command: Command): Command {
438
439
  .option("--error-backoff-max-ms <n>", "Maximum service-error backoff (default 300000)")
439
440
  .option("--adapter <name>", "Fallback adapter for agent calls without a specialist")
440
441
  .option("--cwd <dir>", "Working directory for child agents")
442
+ .option(
443
+ "--observe-pressure",
444
+ "Record observer-only local pressure before each workflow's first real child dispatch",
445
+ )
441
446
  .option("--subscription-only", "Require stored adapter-login billing")
442
447
  .option("--allow-api-billing", "Permit API-key override billing")
443
448
  .option("--policy <file>", "Host policy JSON/JSONC")
@@ -596,6 +601,9 @@ function registerRunCommand(
596
601
  spawners: registry.spawners(),
597
602
  defaultAdapter: opts.adapter,
598
603
  cwd: opts.cwd,
604
+ diagnosticAdmission: opts.observePressure
605
+ ? { schema_version: 1, mode: "shadow" }
606
+ : undefined,
599
607
  subscriptionOnly:
600
608
  opts.subscriptionOnly === true ? true : workflowSubscriptionOnly(coordRoot),
601
609
  allowApiBilling: opts.allowApiBilling,
@@ -5,13 +5,16 @@ import { resolve } from "node:path";
5
5
  import type { Command } from "commander";
6
6
  import type { EmitContext, HarneryProgramContext } from "../commander.ts";
7
7
  import {
8
+ compressSealedLegacyV1Segments,
8
9
  inventoryLegacyV1Segments,
9
10
  streamLegacyV1Rows,
10
11
  verifyLegacyV1HardFence,
11
12
  writeLegacyV1Canary,
12
13
  } from "../core/events/legacy-storage/index.ts";
13
14
  import {
15
+ cleanEventV3Archives,
14
16
  initializeEventLedgerV3,
17
+ inventoryEventV3Archives,
15
18
  readEventV3ControlState,
16
19
  recoverInvalidEventLedgerV3,
17
20
  sha256V3,
@@ -59,6 +62,39 @@ export function registerLedgerV3Command(
59
62
  }
60
63
  });
61
64
 
65
+ const archives = command
66
+ .command("archives")
67
+ .description("Inventory and enforce bounded retention for complete closed V3 epochs");
68
+
69
+ archives
70
+ .command("list")
71
+ .description("Show closed epochs, retention classifications, and planned actions")
72
+ .option("--root <path>", "Explicit coordination root")
73
+ .action((options: { root?: string }) => {
74
+ try {
75
+ const rows = inventoryEventV3Archives(resolve(options.root ?? coordRoot(context)));
76
+ emit.data({ rows, meta: summarizeArchiveRows(rows) });
77
+ } catch (error) {
78
+ emitFailure(emit, "ledger_v3_archive_inventory_failed", error);
79
+ }
80
+ });
81
+
82
+ archives
83
+ .command("clean")
84
+ .description("Preview closed-epoch deletion; pass --yes to execute the exact current plan")
85
+ .option("--root <path>", "Explicit coordination root")
86
+ .option("--yes", "Delete only epochs still classified expired or over-budget")
87
+ .action((options: { root?: string; yes?: boolean }) => {
88
+ try {
89
+ const rows = cleanEventV3Archives(resolve(options.root ?? coordRoot(context)), {
90
+ yes: options.yes,
91
+ });
92
+ emit.data({ rows, meta: summarizeArchiveRows(rows) });
93
+ } catch (error) {
94
+ emitFailure(emit, "ledger_v3_archive_cleanup_failed", error);
95
+ }
96
+ });
97
+
62
98
  command
63
99
  .command("initialize")
64
100
  .description("Create V3 control state, or archive and replace the current V3 epoch")
@@ -355,6 +391,32 @@ export function registerLedgerV3Command(
355
391
  }
356
392
  });
357
393
 
394
+ command
395
+ .command("legacy-compress")
396
+ .description("Preview verified gzip replacement of sealed non-terminal V1 shards")
397
+ .option("--root <path>", "Explicit coordination root")
398
+ .option("--yes", "Replace only unchanged shards after exact logical-row parity")
399
+ .action(async (options: { root?: string; yes?: boolean }) => {
400
+ try {
401
+ const rows = await compressSealedLegacyV1Segments(
402
+ resolve(options.root ?? coordRoot(context)),
403
+ { yes: options.yes },
404
+ );
405
+ emit.data({
406
+ rows,
407
+ meta: {
408
+ total: rows.length,
409
+ would_compress: rows.filter((row) => row.action === "would-compress").length,
410
+ compressed: rows.filter((row) => row.action === "compressed").length,
411
+ bytes_before: rows.reduce((sum, row) => sum + row.bytes_before, 0),
412
+ bytes_after: rows.reduce((sum, row) => sum + (row.bytes_after ?? row.bytes_before), 0),
413
+ },
414
+ });
415
+ } catch (error) {
416
+ emitFailure(emit, "ledger_v1_compression_failed", error);
417
+ }
418
+ });
419
+
358
420
  command
359
421
  .command("verify-legacy")
360
422
  .description("Stream and validate one loose or manifest-bound gzip legacy segment")
@@ -525,6 +587,26 @@ function sameSortedStrings(left: string[], right: string[]): boolean {
525
587
  );
526
588
  }
527
589
 
590
+ function summarizeArchiveRows(
591
+ rows: Array<{ classification: string; action: string; bytes: number | null }>,
592
+ ): Record<string, unknown> {
593
+ const classifications: Record<string, number> = {};
594
+ for (const row of rows) {
595
+ classifications[row.classification] = (classifications[row.classification] ?? 0) + 1;
596
+ }
597
+ return {
598
+ total: rows.length,
599
+ bytes: rows.reduce((sum, row) => sum + (row.bytes ?? 0), 0),
600
+ classifications,
601
+ would_delete: rows.filter((row) => row.action === "would-delete").length,
602
+ would_delete_bytes: rows.reduce(
603
+ (sum, row) => sum + (row.action === "would-delete" ? (row.bytes ?? 0) : 0),
604
+ 0,
605
+ ),
606
+ deleted: rows.filter((row) => row.action === "deleted").length,
607
+ };
608
+ }
609
+
528
610
  function emitFailure(emit: EmitContext, code: string, error: unknown): never {
529
611
  emit.error({ code, message: error instanceof Error ? error.message : String(error) });
530
612
  throw error;