pi-harness-runtime 0.9.25 → 1.1.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 (350) hide show
  1. package/CHANGELOG.md +49 -7
  2. package/README.md +173 -51
  3. package/harness/auto-compact.ts +1 -1
  4. package/harness/autonomous-review.ts +88 -0
  5. package/harness/context-compact-orchestrator.ts +23 -23
  6. package/harness/context-window-manager.ts +5 -5
  7. package/harness/continue-prompt.ts +3 -3
  8. package/harness/e2e/minimax-quota-scraper.ts +5 -5
  9. package/harness/e2e/openai-quota-scraper.ts +13 -8
  10. package/harness/forked-summarizer.ts +4 -4
  11. package/harness/herdr-agents.ts +91 -0
  12. package/harness/index.ts +5 -5
  13. package/harness/loop-code-agent.ts +284 -0
  14. package/harness/loop-controller.ts +201 -0
  15. package/harness/loop-review-agent.ts +295 -0
  16. package/harness/loop-runtime.ts +10 -10
  17. package/harness/loop-types.ts +118 -0
  18. package/harness/notification-events.ts +3 -3
  19. package/harness/output-limit-handler.ts +1 -1
  20. package/harness/partial-recovery.ts +3 -3
  21. package/harness/session-memory.ts +5 -5
  22. package/index.ts +139 -34
  23. package/package.json +1 -1
  24. package/packages/a2a-adapter/package.json +1 -1
  25. package/packages/a2a-adapter/src/types.ts +5 -5
  26. package/packages/auth/package.json +1 -1
  27. package/packages/autonomous-refactor/package.json +1 -1
  28. package/packages/autonomous-runtime/dist/inbox.d.ts +3 -3
  29. package/packages/autonomous-runtime/dist/inbox.js +12 -12
  30. package/packages/autonomous-runtime/dist/lease.js +14 -14
  31. package/packages/autonomous-runtime/dist/types.js +1 -1
  32. package/packages/autonomous-runtime/package.json +1 -1
  33. package/packages/autonomous-runtime/src/inbox.ts +12 -12
  34. package/packages/autonomous-runtime/src/lease.ts +15 -15
  35. package/packages/autonomous-runtime/src/types.ts +13 -13
  36. package/packages/autonomous-runtime/tsconfig.tsbuildinfo +1 -1
  37. package/packages/cache-strategy/package.json +1 -1
  38. package/packages/capability-registry/dist/types.js +1 -1
  39. package/packages/capability-registry/package.json +1 -1
  40. package/packages/capability-registry/src/types.ts +5 -5
  41. package/packages/checkpoint/README.md +6 -6
  42. package/packages/checkpoint/dist/differ.js +2 -2
  43. package/packages/checkpoint/dist/engine.js +3 -3
  44. package/packages/checkpoint/dist/index.js +5 -5
  45. package/packages/checkpoint/dist/storage.js +3 -3
  46. package/packages/checkpoint/dist/types.js +1 -1
  47. package/packages/checkpoint/package.json +1 -1
  48. package/packages/checkpoint/src/differ.ts +2 -2
  49. package/packages/checkpoint/src/engine.ts +3 -3
  50. package/packages/checkpoint/src/index.ts +5 -5
  51. package/packages/checkpoint/src/storage.ts +3 -3
  52. package/packages/checkpoint/src/types.ts +10 -10
  53. package/packages/checkpoint/tsconfig.tsbuildinfo +1 -1
  54. package/packages/cli-plugin-sdk/package.json +1 -1
  55. package/packages/clipboard/dist/src/clipboard.d.ts.map +1 -1
  56. package/packages/clipboard/dist/src/clipboard.js +15 -7
  57. package/packages/clipboard/dist/src/clipboard.js.map +1 -1
  58. package/packages/clipboard/dist/src/copy-sync.d.ts +27 -0
  59. package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -0
  60. package/packages/clipboard/dist/src/copy-sync.js +170 -0
  61. package/packages/clipboard/dist/src/copy-sync.js.map +1 -0
  62. package/packages/clipboard/dist/src/gist-relay.d.ts +48 -0
  63. package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -0
  64. package/packages/clipboard/dist/src/gist-relay.js +186 -0
  65. package/packages/clipboard/dist/src/gist-relay.js.map +1 -0
  66. package/packages/clipboard/dist/src/github-login.d.ts +16 -0
  67. package/packages/clipboard/dist/src/github-login.d.ts.map +1 -0
  68. package/packages/clipboard/dist/src/github-login.js +69 -0
  69. package/packages/clipboard/dist/src/github-login.js.map +1 -0
  70. package/packages/clipboard/dist/src/index.d.ts +2 -0
  71. package/packages/clipboard/dist/src/index.d.ts.map +1 -1
  72. package/packages/clipboard/dist/src/index.js +2 -1
  73. package/packages/clipboard/dist/src/index.js.map +1 -1
  74. package/packages/clipboard/package.json +1 -1
  75. package/packages/clipboard/src/clipboard.ts +16 -9
  76. package/packages/clipboard/src/copy-sync.ts +201 -0
  77. package/packages/clipboard/src/gist-relay.ts +240 -0
  78. package/packages/clipboard/src/github-login.ts +107 -0
  79. package/packages/clipboard/src/index.ts +2 -0
  80. package/packages/clipboard/test/mojibake.test.mjs +143 -0
  81. package/packages/code-generation/README.md +13 -13
  82. package/packages/code-generation/dist/faker.js +4 -4
  83. package/packages/code-generation/dist/generator.js +3 -3
  84. package/packages/code-generation/dist/index.js +3 -3
  85. package/packages/code-generation/dist/templates/registry.js +1 -1
  86. package/packages/code-generation/dist/types.js +1 -1
  87. package/packages/code-generation/package.json +1 -1
  88. package/packages/code-generation/src/faker.ts +5 -5
  89. package/packages/code-generation/src/generator.ts +4 -4
  90. package/packages/code-generation/src/index.ts +3 -3
  91. package/packages/code-generation/src/templates/registry.ts +1 -1
  92. package/packages/code-generation/src/types.ts +10 -10
  93. package/packages/code-generation/tsconfig.tsbuildinfo +1 -1
  94. package/packages/code-review/README.md +13 -13
  95. package/packages/code-review/dist/engine.js +3 -3
  96. package/packages/code-review/dist/index.js +3 -3
  97. package/packages/code-review/dist/types.js +1 -1
  98. package/packages/code-review/package.json +1 -1
  99. package/packages/code-review/src/engine.ts +3 -3
  100. package/packages/code-review/src/index.ts +3 -3
  101. package/packages/code-review/src/types.ts +10 -10
  102. package/packages/code-review/tsconfig.tsbuildinfo +1 -1
  103. package/packages/codex-adapter/package.json +1 -1
  104. package/packages/context-compiler/dist/compiler.js +1 -1
  105. package/packages/context-compiler/package.json +1 -1
  106. package/packages/context-compiler/src/compiler.ts +1 -1
  107. package/packages/context-compiler/src/types.ts +11 -11
  108. package/packages/context-compiler/test/context-compiler.test.ts +11 -11
  109. package/packages/context-discovery/package.json +1 -1
  110. package/packages/context-manager/package.json +1 -1
  111. package/packages/cookie-sanitizer/README.md +27 -27
  112. package/packages/cookie-sanitizer/package.json +1 -1
  113. package/packages/cost-optimizer/package.json +1 -1
  114. package/packages/cost-optimizer/src/types.ts +5 -5
  115. package/packages/dependency-analyzer/package.json +1 -1
  116. package/packages/django-plugin/dist/analyzer.js +7 -7
  117. package/packages/django-plugin/package.json +1 -1
  118. package/packages/django-plugin/src/analyzer.ts +7 -7
  119. package/packages/doc-generator/package.json +1 -1
  120. package/packages/evaluation-engine/dist/engine.js +9 -9
  121. package/packages/evaluation-engine/package.json +1 -1
  122. package/packages/evaluation-engine/src/engine.ts +9 -9
  123. package/packages/evaluation-engine/src/types.ts +3 -3
  124. package/packages/evaluation-runner/package.json +1 -1
  125. package/packages/event-bus/dist/src/herdr-bus.d.ts +121 -0
  126. package/packages/event-bus/dist/src/herdr-bus.d.ts.map +1 -0
  127. package/packages/event-bus/dist/src/herdr-bus.js +322 -0
  128. package/packages/event-bus/dist/src/herdr-bus.js.map +1 -0
  129. package/packages/event-bus/dist/src/index.d.ts +2 -0
  130. package/packages/event-bus/dist/src/index.d.ts.map +1 -1
  131. package/packages/event-bus/dist/src/index.js +1 -0
  132. package/packages/event-bus/dist/src/index.js.map +1 -1
  133. package/packages/event-bus/package.json +1 -1
  134. package/packages/event-bus/src/herdr-bus.ts +525 -0
  135. package/packages/event-bus/src/index.ts +29 -0
  136. package/packages/event-store/package.json +1 -1
  137. package/packages/experience-replay/dist/replay.js +7 -7
  138. package/packages/experience-replay/package.json +1 -1
  139. package/packages/experience-replay/src/replay.ts +7 -7
  140. package/packages/experience-replay/src/types.ts +5 -5
  141. package/packages/feedback-collector/package.json +1 -1
  142. package/packages/framework-detector/README.md +18 -18
  143. package/packages/framework-detector/dist/detector.js +3 -3
  144. package/packages/framework-detector/dist/index.js +3 -3
  145. package/packages/framework-detector/dist/signatures/registry.js +24 -24
  146. package/packages/framework-detector/dist/types.js +1 -1
  147. package/packages/framework-detector/package.json +1 -1
  148. package/packages/framework-detector/src/detector.ts +3 -3
  149. package/packages/framework-detector/src/index.ts +3 -3
  150. package/packages/framework-detector/src/signatures/registry.ts +24 -24
  151. package/packages/framework-detector/src/types.ts +7 -7
  152. package/packages/framework-detector/tsconfig.tsbuildinfo +1 -1
  153. package/packages/framework-plugin-sdk/README.md +23 -23
  154. package/packages/framework-plugin-sdk/dist/index.js +2 -2
  155. package/packages/framework-plugin-sdk/dist/manager.js +3 -3
  156. package/packages/framework-plugin-sdk/dist/types.js +2 -2
  157. package/packages/framework-plugin-sdk/package.json +1 -1
  158. package/packages/framework-plugin-sdk/src/index.ts +2 -2
  159. package/packages/framework-plugin-sdk/src/manager.ts +3 -3
  160. package/packages/framework-plugin-sdk/src/types.ts +10 -10
  161. package/packages/framework-plugin-sdk/tsconfig.tsbuildinfo +1 -1
  162. package/packages/frappe-plugin/dist/analyzer.js +8 -8
  163. package/packages/frappe-plugin/dist/types.js +1 -1
  164. package/packages/frappe-plugin/package.json +1 -1
  165. package/packages/frappe-plugin/src/analyzer.ts +8 -8
  166. package/packages/frappe-plugin/src/types.ts +10 -10
  167. package/packages/generic-web-plugin/dist/analyzer.js +5 -5
  168. package/packages/generic-web-plugin/package.json +1 -1
  169. package/packages/generic-web-plugin/src/analyzer.ts +5 -5
  170. package/packages/health-monitor/package.json +1 -1
  171. package/packages/health-monitor/src/monitor.ts +1 -1
  172. package/packages/intent-analyzer/package.json +1 -1
  173. package/packages/laravel-plugin/dist/analyzer.js +11 -11
  174. package/packages/laravel-plugin/package.json +1 -1
  175. package/packages/laravel-plugin/src/analyzer.ts +11 -11
  176. package/packages/learning-engine/dist/engine.js +5 -5
  177. package/packages/learning-engine/dist/types.js +1 -1
  178. package/packages/learning-engine/package.json +1 -1
  179. package/packages/learning-engine/src/engine.ts +5 -5
  180. package/packages/learning-engine/src/types.ts +6 -6
  181. package/packages/mcp-adapter/package.json +1 -1
  182. package/packages/memory-engine/dist/engine.js +8 -8
  183. package/packages/memory-engine/dist/types.js +2 -2
  184. package/packages/memory-engine/package.json +1 -1
  185. package/packages/memory-engine/src/engine.ts +8 -8
  186. package/packages/memory-engine/src/types.ts +9 -9
  187. package/packages/model-registry/package.json +1 -1
  188. package/packages/model-registry/src/types.ts +4 -4
  189. package/packages/nextjs-plugin/dist/analyzer.js +9 -9
  190. package/packages/nextjs-plugin/package.json +1 -1
  191. package/packages/nextjs-plugin/src/analyzer.ts +9 -9
  192. package/packages/notification/dist/notification-center.js +1 -1
  193. package/packages/notification/package.json +1 -1
  194. package/packages/notification/src/notification-center.ts +1 -1
  195. package/packages/observability/dist/alerts.js +2 -2
  196. package/packages/observability/dist/health.js +4 -4
  197. package/packages/observability/dist/index.js +6 -6
  198. package/packages/observability/dist/logger.js +7 -7
  199. package/packages/observability/dist/metrics.js +11 -11
  200. package/packages/observability/dist/tracer.js +3 -3
  201. package/packages/observability/dist/types.js +1 -1
  202. package/packages/observability/package.json +1 -1
  203. package/packages/observability/src/alerts.ts +2 -2
  204. package/packages/observability/src/health.ts +4 -4
  205. package/packages/observability/src/index.ts +6 -6
  206. package/packages/observability/src/logger.ts +7 -7
  207. package/packages/observability/src/metrics.ts +11 -11
  208. package/packages/observability/src/tracer.ts +3 -3
  209. package/packages/observability/src/types.ts +7 -7
  210. package/packages/observability/test/observability.test.ts +4 -4
  211. package/packages/observability/tsconfig.tsbuildinfo +1 -1
  212. package/packages/performance-optimizer/dist/optimizer.js +1 -1
  213. package/packages/performance-optimizer/package.json +1 -1
  214. package/packages/performance-optimizer/src/optimizer.ts +1 -1
  215. package/packages/performance-optimizer/src/types.ts +4 -4
  216. package/packages/privilege-broker/package.json +1 -1
  217. package/packages/project-analyzer/dist/analyzer.js +4 -4
  218. package/packages/project-analyzer/dist/index.js +8 -8
  219. package/packages/project-analyzer/dist/signals.js +3 -3
  220. package/packages/project-analyzer/dist/types.js +1 -1
  221. package/packages/project-analyzer/package.json +1 -1
  222. package/packages/project-analyzer/src/analyzer.ts +4 -4
  223. package/packages/project-analyzer/src/index.ts +9 -9
  224. package/packages/project-analyzer/src/signals.ts +4 -4
  225. package/packages/project-analyzer/src/types.ts +15 -15
  226. package/packages/project-analyzer/test/index.test.ts +17 -17
  227. package/packages/project-analyzer/tsconfig.tsbuildinfo +1 -1
  228. package/packages/projection-engine/package.json +1 -1
  229. package/packages/prompt-compiler/dist/section-builder.js +1 -1
  230. package/packages/prompt-compiler/dist/types.js +3 -3
  231. package/packages/prompt-compiler/package.json +1 -1
  232. package/packages/prompt-compiler/src/section-builder.ts +1 -1
  233. package/packages/prompt-compiler/src/types.ts +12 -12
  234. package/packages/prompt-compiler/test/prompt-compiler.test.ts +12 -12
  235. package/packages/prompt-versioning/package.json +1 -1
  236. package/packages/provider-adapter-sdk/dist/builder.js +1 -1
  237. package/packages/provider-adapter-sdk/dist/index.js +5 -5
  238. package/packages/provider-adapter-sdk/dist/registry.js +1 -1
  239. package/packages/provider-adapter-sdk/dist/tester.js +1 -1
  240. package/packages/provider-adapter-sdk/dist/types.js +1 -1
  241. package/packages/provider-adapter-sdk/package.json +1 -1
  242. package/packages/provider-adapter-sdk/src/builder.ts +1 -1
  243. package/packages/provider-adapter-sdk/src/index.ts +6 -6
  244. package/packages/provider-adapter-sdk/src/registry.ts +1 -1
  245. package/packages/provider-adapter-sdk/src/tester.ts +1 -1
  246. package/packages/provider-adapter-sdk/src/types.ts +5 -5
  247. package/packages/provider-adapter-sdk/tsconfig.tsbuildinfo +1 -1
  248. package/packages/provider-router/package.json +1 -1
  249. package/packages/provider-router/src/types.ts +4 -4
  250. package/packages/provider-selector/package.json +1 -1
  251. package/packages/providers/dist/deepagents-adapter.js +1 -1
  252. package/packages/providers/package.json +1 -1
  253. package/packages/providers/src/deepagents-adapter.ts +1 -1
  254. package/packages/quota-manager/package.json +1 -1
  255. package/packages/rate-limiter/package.json +1 -1
  256. package/packages/react-vite-plugin/dist/analyzer.js +7 -7
  257. package/packages/react-vite-plugin/package.json +1 -1
  258. package/packages/react-vite-plugin/src/analyzer.ts +7 -7
  259. package/packages/release-manager/package.json +1 -1
  260. package/packages/requirement-compiler/dist/compiler.js +17 -17
  261. package/packages/requirement-compiler/dist/types.js +2 -2
  262. package/packages/requirement-compiler/package.json +1 -1
  263. package/packages/requirement-compiler/src/compiler.ts +17 -17
  264. package/packages/requirement-compiler/src/types.ts +21 -21
  265. package/packages/requirement-compiler/test/requirement-compiler.test.ts +17 -17
  266. package/packages/runtime/dist/command-executor.js +1 -1
  267. package/packages/runtime/dist/local-runtime-agent.js +1 -1
  268. package/packages/runtime/dist/policy-engine.js +1 -1
  269. package/packages/runtime/dist/runtime-api.js +1 -1
  270. package/packages/runtime/dist/workspace-manager.js +1 -1
  271. package/packages/runtime/package.json +1 -1
  272. package/packages/runtime/src/command-executor.ts +1 -1
  273. package/packages/runtime/src/local-runtime-agent.ts +1 -1
  274. package/packages/runtime/src/policy-engine.ts +1 -1
  275. package/packages/runtime/src/runtime-api.ts +1 -1
  276. package/packages/runtime/src/workspace-manager.ts +1 -1
  277. package/packages/scheduler/package.json +1 -1
  278. package/packages/scheduler-adapter/package.json +1 -1
  279. package/packages/session/README.md +18 -18
  280. package/packages/session/dist/history.js +3 -3
  281. package/packages/session/dist/index.js +5 -5
  282. package/packages/session/dist/manager.js +3 -3
  283. package/packages/session/dist/policy.js +2 -2
  284. package/packages/session/dist/store.js +2 -2
  285. package/packages/session/dist/types.js +1 -1
  286. package/packages/session/package.json +1 -1
  287. package/packages/session/src/history.ts +3 -3
  288. package/packages/session/src/index.ts +5 -5
  289. package/packages/session/src/manager.ts +3 -3
  290. package/packages/session/src/policy.ts +4 -4
  291. package/packages/session/src/store.ts +3 -3
  292. package/packages/session/src/types.ts +10 -10
  293. package/packages/session/tsconfig.tsbuildinfo +1 -1
  294. package/packages/session-api/package.json +1 -1
  295. package/packages/session-export/package.json +1 -1
  296. package/packages/shared-context/package.json +1 -1
  297. package/packages/skill-registry/package.json +1 -1
  298. package/packages/skill-registry/src/types.ts +4 -4
  299. package/packages/sprint-planner/package.json +1 -1
  300. package/packages/subscription-engine/package.json +1 -1
  301. package/packages/task-compiler/dist/compiler.js +7 -7
  302. package/packages/task-compiler/dist/decompose.js +11 -11
  303. package/packages/task-compiler/dist/file-scope.js +6 -6
  304. package/packages/task-compiler/dist/graph.js +6 -6
  305. package/packages/task-compiler/dist/index.js +2 -2
  306. package/packages/task-compiler/dist/types.js +5 -5
  307. package/packages/task-compiler/dist/validate.js +2 -2
  308. package/packages/task-compiler/dist/verification.js +2 -2
  309. package/packages/task-compiler/package.json +1 -1
  310. package/packages/task-compiler/src/command-policy.ts +1 -1
  311. package/packages/task-compiler/src/compiler.ts +7 -7
  312. package/packages/task-compiler/src/decompose.ts +11 -11
  313. package/packages/task-compiler/src/file-scope.ts +6 -6
  314. package/packages/task-compiler/src/graph.ts +6 -6
  315. package/packages/task-compiler/src/index.ts +3 -3
  316. package/packages/task-compiler/src/types.ts +14 -14
  317. package/packages/task-compiler/src/validate.ts +2 -2
  318. package/packages/task-compiler/src/verification.ts +2 -2
  319. package/packages/task-compiler/test/task-compiler.test.ts +10 -10
  320. package/packages/test-data-generator/README.md +13 -13
  321. package/packages/test-data-generator/dist/faker-adapter.js +4 -4
  322. package/packages/test-data-generator/dist/generator.js +3 -3
  323. package/packages/test-data-generator/dist/index.js +3 -3
  324. package/packages/test-data-generator/dist/types.js +1 -1
  325. package/packages/test-data-generator/package.json +1 -1
  326. package/packages/test-data-generator/src/faker-adapter.ts +4 -4
  327. package/packages/test-data-generator/src/generator.ts +3 -3
  328. package/packages/test-data-generator/src/index.ts +3 -3
  329. package/packages/test-data-generator/src/types.ts +9 -9
  330. package/packages/test-data-generator/tsconfig.tsbuildinfo +1 -1
  331. package/packages/test-generator/package.json +1 -1
  332. package/packages/todo-bd-sync/package.json +22 -0
  333. package/packages/todo-bd-sync/src/detector.ts +150 -0
  334. package/packages/todo-bd-sync/src/extension.ts +101 -0
  335. package/packages/todo-bd-sync/src/index.ts +401 -0
  336. package/packages/todo-bd-sync/src/sync.ts +322 -0
  337. package/packages/todo-bd-sync/src/todo-reminder.ts +418 -0
  338. package/packages/todo-bd-sync/src/types.ts +137 -0
  339. package/packages/token-estimation/package.json +1 -1
  340. package/packages/token-optimizer/package.json +1 -1
  341. package/packages/tui/package.json +1 -1
  342. package/packages/types/package.json +1 -1
  343. package/packages/types/src/agent-worker-types.ts +5 -5
  344. package/packages/types/src/runtime-types.ts +12 -12
  345. package/packages/types/tsconfig.tsbuildinfo +1 -1
  346. package/packages/workflow-events/package.json +1 -1
  347. package/packages/workspace-scanner/package.json +1 -1
  348. package/packages/worktree/package.json +1 -1
  349. package/renderer.ts +7 -7
  350. package/skills/harness-runtime/SKILL.md +15 -15
@@ -22,7 +22,7 @@ import {
22
22
  roughMessagesTokens,
23
23
  } from "../packages/token-estimation/src/index.ts";
24
24
 
25
- // ─── Constants ────────────────────────────────────────────────────────────────
25
+ // --- Constants ----------------------------------------------------------------
26
26
 
27
27
  /** Proactive compact triggers at this many tokens before limit */
28
28
  export const AUTOCOMPACT_BUFFER_TOKENS = 13_000;
@@ -36,7 +36,7 @@ export const BLOCKING_THRESHOLD_BUFFER_TOKENS = 20_000;
36
36
  /** Maximum consecutive compact failures before circuit breaker */
37
37
  export const MAX_CONSECUTIVE_COMPACT_FAILURES = 3;
38
38
 
39
- // ─── Types ──────────────────────────────────────────────────────────────────
39
+ // --- Types ------------------------------------------------------------------
40
40
 
41
41
  export interface CompactThresholds {
42
42
  /** Trigger proactive compact at this utilization (0.85) */
@@ -60,7 +60,7 @@ export interface TokenEstimate {
60
60
  shouldBlock: boolean;
61
61
  }
62
62
 
63
- // ─── Context Window Manager ─────────────────────────────────────────────────
63
+ // --- Context Window Manager -------------------------------------------------
64
64
 
65
65
  export class ContextWindowManager {
66
66
  private stats: Map<string, ContextWindowStats> = new Map();
@@ -95,7 +95,7 @@ export class ContextWindowManager {
95
95
  }
96
96
  }
97
97
 
98
- // ─── Public API ───────────────────────────────────────────────────────────
98
+ // --- Public API -----------------------------------------------------------
99
99
 
100
100
  /**
101
101
  * Update context window stats after a request
@@ -399,7 +399,7 @@ export class ContextWindowManager {
399
399
  }
400
400
  }
401
401
 
402
- // ─── Microcompact Helpers ───────────────────────────────────────────────────
402
+ // --- Microcompact Helpers ---------------------------------------------------
403
403
 
404
404
  /**
405
405
  * Microcompact: prune old tool results based on time gap.
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type { CompactableMessage } from "../packages/types/src/runtime-types.js";
9
9
 
10
- // ─── Types ──────────────────────────────────────────────────────────────────
10
+ // --- Types ------------------------------------------------------------------
11
11
 
12
12
  export interface ContinueContext {
13
13
  /** Task identifier */
@@ -35,7 +35,7 @@ export interface MinimalContinueContext {
35
35
  recentMessages: CompactableMessage[];
36
36
  }
37
37
 
38
- // ─── Continue Prompt Generator ───────────────────────────────────────────────
38
+ // --- Continue Prompt Generator -----------------------------------------------
39
39
 
40
40
  export class ContinuePromptGenerator {
41
41
  /**
@@ -237,6 +237,6 @@ export class ContinuePromptGenerator {
237
237
  }
238
238
  }
239
239
 
240
- // ─── Singleton Instance ──────────────────────────────────────────────────────
240
+ // --- Singleton Instance ------------------------------------------------------
241
241
 
242
242
  export const continuePromptGenerator = new ContinuePromptGenerator();
@@ -387,7 +387,8 @@ export class MiniMaxQuotaScraper {
387
387
  } catch {
388
388
  const msg =
389
389
  "Playwright not installed. Install with: bun add playwright\nThen install browsers: bunx playwright install chromium";
390
- if (!this.config.quiet) console.error("[MiniMaxQuotaScraper] " + msg);
390
+ if (!this.config.quiet)
391
+ console.error("[DEBUG MiniMaxQuotaScraper] " + msg);
391
392
  throw new Error(msg);
392
393
  }
393
394
 
@@ -399,8 +400,7 @@ export class MiniMaxQuotaScraper {
399
400
 
400
401
  if (cookies.length === 0) {
401
402
  const dropHint = join(homedir(), ".pi-harness-runtime", "cookies");
402
- const msg =
403
- `No cookies found.
403
+ const msg = `No cookies found.
404
404
  Drop your platform.minimax.io cookies (Netscape or EditThisCookie JSON) into:
405
405
  ${dropHint}
406
406
  …or run: bun packages/auth/src/run-minimax-auth.ts auth`;
@@ -550,12 +550,12 @@ MiniMax Quota Scraper Setup Instructions
550
550
  3. Use the scraper:
551
551
  const scraper = new MiniMaxQuotaScraper();
552
552
  const quota = await scraper.scrape();
553
- console.log("5h usage:", quota.h5UsedPct, "%");
553
+ console.log("[DEBUG MiniMaxQuotaScraper] 5h usage:", quota.h5UsedPct, "%");
554
554
  `;
555
555
  }
556
556
  }
557
557
 
558
- // ─── Quota Manager Integration ───────────────────────────────────────────────
558
+ // --- Quota Manager Integration -----------------------------------------------
559
559
 
560
560
  /**
561
561
  * Quota Manager that periodically fetches MiniMax quota data
@@ -205,13 +205,14 @@ export class OpenAIQuotaScraper {
205
205
  Drop your chatgpt.com cookies (Netscape or EditThisCookie JSON) into:
206
206
  ${dropHint}
207
207
  Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
208
- if (!this.config.quiet) console.error(`[OpenAIQuotaScraper] ${msg}`);
208
+ if (!this.config.quiet)
209
+ console.error(`[DEBUG OpenAIQuotaScraper] ${msg}`);
209
210
  throw new Error(msg);
210
211
  }
211
212
 
212
213
  // Launch browser
213
214
  if (!this.config.quiet) {
214
- console.log("[OpenAIQuotaScraper] Launching browser...");
215
+ console.log("[DEBUG OpenAIQuotaScraper] Launching browser...");
215
216
  }
216
217
 
217
218
  const browser = await chromium.launch({
@@ -259,7 +260,9 @@ Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
259
260
  try {
260
261
  // Navigate to analytics page
261
262
  if (!this.config.quiet) {
262
- console.log("[OpenAIQuotaScraper] Navigating to analytics page...");
263
+ console.log(
264
+ "[DEBUG OpenAIQuotaScraper] Navigating to analytics page...",
265
+ );
263
266
  }
264
267
 
265
268
  await page.goto(ANALYTICS_URL, {
@@ -282,14 +285,16 @@ Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
282
285
  if (currentUrl.includes("login") || currentUrl.includes("auth")) {
283
286
  const msg =
284
287
  "OpenAI cookies are expired or insufficient. Please re-export cookies from chatgpt.com.";
285
- if (!this.config.quiet) console.error("[OpenAIQuotaScraper] " + msg);
288
+ if (!this.config.quiet)
289
+ console.error("[DEBUG OpenAIQuotaScraper] " + msg);
286
290
  throw new Error(msg);
287
291
  }
288
292
 
289
293
  // Parse the usage data from the captured API response
290
294
  if (!usageData) {
291
295
  const msg = "Failed to capture usage data from API";
292
- if (!this.config.quiet) console.error("[OpenAIQuotaScraper] " + msg);
296
+ if (!this.config.quiet)
297
+ console.error("[DEBUG OpenAIQuotaScraper] " + msg);
293
298
  throw new Error(msg);
294
299
  }
295
300
 
@@ -306,7 +311,7 @@ Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
306
311
 
307
312
  if (!this.config.quiet) {
308
313
  console.log(
309
- `[OpenAIQuotaScraper] Weekly usage: ${weeklyUsedPct}%, resets in ${weeklyResetsAt}`,
314
+ `[DEBUG OpenAIQuotaScraper] Weekly usage: ${weeklyUsedPct}%, resets in ${weeklyResetsAt}`,
310
315
  );
311
316
  }
312
317
 
@@ -349,7 +354,7 @@ Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
349
354
  if (!response.ok) {
350
355
  if (!this.config.quiet) {
351
356
  console.log(
352
- `[OpenAIQuotaScraper] Direct API failed: ${response.status}`,
357
+ `[DEBUG OpenAIQuotaScraper] Direct API failed: ${response.status}`,
353
358
  );
354
359
  }
355
360
  return null;
@@ -374,7 +379,7 @@ Then run: bun run packages/cookie-sanitizer/src/sync.ts`;
374
379
  } catch (error) {
375
380
  if (!this.config.quiet) {
376
381
  console.warn(
377
- "[OpenAIQuotaScraper] Direct API error:",
382
+ "[DEBUG OpenAIQuotaScraper] Direct API error:",
378
383
  error instanceof Error ? error.message : String(error),
379
384
  );
380
385
  }
@@ -12,7 +12,7 @@ import type {
12
12
  InvokeResult,
13
13
  } from "../packages/types/src/runtime-types.js";
14
14
 
15
- // ─── Types ──────────────────────────────────────────────────────────────────
15
+ // --- Types ------------------------------------------------------------------
16
16
 
17
17
  export interface SummarizerConfig {
18
18
  /** Model to use for summarization */
@@ -43,7 +43,7 @@ export interface InvokeOptions {
43
43
  systemPrompt?: string;
44
44
  }
45
45
 
46
- // ─── Default System Prompt ───────────────────────────────────────────────────
46
+ // --- Default System Prompt ---------------------------------------------------
47
47
 
48
48
  const DEFAULT_SUMMARY_SYSTEM = `You are a conversation summarizer. Your task is to create concise, accurate summaries that preserve essential context for continuing a conversation.
49
49
 
@@ -70,7 +70,7 @@ const DECISIONS_FOCUS = `Focus specifically on:
70
70
  4. What constraints or requirements influenced the decisions?
71
71
  5. Any lessons learned or trade-offs considered?`;
72
72
 
73
- // ─── Forked Summarizer ──────────────────────────────────────────────────────
73
+ // --- Forked Summarizer ------------------------------------------------------
74
74
 
75
75
  export class ForkedSummarizer {
76
76
  private readonly config: SummarizerConfig;
@@ -283,7 +283,7 @@ Do not include verbatim tool results — summarize their outcomes instead.`;
283
283
  }
284
284
  }
285
285
 
286
- // ─── Convenience Factory ────────────────────────────────────────────────────
286
+ // --- Convenience Factory ----------------------------------------------------
287
287
 
288
288
  /**
289
289
  * Create a ForkedSummarizer with default config
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Herdr Agent Commands
3
+ */
4
+ import { existsSync, readdirSync, readFileSync } from "fs";
5
+ import { join } from "path";
6
+ import {
7
+ createHerdrBus,
8
+ getHerdrWorkspacePaths,
9
+ ensureHerdrWorkspace,
10
+ } from "../packages/event-bus/src/herdr-bus.js";
11
+
12
+ async function startReviewAgent(): Promise<void> {
13
+ console.log("[herdr:review] Starting review agent...");
14
+ const bus = createHerdrBus("review-agent");
15
+ ensureHerdrWorkspace();
16
+ bus.subscribe("code.written");
17
+ bus.startPolling(async (payload) => {
18
+ if (payload.topic === "code.written") {
19
+ const data = payload.data as { taskId: string; files: string[] };
20
+ console.log(
21
+ `[herdr:review] code.written: task=${data.taskId} files=${data.files.length}`,
22
+ );
23
+ for (const file of data.files ?? []) {
24
+ console.log(`[herdr:review] Reviewing: ${file}`);
25
+ }
26
+ }
27
+ });
28
+ console.log(`[herdr:review] Workspace: ${bus.getWorkspace()}`);
29
+ await new Promise(() => {});
30
+ }
31
+
32
+ async function startCodeAgent(): Promise<void> {
33
+ console.log("[herdr:code] Starting code agent...");
34
+ const bus = createHerdrBus("code-agent");
35
+ ensureHerdrWorkspace();
36
+ bus.subscribe("review.completed");
37
+ bus.startPolling(async (payload) => {
38
+ if (payload.topic === "review.completed") {
39
+ const data = payload.data as {
40
+ taskId: string;
41
+ reportFile: string;
42
+ status: string;
43
+ };
44
+ console.log(
45
+ `[herdr:code] Review done: task=${data.taskId} status=${data.status} report=${data.reportFile}`,
46
+ );
47
+ }
48
+ });
49
+ console.log(`[herdr:code] Workspace: ${bus.getWorkspace()}`);
50
+ await new Promise(() => {});
51
+ }
52
+
53
+ function showStatus(): void {
54
+ const paths = getHerdrWorkspacePaths();
55
+ console.log(`Workspace: ${paths.root}`);
56
+ const evPath = join(paths.root, "events.jsonl");
57
+ if (existsSync(evPath)) {
58
+ const lines = readFileSync(evPath, "utf-8")
59
+ .split("\n")
60
+ .filter(Boolean)
61
+ .slice(-10);
62
+ console.log("Recent events:");
63
+ for (const line of lines) {
64
+ try {
65
+ const { topic, ts } = JSON.parse(line);
66
+ console.log(` ${ts} ${topic}`);
67
+ } catch {
68
+ /* skip */
69
+ }
70
+ }
71
+ }
72
+ if (existsSync(paths.subscriptions)) {
73
+ const agents = readdirSync(paths.subscriptions);
74
+ console.log(`Active agents: ${agents.join(", ") || "none"}`);
75
+ }
76
+ }
77
+
78
+ const [command] = process.argv.slice(2);
79
+ switch (command) {
80
+ case "review":
81
+ startReviewAgent().catch(console.error);
82
+ break;
83
+ case "code":
84
+ startCodeAgent().catch(console.error);
85
+ break;
86
+ case "status":
87
+ showStatus();
88
+ break;
89
+ default:
90
+ console.log("Usage: bun harness/herdr-agents.ts <review|code|status>");
91
+ }
package/harness/index.ts CHANGED
@@ -178,7 +178,7 @@ export type { QuotaStatus, QuotaDisplayConfig } from "./e2e/quota-status.js";
178
178
  // Project Detector
179
179
  export { ProjectDetector } from "./project-detector/detector.js";
180
180
 
181
- // ─── RFC-0056: Performance Optimizer ────────────────────────────────────────
181
+ // --- RFC-0056: Performance Optimizer ----------------------------------------
182
182
  export {
183
183
  PerformanceOptimizer,
184
184
  createPerformanceOptimizer,
@@ -189,7 +189,7 @@ export type {
189
189
  PerformanceRecommendation,
190
190
  } from "../packages/performance-optimizer/src/types.js";
191
191
 
192
- // ─── RFC-0057: Evaluation Engine ───────────────────────────────────────────
192
+ // --- RFC-0057: Evaluation Engine -------------------------------------------
193
193
  export {
194
194
  EvaluationEngine,
195
195
  createEvaluationEngine,
@@ -200,7 +200,7 @@ export type {
200
200
  EvaluationFinding,
201
201
  } from "../packages/evaluation-engine/src/types.js";
202
202
 
203
- // ─── RFC-0058: Learning Engine ─────────────────────────────────────────────
203
+ // --- RFC-0058: Learning Engine ---------------------------------------------
204
204
  export {
205
205
  LearningEngine,
206
206
  createLearningEngine,
@@ -210,7 +210,7 @@ export type {
210
210
  LearnedExperience,
211
211
  } from "../packages/learning-engine/src/types.js";
212
212
 
213
- // ─── RFC-0059: Experience Replay ──────────────────────────────────────────
213
+ // --- RFC-0059: Experience Replay ------------------------------------------
214
214
  export {
215
215
  ExperienceReplay,
216
216
  createExperienceReplay,
@@ -220,7 +220,7 @@ export type {
220
220
  ReplayResult,
221
221
  } from "../packages/experience-replay/src/types.js";
222
222
 
223
- // ─── RFC-0060: Memory Engine ───────────────────────────────────────────────
223
+ // --- RFC-0060: Memory Engine -----------------------------------------------
224
224
  export {
225
225
  MemoryEngine,
226
226
  createMemoryEngine,
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Loop Code Agent (Minimax) — coordinates via blackboard.
3
+ *
4
+ * Polls blackboard for nextAction where agentType="code".
5
+ * Writes code, updates node status, sets nextAction for review-agent.
6
+ *
7
+ * Usage: bun harness/loop-code-agent.ts
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
11
+ import { join } from "path";
12
+ import {
13
+ createHerdrBus,
14
+ ensureHerdrWorkspace,
15
+ } from "../packages/event-bus/src/herdr-bus.js";
16
+ import { SharedBlackboard } from "./blackboard.js";
17
+ import type { LoopNextAction, LoopAgentReport } from "./loop-types.js";
18
+ import type { BlackboardRecord } from "../packages/types/src/runtime-types.js";
19
+
20
+ const AGENT_ID = "code-agent";
21
+ const AGENT_TYPE = "code";
22
+ const POLL_MS = 1000;
23
+
24
+ async function main(): Promise<void> {
25
+ console.log(`[${AGENT_ID}] Starting...`);
26
+ const paths = ensureHerdrWorkspace();
27
+
28
+ // Find active loop config
29
+ const loopId = await findActiveLoop(paths.root);
30
+ if (!loopId) {
31
+ console.log(`[${AGENT_ID}] No active loop found. Waiting...`);
32
+ }
33
+
34
+ const rootDir = paths.root;
35
+ const blackboard = new SharedBlackboard(loopId ?? "no-loop", rootDir);
36
+ blackboard.load();
37
+ blackboard.registerAgent(AGENT_ID, "Code Agent", "minimax", "minimax");
38
+ blackboard.updateAgentStatus(AGENT_ID, "idle");
39
+ console.log(`[${AGENT_ID}] Registered. Blackboard: ${blackboard.getPath()}`);
40
+
41
+ while (true) {
42
+ await sleep(POLL_MS);
43
+
44
+ blackboard.load();
45
+ const record = blackboard.getRecord();
46
+ if (!record) continue;
47
+
48
+ // Check for early exit
49
+ const earlyExit = checkEarlyExit(record);
50
+ if (earlyExit) {
51
+ console.log(`[${AGENT_ID}] Early exit: ${earlyExit}`);
52
+ blackboard.updateAgentStatus(AGENT_ID, "idle");
53
+ break;
54
+ }
55
+
56
+ // Check if this loop is ours (loopId matches)
57
+ if (record.jobId !== loopId) continue;
58
+
59
+ // Check nextAction for code agent
60
+ const action = parseNextAction(record.nextAction);
61
+ if (!action || action.agentType !== AGENT_TYPE) continue;
62
+
63
+ // Claim the task
64
+ const locked = blackboard.acquireLock(action.taskId, AGENT_ID);
65
+ if (!locked) continue;
66
+
67
+ blackboard.updateAgentStatus(AGENT_ID, "working", action.taskId);
68
+ console.log(`[${AGENT_ID}] Claimed: ${action.taskId}`);
69
+
70
+ // Do the work
71
+ const result = await writeCode(action, paths, record.jobId);
72
+
73
+ // Update task status
74
+ updateTaskStatus(blackboard, record, action.taskId, "done", result.files);
75
+
76
+ // Set nextAction for review agent
77
+ const nextTaskId = getNextReviewTask(record, action.iteration);
78
+ if (nextTaskId) {
79
+ blackboard.setNextAction(
80
+ encodeNextAction({
81
+ taskId: nextTaskId,
82
+ agentType: "review",
83
+ iteration: getReviewIteration(nextTaskId),
84
+ prompt: action.prompt,
85
+ codeFiles: result.files,
86
+ }),
87
+ );
88
+ console.log(`[${AGENT_ID}] -> nextAction: review ${nextTaskId}`);
89
+ } else {
90
+ // No review needed — go to report
91
+ const writeDone = getNextWriteTask(record, action.iteration);
92
+ if (writeDone) {
93
+ blackboard.setNextAction(
94
+ encodeNextAction({
95
+ taskId: writeDone,
96
+ agentType: "code",
97
+ iteration: getWriteIteration(writeDone),
98
+ prompt: action.prompt,
99
+ }),
100
+ );
101
+ console.log(`[${AGENT_ID}] -> nextAction: code ${writeDone}`);
102
+ } else {
103
+ // All done — go to report
104
+ blackboard.setNextAction(
105
+ encodeNextAction({
106
+ taskId: "report",
107
+ agentType: "review",
108
+ iteration: 0,
109
+ }),
110
+ );
111
+ console.log(`[${AGENT_ID}] -> nextAction: report`);
112
+ }
113
+ }
114
+
115
+ // Release lock and report
116
+ blackboard.releaseLock(action.taskId, AGENT_ID);
117
+ blackboard.writeReport({
118
+ agentId: AGENT_ID,
119
+ taskId: action.taskId,
120
+ status: "success",
121
+ files: result.files,
122
+ } satisfies LoopAgentReport);
123
+ blackboard.updateAgentStatus(AGENT_ID, "idle");
124
+ }
125
+ }
126
+
127
+ async function writeCode(
128
+ action: LoopNextAction,
129
+ paths: { code: string },
130
+ loopId: string,
131
+ ): Promise<{ files: string[] }> {
132
+ const iteration = action.iteration;
133
+ const outputDir = join(paths.code, loopId, `iteration-${iteration}`);
134
+ mkdirSync(outputDir, { recursive: true });
135
+
136
+ const codeFile = join(outputDir, `index.ts`);
137
+ // TODO: Replace with actual Minimax call
138
+ const code = `// Iteration ${iteration} — ${action.prompt}
139
+ // TODO: Replace with actual Minimax code generation
140
+ export const iteration = ${iteration};
141
+ export const prompt = ${JSON.stringify(action.prompt)};
142
+ `;
143
+ writeFileSync(codeFile, code);
144
+ console.log(`[${AGENT_ID}] Wrote: ${codeFile}`);
145
+ return { files: [codeFile] };
146
+ }
147
+
148
+ // ─── Helpers ────────────────────────────────────────────────────────────────
149
+
150
+ async function findActiveLoop(rootDir: string): Promise<string | null> {
151
+ const { readdirSync, existsSync } = await import("fs");
152
+ try {
153
+ const files = readdirSync(rootDir).filter(
154
+ (f) => f.startsWith("loop-") && f.endsWith(".config.json"),
155
+ );
156
+ // Find most recent config that has an active blackboard
157
+ for (const configFile of files.slice(-1)) {
158
+ const config = JSON.parse(
159
+ readFileSync(join(rootDir, configFile), "utf-8"),
160
+ );
161
+ const bbPath = join(rootDir, "jobs", config.loopId, "blackboard");
162
+ if (existsSync(bbPath)) return config.loopId;
163
+ }
164
+ } catch {
165
+ // no loop yet
166
+ }
167
+ return null;
168
+ }
169
+
170
+ function encodeNextAction(action: Partial<LoopNextAction>) {
171
+ return {
172
+ taskId: action.taskId,
173
+ instruction: "LOOP:" + JSON.stringify(action),
174
+ priority: "high" as const,
175
+ createdAt: new Date().toISOString(),
176
+ };
177
+ }
178
+
179
+ function parseNextAction(nextAction: unknown): LoopNextAction | null {
180
+ if (!nextAction) return null;
181
+ const a = nextAction as Record<string, unknown>;
182
+ if (!a.taskId) return null;
183
+ if (typeof a.instruction === "string" && a.instruction.startsWith("LOOP:")) {
184
+ try {
185
+ return JSON.parse(a.instruction.slice(5)) as LoopNextAction;
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+ return null;
191
+ }
192
+
193
+ function checkEarlyExit(record: BlackboardRecord): string | null {
194
+ const blocked = Object.values(record.tasks.nodes).find(
195
+ (n) => n.status === "blocked",
196
+ );
197
+ if (blocked) return `blocked at ${blocked.id}`;
198
+ const approved = Object.values(record.tasks.nodes).find(
199
+ (n) =>
200
+ n.status === "done" && (n as { result?: string }).result === "approved",
201
+ );
202
+ if (approved) return "approved";
203
+ return null;
204
+ }
205
+
206
+ function updateTaskStatus(
207
+ blackboard: SharedBlackboard,
208
+ record: BlackboardRecord,
209
+ taskId: string,
210
+ status: "done" | "running" | "blocked",
211
+ files?: string[],
212
+ ): void {
213
+ if (record.tasks.nodes[taskId]) {
214
+ (
215
+ record.tasks.nodes[taskId] as unknown as {
216
+ status: string;
217
+ result?: string;
218
+ }
219
+ ).status = status;
220
+ if (files)
221
+ (record.tasks.nodes[taskId] as unknown as { result?: string }).result =
222
+ files.join(", ");
223
+ (
224
+ record.tasks.nodes[taskId] as unknown as { updatedAt?: string }
225
+ ).updatedAt = new Date().toISOString();
226
+ blackboard.save();
227
+ }
228
+ }
229
+
230
+ function getNextReviewTask(
231
+ record: BlackboardRecord,
232
+ writeIteration: number,
233
+ ): string | null {
234
+ const reviewCount = Object.keys(record.tasks.nodes).filter((id) =>
235
+ id.startsWith("review-"),
236
+ ).length;
237
+ const step = Math.max(
238
+ 1,
239
+ Math.floor(
240
+ Object.keys(record.tasks.nodes).filter((id) => id.startsWith("write-"))
241
+ .length / (reviewCount || 1),
242
+ ),
243
+ );
244
+ const targetReview = Math.ceil(writeIteration / step);
245
+ const taskId = `review-${targetReview}`;
246
+ if (
247
+ record.tasks.nodes[taskId] &&
248
+ record.tasks.nodes[taskId].status === "pending"
249
+ ) {
250
+ return taskId;
251
+ }
252
+ return null;
253
+ }
254
+
255
+ function getNextWriteTask(
256
+ record: BlackboardRecord,
257
+ currentIteration: number,
258
+ ): string | null {
259
+ const nextWrite = currentIteration + 1;
260
+ const taskId = `write-${nextWrite}`;
261
+ if (
262
+ record.tasks.nodes[taskId] &&
263
+ record.tasks.nodes[taskId].status === "pending"
264
+ ) {
265
+ return taskId;
266
+ }
267
+ return null;
268
+ }
269
+
270
+ function getReviewIteration(taskId: string): number {
271
+ const m = taskId.match(/^review-(\d+)$/);
272
+ return m ? parseInt(m[1], 10) : 0;
273
+ }
274
+
275
+ function getWriteIteration(taskId: string): number {
276
+ const m = taskId.match(/^write-(\d+)$/);
277
+ return m ? parseInt(m[1], 10) : 0;
278
+ }
279
+
280
+ function sleep(ms: number): Promise<void> {
281
+ return new Promise((r) => setTimeout(r, ms));
282
+ }
283
+
284
+ main().catch(console.error);