@hearsay-tools/squawk 0.0.0-stage → 0.17.0-dev.5685

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 (1976) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1846 -2
  3. package/dist/activity/demand.d.ts +21 -0
  4. package/dist/activity/demand.js +49 -0
  5. package/dist/activity/demand.js.map +1 -0
  6. package/dist/activity/publisher.d.ts +48 -0
  7. package/dist/activity/publisher.js +175 -0
  8. package/dist/activity/publisher.js.map +1 -0
  9. package/dist/activity/read-model.d.ts +50 -0
  10. package/dist/activity/read-model.js +143 -0
  11. package/dist/activity/read-model.js.map +1 -0
  12. package/dist/activity/requests.d.ts +27 -0
  13. package/dist/activity/requests.js +81 -0
  14. package/dist/activity/requests.js.map +1 -0
  15. package/dist/activity/text.d.ts +4 -0
  16. package/dist/activity/text.js +13 -0
  17. package/dist/activity/text.js.map +1 -0
  18. package/dist/activity/tool-activity.d.ts +87 -0
  19. package/dist/activity/tool-activity.js +250 -0
  20. package/dist/activity/tool-activity.js.map +1 -0
  21. package/dist/activity/wait.d.ts +19 -0
  22. package/dist/activity/wait.js +91 -0
  23. package/dist/activity/wait.js.map +1 -0
  24. package/dist/agent-config/account-identity.d.ts +28 -0
  25. package/dist/agent-config/account-identity.js +132 -0
  26. package/dist/agent-config/account-identity.js.map +1 -0
  27. package/dist/agent-config/catalog.d.ts +70 -0
  28. package/dist/agent-config/catalog.js +384 -0
  29. package/dist/agent-config/catalog.js.map +1 -0
  30. package/dist/agent-config/files.d.ts +42 -0
  31. package/dist/agent-config/files.js +115 -0
  32. package/dist/agent-config/files.js.map +1 -0
  33. package/dist/agent-config/model-settings/claude.d.ts +2 -0
  34. package/dist/agent-config/model-settings/claude.js +11 -0
  35. package/dist/agent-config/model-settings/claude.js.map +1 -0
  36. package/dist/agent-config/model-settings/codex.d.ts +2 -0
  37. package/dist/agent-config/model-settings/codex.js +16 -0
  38. package/dist/agent-config/model-settings/codex.js.map +1 -0
  39. package/dist/agent-config/model-settings/opencode.d.ts +2 -0
  40. package/dist/agent-config/model-settings/opencode.js +8 -0
  41. package/dist/agent-config/model-settings/opencode.js.map +1 -0
  42. package/dist/agent-config/model-settings/pi.d.ts +2 -0
  43. package/dist/agent-config/model-settings/pi.js +38 -0
  44. package/dist/agent-config/model-settings/pi.js.map +1 -0
  45. package/dist/agent-config/model-settings/shared.d.ts +12 -0
  46. package/dist/agent-config/model-settings/shared.js +100 -0
  47. package/dist/agent-config/model-settings/shared.js.map +1 -0
  48. package/dist/agent-config/model-settings/types.d.ts +9 -0
  49. package/dist/agent-config/model-settings/types.js +2 -0
  50. package/dist/agent-config/model-settings/types.js.map +1 -0
  51. package/dist/agent-config/models.d.ts +9 -0
  52. package/dist/agent-config/models.js +40 -0
  53. package/dist/agent-config/models.js.map +1 -0
  54. package/dist/agent-config/seed.d.ts +15 -0
  55. package/dist/agent-config/seed.js +94 -0
  56. package/dist/agent-config/seed.js.map +1 -0
  57. package/dist/agent-config/service.d.ts +44 -0
  58. package/dist/agent-config/service.js +73 -0
  59. package/dist/agent-config/service.js.map +1 -0
  60. package/dist/agent-config/validate.d.ts +18 -0
  61. package/dist/agent-config/validate.js +82 -0
  62. package/dist/agent-config/validate.js.map +1 -0
  63. package/dist/application-update/bin-recovery.d.ts +5 -0
  64. package/dist/application-update/bin-recovery.js +96 -0
  65. package/dist/application-update/bin-recovery.js.map +1 -0
  66. package/dist/application-update/discovery.d.ts +23 -0
  67. package/dist/application-update/discovery.js +131 -0
  68. package/dist/application-update/discovery.js.map +1 -0
  69. package/dist/application-update/helper.d.ts +58 -0
  70. package/dist/application-update/helper.js +363 -0
  71. package/dist/application-update/helper.js.map +1 -0
  72. package/dist/application-update/launcher.d.ts +12 -0
  73. package/dist/application-update/launcher.js +61 -0
  74. package/dist/application-update/launcher.js.map +1 -0
  75. package/dist/application-update/lock.d.ts +4 -0
  76. package/dist/application-update/lock.js +70 -0
  77. package/dist/application-update/lock.js.map +1 -0
  78. package/dist/application-update/npm-process.d.ts +17 -0
  79. package/dist/application-update/npm-process.js +156 -0
  80. package/dist/application-update/npm-process.js.map +1 -0
  81. package/dist/application-update/runtime-layout.d.ts +20 -0
  82. package/dist/application-update/runtime-layout.js +91 -0
  83. package/dist/application-update/runtime-layout.js.map +1 -0
  84. package/dist/application-update/scoped-recovery.d.ts +32 -0
  85. package/dist/application-update/scoped-recovery.js +210 -0
  86. package/dist/application-update/scoped-recovery.js.map +1 -0
  87. package/dist/application-update/service.d.ts +86 -0
  88. package/dist/application-update/service.js +523 -0
  89. package/dist/application-update/service.js.map +1 -0
  90. package/dist/archive-on-merge-setting.d.ts +15 -0
  91. package/dist/archive-on-merge-setting.js +15 -0
  92. package/dist/archive-on-merge-setting.js.map +1 -0
  93. package/dist/artifacts/cli.d.ts +1 -0
  94. package/dist/artifacts/cli.js +54 -0
  95. package/dist/artifacts/cli.js.map +1 -0
  96. package/dist/artifacts/lifecycle.d.ts +4 -0
  97. package/dist/artifacts/lifecycle.js +42 -0
  98. package/dist/artifacts/lifecycle.js.map +1 -0
  99. package/dist/artifacts/reports-index.d.ts +46 -0
  100. package/dist/artifacts/reports-index.js +279 -0
  101. package/dist/artifacts/reports-index.js.map +1 -0
  102. package/dist/artifacts/reports-watch.d.ts +43 -0
  103. package/dist/artifacts/reports-watch.js +129 -0
  104. package/dist/artifacts/reports-watch.js.map +1 -0
  105. package/dist/artifacts/resolve.d.ts +18 -0
  106. package/dist/artifacts/resolve.js +129 -0
  107. package/dist/artifacts/resolve.js.map +1 -0
  108. package/dist/artifacts/store.d.ts +40 -0
  109. package/dist/artifacts/store.js +513 -0
  110. package/dist/artifacts/store.js.map +1 -0
  111. package/dist/automations/automation-cli.d.ts +61 -0
  112. package/dist/automations/automation-cli.js +519 -0
  113. package/dist/automations/automation-cli.js.map +1 -0
  114. package/dist/automations/builtin-skill.d.ts +7 -0
  115. package/dist/automations/builtin-skill.js +14 -0
  116. package/dist/automations/builtin-skill.js.map +1 -0
  117. package/dist/automations/coordinator.d.ts +53 -0
  118. package/dist/automations/coordinator.js +136 -0
  119. package/dist/automations/coordinator.js.map +1 -0
  120. package/dist/automations/github-baseline.d.ts +3 -0
  121. package/dist/automations/github-baseline.js +17 -0
  122. package/dist/automations/github-baseline.js.map +1 -0
  123. package/dist/automations/github-poller.d.ts +93 -0
  124. package/dist/automations/github-poller.js +303 -0
  125. package/dist/automations/github-poller.js.map +1 -0
  126. package/dist/automations/project-files.d.ts +6 -0
  127. package/dist/automations/project-files.js +43 -0
  128. package/dist/automations/project-files.js.map +1 -0
  129. package/dist/automations/prompts.d.ts +12 -0
  130. package/dist/automations/prompts.js +162 -0
  131. package/dist/automations/prompts.js.map +1 -0
  132. package/dist/automations/schedule-runner.d.ts +153 -0
  133. package/dist/automations/schedule-runner.js +529 -0
  134. package/dist/automations/schedule-runner.js.map +1 -0
  135. package/dist/automations/scheduler.d.ts +95 -0
  136. package/dist/automations/scheduler.js +462 -0
  137. package/dist/automations/scheduler.js.map +1 -0
  138. package/dist/automations/service.d.ts +793 -0
  139. package/dist/automations/service.js +444 -0
  140. package/dist/automations/service.js.map +1 -0
  141. package/dist/automations/store.d.ts +168 -0
  142. package/dist/automations/store.js +805 -0
  143. package/dist/automations/store.js.map +1 -0
  144. package/dist/automations/task-template.d.ts +59 -0
  145. package/dist/automations/task-template.js +285 -0
  146. package/dist/automations/task-template.js.map +1 -0
  147. package/dist/automations/tool-adapter.d.ts +6 -0
  148. package/dist/automations/tool-adapter.js +115 -0
  149. package/dist/automations/tool-adapter.js.map +1 -0
  150. package/dist/automations/types.d.ts +1 -0
  151. package/dist/automations/types.js +2 -0
  152. package/dist/automations/types.js.map +1 -0
  153. package/dist/autosave-cleanup.d.ts +26 -0
  154. package/dist/autosave-cleanup.js +87 -0
  155. package/dist/autosave-cleanup.js.map +1 -0
  156. package/dist/autosave-git.d.ts +23 -0
  157. package/dist/autosave-git.js +211 -0
  158. package/dist/autosave-git.js.map +1 -0
  159. package/dist/autosave-group.d.ts +8 -0
  160. package/dist/autosave-group.js +61 -0
  161. package/dist/autosave-group.js.map +1 -0
  162. package/dist/autosave-holders.d.ts +34 -0
  163. package/dist/autosave-holders.js +228 -0
  164. package/dist/autosave-holders.js.map +1 -0
  165. package/dist/autosave-windows.d.ts +25 -0
  166. package/dist/autosave-windows.js +170 -0
  167. package/dist/autosave-windows.js.map +1 -0
  168. package/dist/cez.d.ts +2 -0
  169. package/dist/cez.js +3 -0
  170. package/dist/cez.js.map +1 -0
  171. package/dist/cezarion.d.ts +2 -0
  172. package/dist/cezarion.js +3 -0
  173. package/dist/cezarion.js.map +1 -0
  174. package/dist/ci-wait/client.d.ts +28 -0
  175. package/dist/ci-wait/client.js +123 -0
  176. package/dist/ci-wait/client.js.map +1 -0
  177. package/dist/ci-wait/controller.d.ts +824 -0
  178. package/dist/ci-wait/controller.js +305 -0
  179. package/dist/ci-wait/controller.js.map +1 -0
  180. package/dist/ci-wait/errors.d.ts +35 -0
  181. package/dist/ci-wait/errors.js +273 -0
  182. package/dist/ci-wait/errors.js.map +1 -0
  183. package/dist/ci-wait/github.d.ts +105 -0
  184. package/dist/ci-wait/github.js +324 -0
  185. package/dist/ci-wait/github.js.map +1 -0
  186. package/dist/ci-wait/injection.d.ts +3 -0
  187. package/dist/ci-wait/injection.js +13 -0
  188. package/dist/ci-wait/injection.js.map +1 -0
  189. package/dist/ci-wait/mcp.d.ts +3 -0
  190. package/dist/ci-wait/mcp.js +30 -0
  191. package/dist/ci-wait/mcp.js.map +1 -0
  192. package/dist/ci-wait/merge-progress.d.ts +84 -0
  193. package/dist/ci-wait/merge-progress.js +50 -0
  194. package/dist/ci-wait/merge-progress.js.map +1 -0
  195. package/dist/ci-wait/progress.d.ts +56 -0
  196. package/dist/ci-wait/progress.js +100 -0
  197. package/dist/ci-wait/progress.js.map +1 -0
  198. package/dist/ci-wait/resources.d.ts +9 -0
  199. package/dist/ci-wait/resources.js +46 -0
  200. package/dist/ci-wait/resources.js.map +1 -0
  201. package/dist/ci-wait/supervisor.d.ts +47 -0
  202. package/dist/ci-wait/supervisor.js +334 -0
  203. package/dist/ci-wait/supervisor.js.map +1 -0
  204. package/dist/ci-wait/text.d.ts +2 -0
  205. package/dist/ci-wait/text.js +5 -0
  206. package/dist/ci-wait/text.js.map +1 -0
  207. package/dist/ci-wait/tools.d.ts +845 -0
  208. package/dist/ci-wait/tools.js +232 -0
  209. package/dist/ci-wait/tools.js.map +1 -0
  210. package/dist/cli-dispatch.d.ts +33 -0
  211. package/dist/cli-dispatch.js +239 -0
  212. package/dist/cli-dispatch.js.map +1 -0
  213. package/dist/config.d.ts +117 -0
  214. package/dist/config.js +343 -0
  215. package/dist/config.js.map +1 -0
  216. package/dist/contract/active-provider.d.ts +12 -0
  217. package/dist/contract/activity.d.ts +1478 -0
  218. package/dist/contract/agent-config.d.ts +160 -0
  219. package/dist/contract/agent-profiles.d.ts +369 -0
  220. package/dist/contract/application-update.d.ts +32 -0
  221. package/dist/contract/artifacts.d.ts +100 -0
  222. package/dist/contract/ask-schema.d.ts +50 -0
  223. package/dist/contract/ask.d.ts +11 -0
  224. package/dist/contract/attachments.d.ts +35 -0
  225. package/dist/contract/attention.d.ts +99 -0
  226. package/dist/contract/automation-requests.d.ts +314 -0
  227. package/dist/contract/automation-schedule.d.ts +89 -0
  228. package/dist/contract/automation-storage.d.ts +518 -0
  229. package/dist/contract/automations.d.ts +1560 -0
  230. package/dist/contract/ci-wait.d.ts +281 -0
  231. package/dist/contract/config-requests.d.ts +36 -0
  232. package/dist/contract/control-locations.d.ts +10 -0
  233. package/dist/contract/conversations.d.ts +415 -0
  234. package/dist/contract/defined.d.ts +8 -0
  235. package/dist/contract/delegation.d.ts +2168 -0
  236. package/dist/contract/discovery.d.ts +228 -0
  237. package/dist/contract/effort.d.ts +18 -0
  238. package/dist/contract/events.d.ts +324 -0
  239. package/dist/contract/github-requests.d.ts +26 -0
  240. package/dist/contract/github.d.ts +732 -0
  241. package/dist/contract/handoff.d.ts +43 -0
  242. package/dist/contract/health.d.ts +128 -0
  243. package/dist/contract/home-recovery.d.ts +71 -0
  244. package/dist/contract/image-path.d.ts +6 -0
  245. package/dist/contract/index.d.ts +68 -0
  246. package/dist/contract/index.js +6709 -0
  247. package/dist/contract/live-coordination.d.ts +164 -0
  248. package/dist/contract/live-limits.d.ts +8 -0
  249. package/dist/contract/live.d.ts +123 -0
  250. package/dist/contract/pending-ask.d.ts +58 -0
  251. package/dist/contract/per-runner-optional-strings.d.ts +10 -0
  252. package/dist/contract/permissions.d.ts +54 -0
  253. package/dist/contract/preview.d.ts +568 -0
  254. package/dist/contract/project-limits.d.ts +35 -0
  255. package/dist/contract/projects.d.ts +716 -0
  256. package/dist/contract/provider-requests.d.ts +77 -0
  257. package/dist/contract/query-requests.d.ts +52 -0
  258. package/dist/contract/repo-requests.d.ts +12 -0
  259. package/dist/contract/repo.d.ts +515 -0
  260. package/dist/contract/report-title.d.ts +3 -0
  261. package/dist/contract/reports.d.ts +101 -0
  262. package/dist/contract/run-labels.d.ts +8 -0
  263. package/dist/contract/run-limits.d.ts +8 -0
  264. package/dist/contract/run-predicates.d.ts +87 -0
  265. package/dist/contract/run-requests.d.ts +33 -0
  266. package/dist/contract/run-search.d.ts +193 -0
  267. package/dist/contract/runs.d.ts +7108 -0
  268. package/dist/contract/scheduled-tasks.d.ts +188 -0
  269. package/dist/contract/self-update.d.ts +255 -0
  270. package/dist/contract/skill-sources.d.ts +125 -0
  271. package/dist/contract/skills.d.ts +56 -0
  272. package/dist/contract/task-options.d.ts +18 -0
  273. package/dist/contract/team-skill-ref.d.ts +10 -0
  274. package/dist/contract/tool-display.d.ts +25 -0
  275. package/dist/contract/ui-state-requests.d.ts +60 -0
  276. package/dist/contract/version.d.ts +3 -0
  277. package/dist/contract/voice.d.ts +213 -0
  278. package/dist/contract/waits.d.ts +1996 -0
  279. package/dist/contract/workflow-file.d.ts +37 -0
  280. package/dist/contract/workflow-requests.d.ts +7 -0
  281. package/dist/contract/workflows.d.ts +250 -0
  282. package/dist/contract/workspace-events.d.ts +410 -0
  283. package/dist/contract/workspace.d.ts +631 -0
  284. package/dist/contract/zoned-time.d.ts +26 -0
  285. package/dist/core/agent-env.d.ts +34 -0
  286. package/dist/core/agent-env.js +411 -0
  287. package/dist/core/agent-env.js.map +1 -0
  288. package/dist/core/agent-model-policy.d.ts +11 -0
  289. package/dist/core/agent-model-policy.js +38 -0
  290. package/dist/core/agent-model-policy.js.map +1 -0
  291. package/dist/core/agent-profiles.d.ts +50 -0
  292. package/dist/core/agent-profiles.js +105 -0
  293. package/dist/core/agent-profiles.js.map +1 -0
  294. package/dist/core/agent-runner.d.ts +425 -0
  295. package/dist/core/agent-runner.js +98 -0
  296. package/dist/core/agent-runner.js.map +1 -0
  297. package/dist/core/ask.d.ts +26 -0
  298. package/dist/core/ask.js +35 -0
  299. package/dist/core/ask.js.map +1 -0
  300. package/dist/core/backend-detect.d.ts +18 -0
  301. package/dist/core/backend-detect.js +216 -0
  302. package/dist/core/backend-detect.js.map +1 -0
  303. package/dist/core/claude-cli-runner.d.ts +85 -0
  304. package/dist/core/claude-cli-runner.js +899 -0
  305. package/dist/core/claude-cli-runner.js.map +1 -0
  306. package/dist/core/claude-executable.d.ts +2 -0
  307. package/dist/core/claude-executable.js +15 -0
  308. package/dist/core/claude-executable.js.map +1 -0
  309. package/dist/core/claude-model-catalog.d.ts +16 -0
  310. package/dist/core/claude-model-catalog.js +198 -0
  311. package/dist/core/claude-model-catalog.js.map +1 -0
  312. package/dist/core/claude-stream.d.ts +38 -0
  313. package/dist/core/claude-stream.js +77 -0
  314. package/dist/core/claude-stream.js.map +1 -0
  315. package/dist/core/claude-ui-mapper.d.ts +88 -0
  316. package/dist/core/claude-ui-mapper.js +851 -0
  317. package/dist/core/claude-ui-mapper.js.map +1 -0
  318. package/dist/core/codex-app-server-runner.d.ts +76 -0
  319. package/dist/core/codex-app-server-runner.js +1271 -0
  320. package/dist/core/codex-app-server-runner.js.map +1 -0
  321. package/dist/core/codex-app-server-transport.d.ts +52 -0
  322. package/dist/core/codex-app-server-transport.js +200 -0
  323. package/dist/core/codex-app-server-transport.js.map +1 -0
  324. package/dist/core/codex-approvals.d.ts +12 -0
  325. package/dist/core/codex-approvals.js +180 -0
  326. package/dist/core/codex-approvals.js.map +1 -0
  327. package/dist/core/codex-hook-mapper.d.ts +15 -0
  328. package/dist/core/codex-hook-mapper.js +73 -0
  329. package/dist/core/codex-hook-mapper.js.map +1 -0
  330. package/dist/core/codex-model-catalog.d.ts +10 -0
  331. package/dist/core/codex-model-catalog.js +110 -0
  332. package/dist/core/codex-model-catalog.js.map +1 -0
  333. package/dist/core/codex-native-frames.d.ts +35 -0
  334. package/dist/core/codex-native-frames.js +80 -0
  335. package/dist/core/codex-native-frames.js.map +1 -0
  336. package/dist/core/codex-permissions.d.ts +6 -0
  337. package/dist/core/codex-permissions.js +27 -0
  338. package/dist/core/codex-permissions.js.map +1 -0
  339. package/dist/core/codex-stream-error.d.ts +2 -0
  340. package/dist/core/codex-stream-error.js +12 -0
  341. package/dist/core/codex-stream-error.js.map +1 -0
  342. package/dist/core/codex-turn-outcome.d.ts +10 -0
  343. package/dist/core/codex-turn-outcome.js +35 -0
  344. package/dist/core/codex-turn-outcome.js.map +1 -0
  345. package/dist/core/codex-ui-mapper-state.d.ts +84 -0
  346. package/dist/core/codex-ui-mapper-state.js +8 -0
  347. package/dist/core/codex-ui-mapper-state.js.map +1 -0
  348. package/dist/core/codex-ui-mapper.d.ts +44 -0
  349. package/dist/core/codex-ui-mapper.js +799 -0
  350. package/dist/core/codex-ui-mapper.js.map +1 -0
  351. package/dist/core/cursor-cli-flags.d.ts +19 -0
  352. package/dist/core/cursor-cli-flags.js +47 -0
  353. package/dist/core/cursor-cli-flags.js.map +1 -0
  354. package/dist/core/cursor-config-options.d.ts +18 -0
  355. package/dist/core/cursor-config-options.js +31 -0
  356. package/dist/core/cursor-config-options.js.map +1 -0
  357. package/dist/core/cursor-model-catalog.d.ts +50 -0
  358. package/dist/core/cursor-model-catalog.js +306 -0
  359. package/dist/core/cursor-model-catalog.js.map +1 -0
  360. package/dist/core/cursor-print-capabilities.d.ts +14 -0
  361. package/dist/core/cursor-print-capabilities.js +169 -0
  362. package/dist/core/cursor-print-capabilities.js.map +1 -0
  363. package/dist/core/cursor-print-process.d.ts +31 -0
  364. package/dist/core/cursor-print-process.js +117 -0
  365. package/dist/core/cursor-print-process.js.map +1 -0
  366. package/dist/core/cursor-print-runner.d.ts +43 -0
  367. package/dist/core/cursor-print-runner.js +574 -0
  368. package/dist/core/cursor-print-runner.js.map +1 -0
  369. package/dist/core/cursor-print-tool-catalog.d.ts +3 -0
  370. package/dist/core/cursor-print-tool-catalog.js +13 -0
  371. package/dist/core/cursor-print-tool-catalog.js.map +1 -0
  372. package/dist/core/cursor-print-ui-mapper.d.ts +47 -0
  373. package/dist/core/cursor-print-ui-mapper.js +574 -0
  374. package/dist/core/cursor-print-ui-mapper.js.map +1 -0
  375. package/dist/core/cursor-provider-error.d.ts +9 -0
  376. package/dist/core/cursor-provider-error.js +15 -0
  377. package/dist/core/cursor-provider-error.js.map +1 -0
  378. package/dist/core/cursor-runner.d.ts +46 -0
  379. package/dist/core/cursor-runner.js +144 -0
  380. package/dist/core/cursor-runner.js.map +1 -0
  381. package/dist/core/defined.d.ts +1 -0
  382. package/dist/core/defined.js +2 -0
  383. package/dist/core/defined.js.map +1 -0
  384. package/dist/core/disclaim-spawn.d.ts +11 -0
  385. package/dist/core/disclaim-spawn.js +60 -0
  386. package/dist/core/disclaim-spawn.js.map +1 -0
  387. package/dist/core/edit-write-key.d.ts +2 -0
  388. package/dist/core/edit-write-key.js +6 -0
  389. package/dist/core/edit-write-key.js.map +1 -0
  390. package/dist/core/hook-items.d.ts +14 -0
  391. package/dist/core/hook-items.js +13 -0
  392. package/dist/core/hook-items.js.map +1 -0
  393. package/dist/core/host-model-catalog.d.ts +7 -0
  394. package/dist/core/host-model-catalog.js +21 -0
  395. package/dist/core/host-model-catalog.js.map +1 -0
  396. package/dist/core/input-submissions.d.ts +14 -0
  397. package/dist/core/input-submissions.js +26 -0
  398. package/dist/core/input-submissions.js.map +1 -0
  399. package/dist/core/managed-session.d.ts +6 -0
  400. package/dist/core/managed-session.js +116 -0
  401. package/dist/core/managed-session.js.map +1 -0
  402. package/dist/core/model-discovery-options.d.ts +8 -0
  403. package/dist/core/model-discovery-options.js +2 -0
  404. package/dist/core/model-discovery-options.js.map +1 -0
  405. package/dist/core/model-identity.d.ts +108 -0
  406. package/dist/core/model-identity.js +137 -0
  407. package/dist/core/model-identity.js.map +1 -0
  408. package/dist/core/model-presets.d.ts +10 -0
  409. package/dist/core/model-presets.js +77 -0
  410. package/dist/core/model-presets.js.map +1 -0
  411. package/dist/core/model-probe-teardown.d.ts +4 -0
  412. package/dist/core/model-probe-teardown.js +20 -0
  413. package/dist/core/model-probe-teardown.js.map +1 -0
  414. package/dist/core/ndjson.d.ts +6 -0
  415. package/dist/core/ndjson.js +23 -0
  416. package/dist/core/ndjson.js.map +1 -0
  417. package/dist/core/omp-model-catalog.d.ts +39 -0
  418. package/dist/core/omp-model-catalog.js +183 -0
  419. package/dist/core/omp-model-catalog.js.map +1 -0
  420. package/dist/core/omp-runner.d.ts +86 -0
  421. package/dist/core/omp-runner.js +818 -0
  422. package/dist/core/omp-runner.js.map +1 -0
  423. package/dist/core/omp-subagent-rows.d.ts +21 -0
  424. package/dist/core/omp-subagent-rows.js +99 -0
  425. package/dist/core/omp-subagent-rows.js.map +1 -0
  426. package/dist/core/omp-ui-mapper.d.ts +112 -0
  427. package/dist/core/omp-ui-mapper.js +807 -0
  428. package/dist/core/omp-ui-mapper.js.map +1 -0
  429. package/dist/core/opencode-model-catalog.d.ts +27 -0
  430. package/dist/core/opencode-model-catalog.js +189 -0
  431. package/dist/core/opencode-model-catalog.js.map +1 -0
  432. package/dist/core/opencode-permissions.d.ts +52 -0
  433. package/dist/core/opencode-permissions.js +101 -0
  434. package/dist/core/opencode-permissions.js.map +1 -0
  435. package/dist/core/opencode-runtime-config.d.ts +9 -0
  436. package/dist/core/opencode-runtime-config.js +26 -0
  437. package/dist/core/opencode-runtime-config.js.map +1 -0
  438. package/dist/core/opencode-server-runner.d.ts +75 -0
  439. package/dist/core/opencode-server-runner.js +1690 -0
  440. package/dist/core/opencode-server-runner.js.map +1 -0
  441. package/dist/core/opencode-session-error.d.ts +19 -0
  442. package/dist/core/opencode-session-error.js +42 -0
  443. package/dist/core/opencode-session-error.js.map +1 -0
  444. package/dist/core/opencode-task-scope.d.ts +9 -0
  445. package/dist/core/opencode-task-scope.js +19 -0
  446. package/dist/core/opencode-task-scope.js.map +1 -0
  447. package/dist/core/opencode-ui-mapper-state.d.ts +82 -0
  448. package/dist/core/opencode-ui-mapper-state.js +7 -0
  449. package/dist/core/opencode-ui-mapper-state.js.map +1 -0
  450. package/dist/core/opencode-ui-mapper.d.ts +59 -0
  451. package/dist/core/opencode-ui-mapper.js +724 -0
  452. package/dist/core/opencode-ui-mapper.js.map +1 -0
  453. package/dist/core/opencode-usage.d.ts +32 -0
  454. package/dist/core/opencode-usage.js +105 -0
  455. package/dist/core/opencode-usage.js.map +1 -0
  456. package/dist/core/pi-model-catalog.d.ts +34 -0
  457. package/dist/core/pi-model-catalog.js +247 -0
  458. package/dist/core/pi-model-catalog.js.map +1 -0
  459. package/dist/core/pi-rpc-turn.d.ts +179 -0
  460. package/dist/core/pi-rpc-turn.js +370 -0
  461. package/dist/core/pi-rpc-turn.js.map +1 -0
  462. package/dist/core/pi-rpc-wire.d.ts +31 -0
  463. package/dist/core/pi-rpc-wire.js +88 -0
  464. package/dist/core/pi-rpc-wire.js.map +1 -0
  465. package/dist/core/pi-runner.d.ts +58 -0
  466. package/dist/core/pi-runner.js +587 -0
  467. package/dist/core/pi-runner.js.map +1 -0
  468. package/dist/core/pi-ui-mapper.d.ts +52 -0
  469. package/dist/core/pi-ui-mapper.js +408 -0
  470. package/dist/core/pi-ui-mapper.js.map +1 -0
  471. package/dist/core/process-usage.d.ts +74 -0
  472. package/dist/core/process-usage.js +219 -0
  473. package/dist/core/process-usage.js.map +1 -0
  474. package/dist/core/product-env.d.ts +21 -0
  475. package/dist/core/product-env.js +38 -0
  476. package/dist/core/product-env.js.map +1 -0
  477. package/dist/core/provider-auth.d.ts +168 -0
  478. package/dist/core/provider-auth.js +661 -0
  479. package/dist/core/provider-auth.js.map +1 -0
  480. package/dist/core/provider-availability.d.ts +3 -0
  481. package/dist/core/provider-availability.js +14 -0
  482. package/dist/core/provider-availability.js.map +1 -0
  483. package/dist/core/runner-factory.d.ts +11 -0
  484. package/dist/core/runner-factory.js +36 -0
  485. package/dist/core/runner-factory.js.map +1 -0
  486. package/dist/core/runner-facts.d.ts +135 -0
  487. package/dist/core/runner-facts.js +59 -0
  488. package/dist/core/runner-facts.js.map +1 -0
  489. package/dist/core/runner-model-catalog.d.ts +34 -0
  490. package/dist/core/runner-model-catalog.js +109 -0
  491. package/dist/core/runner-model-catalog.js.map +1 -0
  492. package/dist/core/runner-runtime.d.ts +40 -0
  493. package/dist/core/runner-runtime.js +88 -0
  494. package/dist/core/runner-runtime.js.map +1 -0
  495. package/dist/core/runner-stderr.d.ts +2 -0
  496. package/dist/core/runner-stderr.js +20 -0
  497. package/dist/core/runner-stderr.js.map +1 -0
  498. package/dist/core/runner-tool-grants.d.ts +19 -0
  499. package/dist/core/runner-tool-grants.js +54 -0
  500. package/dist/core/runner-tool-grants.js.map +1 -0
  501. package/dist/core/secret-redaction.d.ts +20 -0
  502. package/dist/core/secret-redaction.js +95 -0
  503. package/dist/core/secret-redaction.js.map +1 -0
  504. package/dist/core/session-process.d.ts +43 -0
  505. package/dist/core/session-process.js +363 -0
  506. package/dist/core/session-process.js.map +1 -0
  507. package/dist/core/shell-env.d.ts +17 -0
  508. package/dist/core/shell-env.js +51 -0
  509. package/dist/core/shell-env.js.map +1 -0
  510. package/dist/core/ui-events.d.ts +356 -0
  511. package/dist/core/ui-events.js +13 -0
  512. package/dist/core/ui-events.js.map +1 -0
  513. package/dist/core/usage-limit.d.ts +42 -0
  514. package/dist/core/usage-limit.js +152 -0
  515. package/dist/core/usage-limit.js.map +1 -0
  516. package/dist/core/usage.d.ts +11 -0
  517. package/dist/core/usage.js +15 -0
  518. package/dist/core/usage.js.map +1 -0
  519. package/dist/core/v1-text-coalescer.d.ts +36 -0
  520. package/dist/core/v1-text-coalescer.js +72 -0
  521. package/dist/core/v1-text-coalescer.js.map +1 -0
  522. package/dist/data-gitignore.d.ts +6 -0
  523. package/dist/data-gitignore.js +80 -0
  524. package/dist/data-gitignore.js.map +1 -0
  525. package/dist/delegation/capacity.d.ts +15 -0
  526. package/dist/delegation/capacity.js +30 -0
  527. package/dist/delegation/capacity.js.map +1 -0
  528. package/dist/delegation/cli.d.ts +5 -0
  529. package/dist/delegation/cli.js +572 -0
  530. package/dist/delegation/cli.js.map +1 -0
  531. package/dist/delegation/context.d.ts +40 -0
  532. package/dist/delegation/context.js +398 -0
  533. package/dist/delegation/context.js.map +1 -0
  534. package/dist/delegation/conversation-send.d.ts +64 -0
  535. package/dist/delegation/conversation-send.js +74 -0
  536. package/dist/delegation/conversation-send.js.map +1 -0
  537. package/dist/delegation/conversations.d.ts +2186 -0
  538. package/dist/delegation/conversations.js +285 -0
  539. package/dist/delegation/conversations.js.map +1 -0
  540. package/dist/delegation/credentials.d.ts +21 -0
  541. package/dist/delegation/credentials.js +66 -0
  542. package/dist/delegation/credentials.js.map +1 -0
  543. package/dist/delegation/destroy-observation.d.ts +42 -0
  544. package/dist/delegation/destroy-observation.js +115 -0
  545. package/dist/delegation/destroy-observation.js.map +1 -0
  546. package/dist/delegation/effort.d.ts +1 -0
  547. package/dist/delegation/effort.js +11 -0
  548. package/dist/delegation/effort.js.map +1 -0
  549. package/dist/delegation/execution-identity.d.ts +257 -0
  550. package/dist/delegation/execution-identity.js +253 -0
  551. package/dist/delegation/execution-identity.js.map +1 -0
  552. package/dist/delegation/execution-settings.d.ts +11 -0
  553. package/dist/delegation/execution-settings.js +65 -0
  554. package/dist/delegation/execution-settings.js.map +1 -0
  555. package/dist/delegation/idle-test-app.d.ts +4 -0
  556. package/dist/delegation/idle-test-app.js +186 -0
  557. package/dist/delegation/idle-test-app.js.map +1 -0
  558. package/dist/delegation/input.d.ts +12 -0
  559. package/dist/delegation/input.js +50 -0
  560. package/dist/delegation/input.js.map +1 -0
  561. package/dist/delegation/park-notice.d.ts +45 -0
  562. package/dist/delegation/park-notice.js +81 -0
  563. package/dist/delegation/park-notice.js.map +1 -0
  564. package/dist/delegation/policy.d.ts +27 -0
  565. package/dist/delegation/policy.js +110 -0
  566. package/dist/delegation/policy.js.map +1 -0
  567. package/dist/delegation/process-liveness.d.ts +98 -0
  568. package/dist/delegation/process-liveness.js +213 -0
  569. package/dist/delegation/process-liveness.js.map +1 -0
  570. package/dist/delegation/provision.d.ts +36 -0
  571. package/dist/delegation/provision.js +218 -0
  572. package/dist/delegation/provision.js.map +1 -0
  573. package/dist/delegation/questions.d.ts +24 -0
  574. package/dist/delegation/questions.js +56 -0
  575. package/dist/delegation/questions.js.map +1 -0
  576. package/dist/delegation/readiness.d.ts +13 -0
  577. package/dist/delegation/readiness.js +19 -0
  578. package/dist/delegation/readiness.js.map +1 -0
  579. package/dist/delegation/results.d.ts +13 -0
  580. package/dist/delegation/results.js +153 -0
  581. package/dist/delegation/results.js.map +1 -0
  582. package/dist/delegation/retry-backoff.d.ts +31 -0
  583. package/dist/delegation/retry-backoff.js +26 -0
  584. package/dist/delegation/retry-backoff.js.map +1 -0
  585. package/dist/delegation/routes.d.ts +1017 -0
  586. package/dist/delegation/routes.js +85 -0
  587. package/dist/delegation/routes.js.map +1 -0
  588. package/dist/delegation/scratch-cleanup.d.ts +33 -0
  589. package/dist/delegation/scratch-cleanup.js +155 -0
  590. package/dist/delegation/scratch-cleanup.js.map +1 -0
  591. package/dist/delegation/service.d.ts +264 -0
  592. package/dist/delegation/service.js +807 -0
  593. package/dist/delegation/service.js.map +1 -0
  594. package/dist/delegation/task-discovery.d.ts +6 -0
  595. package/dist/delegation/task-discovery.js +27 -0
  596. package/dist/delegation/task-discovery.js.map +1 -0
  597. package/dist/delegation/transport.d.ts +995 -0
  598. package/dist/delegation/transport.js +22 -0
  599. package/dist/delegation/transport.js.map +1 -0
  600. package/dist/delegation/wait.d.ts +7 -0
  601. package/dist/delegation/wait.js +44 -0
  602. package/dist/delegation/wait.js.map +1 -0
  603. package/dist/delegation/windows-test-app.d.ts +5 -0
  604. package/dist/delegation/windows-test-app.js +64 -0
  605. package/dist/delegation/windows-test-app.js.map +1 -0
  606. package/dist/delegation/windows-workspace-identity.d.ts +3 -0
  607. package/dist/delegation/windows-workspace-identity.js +23 -0
  608. package/dist/delegation/windows-workspace-identity.js.map +1 -0
  609. package/dist/delegation/worker-cleanup.d.ts +53 -0
  610. package/dist/delegation/worker-cleanup.js +330 -0
  611. package/dist/delegation/worker-cleanup.js.map +1 -0
  612. package/dist/delegation/workspace.d.ts +40 -0
  613. package/dist/delegation/workspace.js +592 -0
  614. package/dist/delegation/workspace.js.map +1 -0
  615. package/dist/detached-process.d.ts +134 -0
  616. package/dist/detached-process.js +460 -0
  617. package/dist/detached-process.js.map +1 -0
  618. package/dist/discovery/catalog.d.ts +16 -0
  619. package/dist/discovery/catalog.js +31 -0
  620. package/dist/discovery/catalog.js.map +1 -0
  621. package/dist/discovery/cli.d.ts +8 -0
  622. package/dist/discovery/cli.js +122 -0
  623. package/dist/discovery/cli.js.map +1 -0
  624. package/dist/git-command.d.ts +7 -0
  625. package/dist/git-command.js +8 -0
  626. package/dist/git-command.js.map +1 -0
  627. package/dist/git-diff-base.d.ts +41 -0
  628. package/dist/git-diff-base.js +116 -0
  629. package/dist/git-diff-base.js.map +1 -0
  630. package/dist/git-index-copy.d.ts +4 -0
  631. package/dist/git-index-copy.js +10 -0
  632. package/dist/git-index-copy.js.map +1 -0
  633. package/dist/git-private-index.d.ts +11 -0
  634. package/dist/git-private-index.js +51 -0
  635. package/dist/git-private-index.js.map +1 -0
  636. package/dist/git-refs.d.ts +11 -0
  637. package/dist/git-refs.js +14 -0
  638. package/dist/git-refs.js.map +1 -0
  639. package/dist/git-worktree-lock-helper.d.ts +7 -0
  640. package/dist/git-worktree-lock-helper.js +178 -0
  641. package/dist/git-worktree-lock-helper.js.map +1 -0
  642. package/dist/git-worktree-lock.d.ts +17 -0
  643. package/dist/git-worktree-lock.js +90 -0
  644. package/dist/git-worktree-lock.js.map +1 -0
  645. package/dist/git-worktree-release.d.ts +20 -0
  646. package/dist/git-worktree-release.js +27 -0
  647. package/dist/git-worktree-release.js.map +1 -0
  648. package/dist/git-worktree.d.ts +148 -0
  649. package/dist/git-worktree.js +858 -0
  650. package/dist/git-worktree.js.map +1 -0
  651. package/dist/handoff.d.ts +72 -0
  652. package/dist/handoff.js +213 -0
  653. package/dist/handoff.js.map +1 -0
  654. package/dist/headless-run.d.ts +48 -0
  655. package/dist/headless-run.js +158 -0
  656. package/dist/headless-run.js.map +1 -0
  657. package/dist/home/boundary.d.ts +5 -0
  658. package/dist/home/boundary.js +76 -0
  659. package/dist/home/boundary.js.map +1 -0
  660. package/dist/home/changes.d.ts +3 -0
  661. package/dist/home/changes.js +11 -0
  662. package/dist/home/changes.js.map +1 -0
  663. package/dist/home/command.d.ts +9 -0
  664. package/dist/home/command.js +37 -0
  665. package/dist/home/command.js.map +1 -0
  666. package/dist/home/deprecation.d.ts +9 -0
  667. package/dist/home/deprecation.js +17 -0
  668. package/dist/home/deprecation.js.map +1 -0
  669. package/dist/home/display.d.ts +4 -0
  670. package/dist/home/display.js +11 -0
  671. package/dist/home/display.js.map +1 -0
  672. package/dist/home/entry-authorization.d.ts +17 -0
  673. package/dist/home/entry-authorization.js +39 -0
  674. package/dist/home/entry-authorization.js.map +1 -0
  675. package/dist/home/entry.d.ts +16 -0
  676. package/dist/home/entry.js +96 -0
  677. package/dist/home/entry.js.map +1 -0
  678. package/dist/home/file-location.d.ts +14 -0
  679. package/dist/home/file-location.js +24 -0
  680. package/dist/home/file-location.js.map +1 -0
  681. package/dist/home/home.d.ts +42 -0
  682. package/dist/home/home.js +70 -0
  683. package/dist/home/home.js.map +1 -0
  684. package/dist/home/identity.d.ts +22 -0
  685. package/dist/home/identity.js +25 -0
  686. package/dist/home/identity.js.map +1 -0
  687. package/dist/home/install-marker.d.ts +27 -0
  688. package/dist/home/install-marker.js +78 -0
  689. package/dist/home/install-marker.js.map +1 -0
  690. package/dist/home/kind-roots.d.ts +10 -0
  691. package/dist/home/kind-roots.js +36 -0
  692. package/dist/home/kind-roots.js.map +1 -0
  693. package/dist/home/leases.d.ts +14 -0
  694. package/dist/home/leases.js +55 -0
  695. package/dist/home/leases.js.map +1 -0
  696. package/dist/home/legacy-account-paths.d.ts +4 -0
  697. package/dist/home/legacy-account-paths.js +41 -0
  698. package/dist/home/legacy-account-paths.js.map +1 -0
  699. package/dist/home/legacy-merge.d.ts +3 -0
  700. package/dist/home/legacy-merge.js +45 -0
  701. package/dist/home/legacy-merge.js.map +1 -0
  702. package/dist/home/legacy-recovery.d.ts +5 -0
  703. package/dist/home/legacy-recovery.js +154 -0
  704. package/dist/home/legacy-recovery.js.map +1 -0
  705. package/dist/home/legacy-source.d.ts +15 -0
  706. package/dist/home/legacy-source.js +57 -0
  707. package/dist/home/legacy-source.js.map +1 -0
  708. package/dist/home/migrate.d.ts +19 -0
  709. package/dist/home/migrate.js +322 -0
  710. package/dist/home/migrate.js.map +1 -0
  711. package/dist/home/migration-account-source.d.ts +2 -0
  712. package/dist/home/migration-account-source.js +17 -0
  713. package/dist/home/migration-account-source.js.map +1 -0
  714. package/dist/home/migration-accounts.d.ts +23 -0
  715. package/dist/home/migration-accounts.js +174 -0
  716. package/dist/home/migration-accounts.js.map +1 -0
  717. package/dist/home/migration-boot.d.ts +23 -0
  718. package/dist/home/migration-boot.js +84 -0
  719. package/dist/home/migration-boot.js.map +1 -0
  720. package/dist/home/migration-claim.d.ts +4 -0
  721. package/dist/home/migration-claim.js +69 -0
  722. package/dist/home/migration-claim.js.map +1 -0
  723. package/dist/home/migration-cli.d.ts +3 -0
  724. package/dist/home/migration-cli.js +64 -0
  725. package/dist/home/migration-cli.js.map +1 -0
  726. package/dist/home/migration-files.d.ts +14 -0
  727. package/dist/home/migration-files.js +192 -0
  728. package/dist/home/migration-files.js.map +1 -0
  729. package/dist/home/migration-guard.d.ts +2 -0
  730. package/dist/home/migration-guard.js +20 -0
  731. package/dist/home/migration-guard.js.map +1 -0
  732. package/dist/home/migration-plan.d.ts +20 -0
  733. package/dist/home/migration-plan.js +71 -0
  734. package/dist/home/migration-plan.js.map +1 -0
  735. package/dist/home/migration-record.d.ts +79 -0
  736. package/dist/home/migration-record.js +39 -0
  737. package/dist/home/migration-record.js.map +1 -0
  738. package/dist/home/private-files.d.ts +13 -0
  739. package/dist/home/private-files.js +113 -0
  740. package/dist/home/private-files.js.map +1 -0
  741. package/dist/home/process-identity.d.ts +2 -0
  742. package/dist/home/process-identity.js +8 -0
  743. package/dist/home/process-identity.js.map +1 -0
  744. package/dist/home/protected-roots.d.ts +25 -0
  745. package/dist/home/protected-roots.js +91 -0
  746. package/dist/home/protected-roots.js.map +1 -0
  747. package/dist/home/recovery-watch.d.ts +3 -0
  748. package/dist/home/recovery-watch.js +75 -0
  749. package/dist/home/recovery-watch.js.map +1 -0
  750. package/dist/home/recovery.d.ts +10 -0
  751. package/dist/home/recovery.js +94 -0
  752. package/dist/home/recovery.js.map +1 -0
  753. package/dist/home/registry-content.d.ts +2 -0
  754. package/dist/home/registry-content.js +17 -0
  755. package/dist/home/registry-content.js.map +1 -0
  756. package/dist/home/reset-detection.d.ts +5 -0
  757. package/dist/home/reset-detection.js +22 -0
  758. package/dist/home/reset-detection.js.map +1 -0
  759. package/dist/home/resolve.d.ts +10 -0
  760. package/dist/home/resolve.js +28 -0
  761. package/dist/home/resolve.js.map +1 -0
  762. package/dist/home/store.d.ts +48 -0
  763. package/dist/home/store.js +324 -0
  764. package/dist/home/store.js.map +1 -0
  765. package/dist/home/switch.d.ts +2 -0
  766. package/dist/home/switch.js +3 -0
  767. package/dist/home/switch.js.map +1 -0
  768. package/dist/home/version-log.d.ts +48 -0
  769. package/dist/home/version-log.js +216 -0
  770. package/dist/home/version-log.js.map +1 -0
  771. package/dist/home/write-lock.d.ts +6 -0
  772. package/dist/home/write-lock.js +96 -0
  773. package/dist/home/write-lock.js.map +1 -0
  774. package/dist/hsq.d.ts +2 -0
  775. package/dist/hsq.js +3 -0
  776. package/dist/hsq.js.map +1 -0
  777. package/dist/index.d.ts +2 -0
  778. package/dist/index.js +123 -0
  779. package/dist/index.js.map +1 -0
  780. package/dist/install-as-command.d.ts +32 -0
  781. package/dist/install-as-command.js +55 -0
  782. package/dist/install-as-command.js.map +1 -0
  783. package/dist/jobs/cli.d.ts +2 -0
  784. package/dist/jobs/cli.js +53 -0
  785. package/dist/jobs/cli.js.map +1 -0
  786. package/dist/jobs/command.d.ts +9 -0
  787. package/dist/jobs/command.js +19 -0
  788. package/dist/jobs/command.js.map +1 -0
  789. package/dist/jobs/job.d.ts +77 -0
  790. package/dist/jobs/job.js +252 -0
  791. package/dist/jobs/job.js.map +1 -0
  792. package/dist/merge-observations.d.ts +13 -0
  793. package/dist/merge-observations.js +55 -0
  794. package/dist/merge-observations.js.map +1 -0
  795. package/dist/merged-streams.d.ts +13 -0
  796. package/dist/merged-streams.js +33 -0
  797. package/dist/merged-streams.js.map +1 -0
  798. package/dist/open-url.d.ts +2 -0
  799. package/dist/open-url.js +10 -0
  800. package/dist/open-url.js.map +1 -0
  801. package/dist/pack-check.d.ts +17 -0
  802. package/dist/pack-check.js +27 -0
  803. package/dist/pack-check.js.map +1 -0
  804. package/dist/paths.d.ts +118 -0
  805. package/dist/paths.js +192 -0
  806. package/dist/paths.js.map +1 -0
  807. package/dist/planner.d.ts +30 -0
  808. package/dist/planner.js +270 -0
  809. package/dist/planner.js.map +1 -0
  810. package/dist/preview/agent-address.d.ts +34 -0
  811. package/dist/preview/agent-address.js +52 -0
  812. package/dist/preview/agent-address.js.map +1 -0
  813. package/dist/preview/agent-marker.d.ts +17 -0
  814. package/dist/preview/agent-marker.js +71 -0
  815. package/dist/preview/agent-marker.js.map +1 -0
  816. package/dist/preview/browser-launch.d.ts +28 -0
  817. package/dist/preview/browser-launch.js +64 -0
  818. package/dist/preview/browser-launch.js.map +1 -0
  819. package/dist/preview/cdp-proxy.d.ts +54 -0
  820. package/dist/preview/cdp-proxy.js +152 -0
  821. package/dist/preview/cdp-proxy.js.map +1 -0
  822. package/dist/preview/cdp.d.ts +49 -0
  823. package/dist/preview/cdp.js +167 -0
  824. package/dist/preview/cdp.js.map +1 -0
  825. package/dist/preview/chromium-launch-progress.d.ts +10 -0
  826. package/dist/preview/chromium-launch-progress.js +48 -0
  827. package/dist/preview/chromium-launch-progress.js.map +1 -0
  828. package/dist/preview/chromium.d.ts +91 -0
  829. package/dist/preview/chromium.js +500 -0
  830. package/dist/preview/chromium.js.map +1 -0
  831. package/dist/preview/density.d.ts +17 -0
  832. package/dist/preview/density.js +62 -0
  833. package/dist/preview/density.js.map +1 -0
  834. package/dist/preview/dev-server.d.ts +68 -0
  835. package/dist/preview/dev-server.js +318 -0
  836. package/dist/preview/dev-server.js.map +1 -0
  837. package/dist/preview/host.d.ts +215 -0
  838. package/dist/preview/host.js +954 -0
  839. package/dist/preview/host.js.map +1 -0
  840. package/dist/preview/registration.d.ts +68 -0
  841. package/dist/preview/registration.js +111 -0
  842. package/dist/preview/registration.js.map +1 -0
  843. package/dist/preview/run-tools.d.ts +9 -0
  844. package/dist/preview/run-tools.js +72 -0
  845. package/dist/preview/run-tools.js.map +1 -0
  846. package/dist/preview/session.d.ts +139 -0
  847. package/dist/preview/session.js +479 -0
  848. package/dist/preview/session.js.map +1 -0
  849. package/dist/preview/url.d.ts +6 -0
  850. package/dist/preview/url.js +17 -0
  851. package/dist/preview/url.js.map +1 -0
  852. package/dist/process-wait/exit-file.d.ts +29 -0
  853. package/dist/process-wait/exit-file.js +67 -0
  854. package/dist/process-wait/exit-file.js.map +1 -0
  855. package/dist/process-wait/supervisor.d.ts +30 -0
  856. package/dist/process-wait/supervisor.js +71 -0
  857. package/dist/process-wait/supervisor.js.map +1 -0
  858. package/dist/project-commands.d.ts +14 -0
  859. package/dist/project-commands.js +91 -0
  860. package/dist/project-commands.js.map +1 -0
  861. package/dist/project-migration/answer.d.ts +23 -0
  862. package/dist/project-migration/answer.js +37 -0
  863. package/dist/project-migration/answer.js.map +1 -0
  864. package/dist/project-migration/auto.d.ts +73 -0
  865. package/dist/project-migration/auto.js +161 -0
  866. package/dist/project-migration/auto.js.map +1 -0
  867. package/dist/project-migration/cli.d.ts +18 -0
  868. package/dist/project-migration/cli.js +57 -0
  869. package/dist/project-migration/cli.js.map +1 -0
  870. package/dist/project-migration/containment.d.ts +52 -0
  871. package/dist/project-migration/containment.js +200 -0
  872. package/dist/project-migration/containment.js.map +1 -0
  873. package/dist/project-migration/durable.d.ts +25 -0
  874. package/dist/project-migration/durable.js +69 -0
  875. package/dist/project-migration/durable.js.map +1 -0
  876. package/dist/project-migration/engine.d.ts +47 -0
  877. package/dist/project-migration/engine.js +103 -0
  878. package/dist/project-migration/engine.js.map +1 -0
  879. package/dist/project-migration/fresh.d.ts +4 -0
  880. package/dist/project-migration/fresh.js +25 -0
  881. package/dist/project-migration/fresh.js.map +1 -0
  882. package/dist/project-migration/journal.d.ts +139 -0
  883. package/dist/project-migration/journal.js +69 -0
  884. package/dist/project-migration/journal.js.map +1 -0
  885. package/dist/project-migration/live.d.ts +48 -0
  886. package/dist/project-migration/live.js +148 -0
  887. package/dist/project-migration/live.js.map +1 -0
  888. package/dist/project-migration/offline.d.ts +25 -0
  889. package/dist/project-migration/offline.js +54 -0
  890. package/dist/project-migration/offline.js.map +1 -0
  891. package/dist/project-migration/plan.d.ts +91 -0
  892. package/dist/project-migration/plan.js +466 -0
  893. package/dist/project-migration/plan.js.map +1 -0
  894. package/dist/project-migration/recovery.d.ts +28 -0
  895. package/dist/project-migration/recovery.js +78 -0
  896. package/dist/project-migration/recovery.js.map +1 -0
  897. package/dist/project-migration/steps.d.ts +18 -0
  898. package/dist/project-migration/steps.js +312 -0
  899. package/dist/project-migration/steps.js.map +1 -0
  900. package/dist/project-paths.d.ts +61 -0
  901. package/dist/project-paths.js +185 -0
  902. package/dist/project-paths.js.map +1 -0
  903. package/dist/release/compatibility.d.ts +12 -0
  904. package/dist/release/compatibility.js +62 -0
  905. package/dist/release/compatibility.js.map +1 -0
  906. package/dist/release/manifests.d.ts +112 -0
  907. package/dist/release/manifests.js +104 -0
  908. package/dist/release/manifests.js.map +1 -0
  909. package/dist/release/publication.d.ts +3 -0
  910. package/dist/release/publication.js +34 -0
  911. package/dist/release/publication.js.map +1 -0
  912. package/dist/release/snapshot.d.ts +67 -0
  913. package/dist/release/snapshot.js +97 -0
  914. package/dist/release/snapshot.js.map +1 -0
  915. package/dist/release/stable.d.ts +38 -0
  916. package/dist/release/stable.js +55 -0
  917. package/dist/release/stable.js.map +1 -0
  918. package/dist/remote-settings-cli.d.ts +24 -0
  919. package/dist/remote-settings-cli.js +156 -0
  920. package/dist/remote-settings-cli.js.map +1 -0
  921. package/dist/remote-settings.d.ts +41 -0
  922. package/dist/remote-settings.js +110 -0
  923. package/dist/remote-settings.js.map +1 -0
  924. package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
  925. package/dist/runs/agent-tmpdir-removal.js +60 -0
  926. package/dist/runs/agent-tmpdir-removal.js.map +1 -0
  927. package/dist/runs/agent-tmpdir.d.ts +119 -0
  928. package/dist/runs/agent-tmpdir.js +855 -0
  929. package/dist/runs/agent-tmpdir.js.map +1 -0
  930. package/dist/runs/archive-family.d.ts +33 -0
  931. package/dist/runs/archive-family.js +90 -0
  932. package/dist/runs/archive-family.js.map +1 -0
  933. package/dist/runs/arm-repo-handle.d.ts +10 -0
  934. package/dist/runs/arm-repo-handle.js +45 -0
  935. package/dist/runs/arm-repo-handle.js.map +1 -0
  936. package/dist/runs/auto-name.d.ts +77 -0
  937. package/dist/runs/auto-name.js +179 -0
  938. package/dist/runs/auto-name.js.map +1 -0
  939. package/dist/runs/claim-families.d.ts +37 -0
  940. package/dist/runs/claim-families.js +101 -0
  941. package/dist/runs/claim-families.js.map +1 -0
  942. package/dist/runs/client-request.d.ts +11 -0
  943. package/dist/runs/client-request.js +26 -0
  944. package/dist/runs/client-request.js.map +1 -0
  945. package/dist/runs/cold-reads.d.ts +40 -0
  946. package/dist/runs/cold-reads.js +56 -0
  947. package/dist/runs/cold-reads.js.map +1 -0
  948. package/dist/runs/conversation-identity.d.ts +19 -0
  949. package/dist/runs/conversation-identity.js +31 -0
  950. package/dist/runs/conversation-identity.js.map +1 -0
  951. package/dist/runs/delegation-state.d.ts +409 -0
  952. package/dist/runs/delegation-state.js +22 -0
  953. package/dist/runs/delegation-state.js.map +1 -0
  954. package/dist/runs/event-history-agent-context.d.ts +15 -0
  955. package/dist/runs/event-history-agent-context.js +119 -0
  956. package/dist/runs/event-history-agent-context.js.map +1 -0
  957. package/dist/runs/event-history-context.d.ts +15 -0
  958. package/dist/runs/event-history-context.js +109 -0
  959. package/dist/runs/event-history-context.js.map +1 -0
  960. package/dist/runs/event-history.d.ts +53 -0
  961. package/dist/runs/event-history.js +563 -0
  962. package/dist/runs/event-history.js.map +1 -0
  963. package/dist/runs/exit-events.d.ts +27 -0
  964. package/dist/runs/exit-events.js +46 -0
  965. package/dist/runs/exit-events.js.map +1 -0
  966. package/dist/runs/history-compressor.d.ts +29 -0
  967. package/dist/runs/history-compressor.js +116 -0
  968. package/dist/runs/history-compressor.js.map +1 -0
  969. package/dist/runs/history-file.d.ts +36 -0
  970. package/dist/runs/history-file.js +464 -0
  971. package/dist/runs/history-file.js.map +1 -0
  972. package/dist/runs/history-stream.d.ts +2 -0
  973. package/dist/runs/history-stream.js +62 -0
  974. package/dist/runs/history-stream.js.map +1 -0
  975. package/dist/runs/legacy-index.d.ts +49 -0
  976. package/dist/runs/legacy-index.js +156 -0
  977. package/dist/runs/legacy-index.js.map +1 -0
  978. package/dist/runs/legacy-run-rows.d.ts +12 -0
  979. package/dist/runs/legacy-run-rows.js +33 -0
  980. package/dist/runs/legacy-run-rows.js.map +1 -0
  981. package/dist/runs/legacy-waits.d.ts +64 -0
  982. package/dist/runs/legacy-waits.js +335 -0
  983. package/dist/runs/legacy-waits.js.map +1 -0
  984. package/dist/runs/live-item-snapshots.d.ts +15 -0
  985. package/dist/runs/live-item-snapshots.js +95 -0
  986. package/dist/runs/live-item-snapshots.js.map +1 -0
  987. package/dist/runs/loaded-run-exits.d.ts +6 -0
  988. package/dist/runs/loaded-run-exits.js +32 -0
  989. package/dist/runs/loaded-run-exits.js.map +1 -0
  990. package/dist/runs/loaded-run-healing.d.ts +3 -0
  991. package/dist/runs/loaded-run-healing.js +17 -0
  992. package/dist/runs/loaded-run-healing.js.map +1 -0
  993. package/dist/runs/outcome-commit.d.ts +119 -0
  994. package/dist/runs/outcome-commit.js +155 -0
  995. package/dist/runs/outcome-commit.js.map +1 -0
  996. package/dist/runs/owned-run-create.d.ts +33 -0
  997. package/dist/runs/owned-run-create.js +98 -0
  998. package/dist/runs/owned-run-create.js.map +1 -0
  999. package/dist/runs/project-run-connection.d.ts +76 -0
  1000. package/dist/runs/project-run-connection.js +287 -0
  1001. package/dist/runs/project-run-connection.js.map +1 -0
  1002. package/dist/runs/project-run-sources.d.ts +21 -0
  1003. package/dist/runs/project-run-sources.js +86 -0
  1004. package/dist/runs/project-run-sources.js.map +1 -0
  1005. package/dist/runs/pull-requests.d.ts +14 -0
  1006. package/dist/runs/pull-requests.js +96 -0
  1007. package/dist/runs/pull-requests.js.map +1 -0
  1008. package/dist/runs/raw-record.d.ts +40 -0
  1009. package/dist/runs/raw-record.js +133 -0
  1010. package/dist/runs/raw-record.js.map +1 -0
  1011. package/dist/runs/record-write-rules.d.ts +129 -0
  1012. package/dist/runs/record-write-rules.js +326 -0
  1013. package/dist/runs/record-write-rules.js.map +1 -0
  1014. package/dist/runs/report-rows.d.ts +36 -0
  1015. package/dist/runs/report-rows.js +102 -0
  1016. package/dist/runs/report-rows.js.map +1 -0
  1017. package/dist/runs/retention.d.ts +91 -0
  1018. package/dist/runs/retention.js +193 -0
  1019. package/dist/runs/retention.js.map +1 -0
  1020. package/dist/runs/review-gate.d.ts +18 -0
  1021. package/dist/runs/review-gate.js +22 -0
  1022. package/dist/runs/review-gate.js.map +1 -0
  1023. package/dist/runs/run-claims.d.ts +30 -0
  1024. package/dist/runs/run-claims.js +52 -0
  1025. package/dist/runs/run-claims.js.map +1 -0
  1026. package/dist/runs/run-database-peer-page.d.ts +12 -0
  1027. package/dist/runs/run-database-peer-page.js +25 -0
  1028. package/dist/runs/run-database-peer-page.js.map +1 -0
  1029. package/dist/runs/run-database-row.d.ts +39 -0
  1030. package/dist/runs/run-database-row.js +83 -0
  1031. package/dist/runs/run-database-row.js.map +1 -0
  1032. package/dist/runs/run-database-version.d.ts +14 -0
  1033. package/dist/runs/run-database-version.js +19 -0
  1034. package/dist/runs/run-database-version.js.map +1 -0
  1035. package/dist/runs/run-database.d.ts +389 -0
  1036. package/dist/runs/run-database.js +957 -0
  1037. package/dist/runs/run-database.js.map +1 -0
  1038. package/dist/runs/run-exits.d.ts +4510 -0
  1039. package/dist/runs/run-exits.js +191 -0
  1040. package/dist/runs/run-exits.js.map +1 -0
  1041. package/dist/runs/run-index.d.ts +36 -0
  1042. package/dist/runs/run-index.js +262 -0
  1043. package/dist/runs/run-index.js.map +1 -0
  1044. package/dist/runs/run-references.d.ts +61 -0
  1045. package/dist/runs/run-references.js +421 -0
  1046. package/dist/runs/run-references.js.map +1 -0
  1047. package/dist/runs/run-row.d.ts +14 -0
  1048. package/dist/runs/run-row.js +56 -0
  1049. package/dist/runs/run-row.js.map +1 -0
  1050. package/dist/runs/run-summary-page.d.ts +36 -0
  1051. package/dist/runs/run-summary-page.js +46 -0
  1052. package/dist/runs/run-summary-page.js.map +1 -0
  1053. package/dist/runs/run-title.d.ts +8 -0
  1054. package/dist/runs/run-title.js +28 -0
  1055. package/dist/runs/run-title.js.map +1 -0
  1056. package/dist/runs/store-open-error.d.ts +24 -0
  1057. package/dist/runs/store-open-error.js +62 -0
  1058. package/dist/runs/store-open-error.js.map +1 -0
  1059. package/dist/runs/store.d.ts +3974 -0
  1060. package/dist/runs/store.js +3516 -0
  1061. package/dist/runs/store.js.map +1 -0
  1062. package/dist/runs/task-markers.d.ts +29 -0
  1063. package/dist/runs/task-markers.js +78 -0
  1064. package/dist/runs/task-markers.js.map +1 -0
  1065. package/dist/runs/task-refs.d.ts +39 -0
  1066. package/dist/runs/task-refs.js +107 -0
  1067. package/dist/runs/task-refs.js.map +1 -0
  1068. package/dist/runs/transcript-facts-fold.d.ts +64 -0
  1069. package/dist/runs/transcript-facts-fold.js +109 -0
  1070. package/dist/runs/transcript-facts-fold.js.map +1 -0
  1071. package/dist/runs/transcript-facts-load.d.ts +11 -0
  1072. package/dist/runs/transcript-facts-load.js +66 -0
  1073. package/dist/runs/transcript-facts-load.js.map +1 -0
  1074. package/dist/runs/transcript-facts-queue.d.ts +30 -0
  1075. package/dist/runs/transcript-facts-queue.js +227 -0
  1076. package/dist/runs/transcript-facts-queue.js.map +1 -0
  1077. package/dist/runs/transcript-facts-worker.d.ts +15 -0
  1078. package/dist/runs/transcript-facts-worker.js +26 -0
  1079. package/dist/runs/transcript-facts-worker.js.map +1 -0
  1080. package/dist/runs/transcript-facts.d.ts +40 -0
  1081. package/dist/runs/transcript-facts.js +193 -0
  1082. package/dist/runs/transcript-facts.js.map +1 -0
  1083. package/dist/runs/ui-event-sink.d.ts +59 -0
  1084. package/dist/runs/ui-event-sink.js +197 -0
  1085. package/dist/runs/ui-event-sink.js.map +1 -0
  1086. package/dist/runs/wait-capacity.d.ts +8 -0
  1087. package/dist/runs/wait-capacity.js +28 -0
  1088. package/dist/runs/wait-capacity.js.map +1 -0
  1089. package/dist/runs/wait-phases.d.ts +4 -0
  1090. package/dist/runs/wait-phases.js +11 -0
  1091. package/dist/runs/wait-phases.js.map +1 -0
  1092. package/dist/runs/wait-registration-write.d.ts +5 -0
  1093. package/dist/runs/wait-registration-write.js +35 -0
  1094. package/dist/runs/wait-registration-write.js.map +1 -0
  1095. package/dist/runs/webhook.d.ts +193 -0
  1096. package/dist/runs/webhook.js +362 -0
  1097. package/dist/runs/webhook.js.map +1 -0
  1098. package/dist/runs/worker-deletion-proof.d.ts +13 -0
  1099. package/dist/runs/worker-deletion-proof.js +13 -0
  1100. package/dist/runs/worker-deletion-proof.js.map +1 -0
  1101. package/dist/runs/worker-execution.d.ts +22 -0
  1102. package/dist/runs/worker-execution.js +30 -0
  1103. package/dist/runs/worker-execution.js.map +1 -0
  1104. package/dist/runs/worker-resource-holders.d.ts +17 -0
  1105. package/dist/runs/worker-resource-holders.js +34 -0
  1106. package/dist/runs/worker-resource-holders.js.map +1 -0
  1107. package/dist/runs/worker-result-commit.d.ts +26 -0
  1108. package/dist/runs/worker-result-commit.js +121 -0
  1109. package/dist/runs/worker-result-commit.js.map +1 -0
  1110. package/dist/self-update/checkouts.d.ts +46 -0
  1111. package/dist/self-update/checkouts.js +284 -0
  1112. package/dist/self-update/checkouts.js.map +1 -0
  1113. package/dist/self-update/cli.d.ts +19 -0
  1114. package/dist/self-update/cli.js +243 -0
  1115. package/dist/self-update/cli.js.map +1 -0
  1116. package/dist/self-update/installer.d.ts +24 -0
  1117. package/dist/self-update/installer.js +139 -0
  1118. package/dist/self-update/installer.js.map +1 -0
  1119. package/dist/self-update/launcher.d.ts +30 -0
  1120. package/dist/self-update/launcher.js +172 -0
  1121. package/dist/self-update/launcher.js.map +1 -0
  1122. package/dist/self-update/layout.d.ts +108 -0
  1123. package/dist/self-update/layout.js +276 -0
  1124. package/dist/self-update/layout.js.map +1 -0
  1125. package/dist/self-update/pulls.d.ts +30 -0
  1126. package/dist/self-update/pulls.js +128 -0
  1127. package/dist/self-update/pulls.js.map +1 -0
  1128. package/dist/self-update/registry.d.ts +37 -0
  1129. package/dist/self-update/registry.js +86 -0
  1130. package/dist/self-update/registry.js.map +1 -0
  1131. package/dist/self-update/restart.d.ts +25 -0
  1132. package/dist/self-update/restart.js +79 -0
  1133. package/dist/self-update/restart.js.map +1 -0
  1134. package/dist/self-update/semver.d.ts +19 -0
  1135. package/dist/self-update/semver.js +79 -0
  1136. package/dist/self-update/semver.js.map +1 -0
  1137. package/dist/self-update/service.d.ts +115 -0
  1138. package/dist/self-update/service.js +332 -0
  1139. package/dist/self-update/service.js.map +1 -0
  1140. package/dist/self-update/supervisor.d.ts +10 -0
  1141. package/dist/self-update/supervisor.js +52 -0
  1142. package/dist/self-update/supervisor.js.map +1 -0
  1143. package/dist/serve-boot.d.ts +60 -0
  1144. package/dist/serve-boot.js +142 -0
  1145. package/dist/serve-boot.js.map +1 -0
  1146. package/dist/serve-command.d.ts +71 -0
  1147. package/dist/serve-command.js +168 -0
  1148. package/dist/serve-command.js.map +1 -0
  1149. package/dist/serve-listener.d.ts +12 -0
  1150. package/dist/serve-listener.js +52 -0
  1151. package/dist/serve-listener.js.map +1 -0
  1152. package/dist/server/activity-project-topic.d.ts +44 -0
  1153. package/dist/server/activity-project-topic.js +137 -0
  1154. package/dist/server/activity-project-topic.js.map +1 -0
  1155. package/dist/server/activity-routes.d.ts +317 -0
  1156. package/dist/server/activity-routes.js +117 -0
  1157. package/dist/server/activity-routes.js.map +1 -0
  1158. package/dist/server/activity-sources.d.ts +26 -0
  1159. package/dist/server/activity-sources.js +45 -0
  1160. package/dist/server/activity-sources.js.map +1 -0
  1161. package/dist/server/activity-topic.d.ts +33 -0
  1162. package/dist/server/activity-topic.js +48 -0
  1163. package/dist/server/activity-topic.js.map +1 -0
  1164. package/dist/server/app-type.d.ts +6 -0
  1165. package/dist/server/app-type.js +2 -0
  1166. package/dist/server/app-type.js.map +1 -0
  1167. package/dist/server/archive-on-merge.d.ts +45 -0
  1168. package/dist/server/archive-on-merge.js +242 -0
  1169. package/dist/server/archive-on-merge.js.map +1 -0
  1170. package/dist/server/automation-log-retry.d.ts +27 -0
  1171. package/dist/server/automation-log-retry.js +88 -0
  1172. package/dist/server/automation-log-retry.js.map +1 -0
  1173. package/dist/server/capabilities.d.ts +69 -0
  1174. package/dist/server/capabilities.js +111 -0
  1175. package/dist/server/capabilities.js.map +1 -0
  1176. package/dist/server/checkout-progress.d.ts +10 -0
  1177. package/dist/server/checkout-progress.js +27 -0
  1178. package/dist/server/checkout-progress.js.map +1 -0
  1179. package/dist/server/checkout.d.ts +153 -0
  1180. package/dist/server/checkout.js +323 -0
  1181. package/dist/server/checkout.js.map +1 -0
  1182. package/dist/server/cockpit-ownership.d.ts +112 -0
  1183. package/dist/server/cockpit-ownership.js +420 -0
  1184. package/dist/server/cockpit-ownership.js.map +1 -0
  1185. package/dist/server/cold-repo-handles.d.ts +17 -0
  1186. package/dist/server/cold-repo-handles.js +82 -0
  1187. package/dist/server/cold-repo-handles.js.map +1 -0
  1188. package/dist/server/completion-actions.d.ts +37 -0
  1189. package/dist/server/completion-actions.js +261 -0
  1190. package/dist/server/completion-actions.js.map +1 -0
  1191. package/dist/server/config-merge.d.ts +11 -0
  1192. package/dist/server/config-merge.js +74 -0
  1193. package/dist/server/config-merge.js.map +1 -0
  1194. package/dist/server/create-project.d.ts +71 -0
  1195. package/dist/server/create-project.js +467 -0
  1196. package/dist/server/create-project.js.map +1 -0
  1197. package/dist/server/forge/draft-pr.d.ts +8 -0
  1198. package/dist/server/forge/draft-pr.js +123 -0
  1199. package/dist/server/forge/draft-pr.js.map +1 -0
  1200. package/dist/server/forge/github-filters.d.ts +22 -0
  1201. package/dist/server/forge/github-filters.js +128 -0
  1202. package/dist/server/forge/github-filters.js.map +1 -0
  1203. package/dist/server/forge/github-merge-requirements.d.ts +18 -0
  1204. package/dist/server/forge/github-merge-requirements.js +148 -0
  1205. package/dist/server/forge/github-merge-requirements.js.map +1 -0
  1206. package/dist/server/forge/github-merge-state.d.ts +27 -0
  1207. package/dist/server/forge/github-merge-state.js +248 -0
  1208. package/dist/server/forge/github-merge-state.js.map +1 -0
  1209. package/dist/server/forge/github-timeline.d.ts +117 -0
  1210. package/dist/server/forge/github-timeline.js +273 -0
  1211. package/dist/server/forge/github-timeline.js.map +1 -0
  1212. package/dist/server/forge/github.d.ts +393 -0
  1213. package/dist/server/forge/github.js +2881 -0
  1214. package/dist/server/forge/github.js.map +1 -0
  1215. package/dist/server/forge/index.d.ts +47 -0
  1216. package/dist/server/forge/index.js +81 -0
  1217. package/dist/server/forge/index.js.map +1 -0
  1218. package/dist/server/forge/repo-handle-discovery.d.ts +16 -0
  1219. package/dist/server/forge/repo-handle-discovery.js +2 -0
  1220. package/dist/server/forge/repo-handle-discovery.js.map +1 -0
  1221. package/dist/server/forge/types.d.ts +278 -0
  1222. package/dist/server/forge/types.js +2 -0
  1223. package/dist/server/forge/types.js.map +1 -0
  1224. package/dist/server/fs-browse.d.ts +82 -0
  1225. package/dist/server/fs-browse.js +217 -0
  1226. package/dist/server/fs-browse.js.map +1 -0
  1227. package/dist/server/git-changes.d.ts +205 -0
  1228. package/dist/server/git-changes.js +531 -0
  1229. package/dist/server/git-changes.js.map +1 -0
  1230. package/dist/server/git.d.ts +52 -0
  1231. package/dist/server/git.js +174 -0
  1232. package/dist/server/git.js.map +1 -0
  1233. package/dist/server/home-recovery-routes.d.ts +171 -0
  1234. package/dist/server/home-recovery-routes.js +56 -0
  1235. package/dist/server/home-recovery-routes.js.map +1 -0
  1236. package/dist/server/launch-key.d.ts +5 -0
  1237. package/dist/server/launch-key.js +35 -0
  1238. package/dist/server/launch-key.js.map +1 -0
  1239. package/dist/server/live-routes.d.ts +73 -0
  1240. package/dist/server/live-routes.js +161 -0
  1241. package/dist/server/live-routes.js.map +1 -0
  1242. package/dist/server/open-in-app.d.ts +57 -0
  1243. package/dist/server/open-in-app.js +178 -0
  1244. package/dist/server/open-in-app.js.map +1 -0
  1245. package/dist/server/open-in-targets.d.ts +53 -0
  1246. package/dist/server/open-in-targets.js +112 -0
  1247. package/dist/server/open-in-targets.js.map +1 -0
  1248. package/dist/server/open-in-terminal.d.ts +73 -0
  1249. package/dist/server/open-in-terminal.js +160 -0
  1250. package/dist/server/open-in-terminal.js.map +1 -0
  1251. package/dist/server/preview-cdp-socket.d.ts +21 -0
  1252. package/dist/server/preview-cdp-socket.js +112 -0
  1253. package/dist/server/preview-cdp-socket.js.map +1 -0
  1254. package/dist/server/preview-socket.d.ts +36 -0
  1255. package/dist/server/preview-socket.js +142 -0
  1256. package/dist/server/preview-socket.js.map +1 -0
  1257. package/dist/server/preview-sockets.d.ts +14 -0
  1258. package/dist/server/preview-sockets.js +18 -0
  1259. package/dist/server/preview-sockets.js.map +1 -0
  1260. package/dist/server/project-context.d.ts +218 -0
  1261. package/dist/server/project-context.js +525 -0
  1262. package/dist/server/project-context.js.map +1 -0
  1263. package/dist/server/project-run-files.d.ts +13 -0
  1264. package/dist/server/project-run-files.js +52 -0
  1265. package/dist/server/project-run-files.js.map +1 -0
  1266. package/dist/server/provider-action-gate.d.ts +5 -0
  1267. package/dist/server/provider-action-gate.js +28 -0
  1268. package/dist/server/provider-action-gate.js.map +1 -0
  1269. package/dist/server/provider-auth-runtime.d.ts +34 -0
  1270. package/dist/server/provider-auth-runtime.js +118 -0
  1271. package/dist/server/provider-auth-runtime.js.map +1 -0
  1272. package/dist/server/repo-branch-forge.d.ts +61 -0
  1273. package/dist/server/repo-branch-forge.js +158 -0
  1274. package/dist/server/repo-branch-forge.js.map +1 -0
  1275. package/dist/server/repo-branch-sweep.d.ts +21 -0
  1276. package/dist/server/repo-branch-sweep.js +64 -0
  1277. package/dist/server/repo-branch-sweep.js.map +1 -0
  1278. package/dist/server/repo-branches.d.ts +103 -0
  1279. package/dist/server/repo-branches.js +804 -0
  1280. package/dist/server/repo-branches.js.map +1 -0
  1281. package/dist/server/repo-pull.d.ts +18 -0
  1282. package/dist/server/repo-pull.js +97 -0
  1283. package/dist/server/repo-pull.js.map +1 -0
  1284. package/dist/server/reports-api.d.ts +90 -0
  1285. package/dist/server/reports-api.js +21 -0
  1286. package/dist/server/reports-api.js.map +1 -0
  1287. package/dist/server/reports-topic.d.ts +5 -0
  1288. package/dist/server/reports-topic.js +37 -0
  1289. package/dist/server/reports-topic.js.map +1 -0
  1290. package/dist/server/run-checkout.d.ts +56 -0
  1291. package/dist/server/run-checkout.js +141 -0
  1292. package/dist/server/run-checkout.js.map +1 -0
  1293. package/dist/server/run-event-feed.d.ts +20 -0
  1294. package/dist/server/run-event-feed.js +203 -0
  1295. package/dist/server/run-event-feed.js.map +1 -0
  1296. package/dist/server/self-update-routes.d.ts +308 -0
  1297. package/dist/server/self-update-routes.js +80 -0
  1298. package/dist/server/self-update-routes.js.map +1 -0
  1299. package/dist/server/server-deps.d.ts +40 -0
  1300. package/dist/server/server-deps.js +132 -0
  1301. package/dist/server/server-deps.js.map +1 -0
  1302. package/dist/server/server.d.ts +35332 -0
  1303. package/dist/server/server.js +5046 -0
  1304. package/dist/server/server.js.map +1 -0
  1305. package/dist/server/skill-sources.d.ts +264 -0
  1306. package/dist/server/skill-sources.js +130 -0
  1307. package/dist/server/skill-sources.js.map +1 -0
  1308. package/dist/server/start-run-options.d.ts +84 -0
  1309. package/dist/server/start-run-options.js +114 -0
  1310. package/dist/server/start-run-options.js.map +1 -0
  1311. package/dist/server/static-ui.d.ts +49 -0
  1312. package/dist/server/static-ui.js +99 -0
  1313. package/dist/server/static-ui.js.map +1 -0
  1314. package/dist/server/upgrade-router.d.ts +23 -0
  1315. package/dist/server/upgrade-router.js +53 -0
  1316. package/dist/server/upgrade-router.js.map +1 -0
  1317. package/dist/server/validators.d.ts +80 -0
  1318. package/dist/server/validators.js +85 -0
  1319. package/dist/server/validators.js.map +1 -0
  1320. package/dist/server/workflow-files.d.ts +9 -0
  1321. package/dist/server/workflow-files.js +27 -0
  1322. package/dist/server/workflow-files.js.map +1 -0
  1323. package/dist/server/workspace-config-patch.d.ts +11 -0
  1324. package/dist/server/workspace-config-patch.js +75 -0
  1325. package/dist/server/workspace-config-patch.js.map +1 -0
  1326. package/dist/server/workspace-live-routes.d.ts +222 -0
  1327. package/dist/server/workspace-live-routes.js +8 -0
  1328. package/dist/server/workspace-live-routes.js.map +1 -0
  1329. package/dist/server/workspace-projects.d.ts +51 -0
  1330. package/dist/server/workspace-projects.js +161 -0
  1331. package/dist/server/workspace-projects.js.map +1 -0
  1332. package/dist/server/workspace-run-feed.d.ts +66 -0
  1333. package/dist/server/workspace-run-feed.js +171 -0
  1334. package/dist/server/workspace-run-feed.js.map +1 -0
  1335. package/dist/server/ws.d.ts +80 -0
  1336. package/dist/server/ws.js +280 -0
  1337. package/dist/server/ws.js.map +1 -0
  1338. package/dist/server/wsl.d.ts +25 -0
  1339. package/dist/server/wsl.js +92 -0
  1340. package/dist/server/wsl.js.map +1 -0
  1341. package/dist/server-command.d.ts +53 -0
  1342. package/dist/server-command.js +180 -0
  1343. package/dist/server-command.js.map +1 -0
  1344. package/dist/server-install/engine.d.ts +51 -0
  1345. package/dist/server-install/engine.js +358 -0
  1346. package/dist/server-install/engine.js.map +1 -0
  1347. package/dist/server-install/group-migrate.d.ts +26 -0
  1348. package/dist/server-install/group-migrate.js +241 -0
  1349. package/dist/server-install/group-migrate.js.map +1 -0
  1350. package/dist/server-install/migrate-cli.d.ts +12 -0
  1351. package/dist/server-install/migrate-cli.js +44 -0
  1352. package/dist/server-install/migrate-cli.js.map +1 -0
  1353. package/dist/server-install/migrate-journal.d.ts +15 -0
  1354. package/dist/server-install/migrate-journal.js +48 -0
  1355. package/dist/server-install/migrate-journal.js.map +1 -0
  1356. package/dist/server-install/migration-report.d.ts +3 -0
  1357. package/dist/server-install/migration-report.js +21 -0
  1358. package/dist/server-install/migration-report.js.map +1 -0
  1359. package/dist/server-install/npx-cache.d.ts +34 -0
  1360. package/dist/server-install/npx-cache.js +142 -0
  1361. package/dist/server-install/npx-cache.js.map +1 -0
  1362. package/dist/server-install/platforms/macosx-launchd-files.d.ts +11 -0
  1363. package/dist/server-install/platforms/macosx-launchd-files.js +60 -0
  1364. package/dist/server-install/platforms/macosx-launchd-files.js.map +1 -0
  1365. package/dist/server-install/platforms/macosx-migration.d.ts +17 -0
  1366. package/dist/server-install/platforms/macosx-migration.js +239 -0
  1367. package/dist/server-install/platforms/macosx-migration.js.map +1 -0
  1368. package/dist/server-install/platforms/macosx-ngrok.d.ts +12 -0
  1369. package/dist/server-install/platforms/macosx-ngrok.js +490 -0
  1370. package/dist/server-install/platforms/macosx-ngrok.js.map +1 -0
  1371. package/dist/server-install/platforms/service-env.d.ts +4 -0
  1372. package/dist/server-install/platforms/service-env.js +17 -0
  1373. package/dist/server-install/platforms/service-env.js.map +1 -0
  1374. package/dist/server-install/platforms/ubuntu-vps-files.d.ts +18 -0
  1375. package/dist/server-install/platforms/ubuntu-vps-files.js +43 -0
  1376. package/dist/server-install/platforms/ubuntu-vps-files.js.map +1 -0
  1377. package/dist/server-install/platforms/ubuntu-vps-migration.d.ts +26 -0
  1378. package/dist/server-install/platforms/ubuntu-vps-migration.js +391 -0
  1379. package/dist/server-install/platforms/ubuntu-vps-migration.js.map +1 -0
  1380. package/dist/server-install/platforms/ubuntu-vps-names.d.ts +30 -0
  1381. package/dist/server-install/platforms/ubuntu-vps-names.js +50 -0
  1382. package/dist/server-install/platforms/ubuntu-vps-names.js.map +1 -0
  1383. package/dist/server-install/platforms/ubuntu-vps-redeploy.d.ts +42 -0
  1384. package/dist/server-install/platforms/ubuntu-vps-redeploy.js +127 -0
  1385. package/dist/server-install/platforms/ubuntu-vps-redeploy.js.map +1 -0
  1386. package/dist/server-install/platforms/ubuntu-vps.d.ts +57 -0
  1387. package/dist/server-install/platforms/ubuntu-vps.js +945 -0
  1388. package/dist/server-install/platforms/ubuntu-vps.js.map +1 -0
  1389. package/dist/server-install/state.d.ts +51 -0
  1390. package/dist/server-install/state.js +178 -0
  1391. package/dist/server-install/state.js.map +1 -0
  1392. package/dist/server-install/steps.d.ts +108 -0
  1393. package/dist/server-install/steps.js +348 -0
  1394. package/dist/server-install/steps.js.map +1 -0
  1395. package/dist/server-install/strategies.d.ts +6 -0
  1396. package/dist/server-install/strategies.js +24 -0
  1397. package/dist/server-install/strategies.js.map +1 -0
  1398. package/dist/server-install/types.d.ts +316 -0
  1399. package/dist/server-install/types.js +118 -0
  1400. package/dist/server-install/types.js.map +1 -0
  1401. package/dist/server-install/ui.d.ts +36 -0
  1402. package/dist/server-install/ui.js +131 -0
  1403. package/dist/server-install/ui.js.map +1 -0
  1404. package/dist/session-context.d.ts +66 -0
  1405. package/dist/session-context.js +266 -0
  1406. package/dist/session-context.js.map +1 -0
  1407. package/dist/skills-provision.d.ts +64 -0
  1408. package/dist/skills-provision.js +325 -0
  1409. package/dist/skills-provision.js.map +1 -0
  1410. package/dist/skills-remote.d.ts +144 -0
  1411. package/dist/skills-remote.js +847 -0
  1412. package/dist/skills-remote.js.map +1 -0
  1413. package/dist/skills.d.ts +81 -0
  1414. package/dist/skills.js +288 -0
  1415. package/dist/skills.js.map +1 -0
  1416. package/dist/subcommands.d.ts +12 -0
  1417. package/dist/subcommands.js +59 -0
  1418. package/dist/subcommands.js.map +1 -0
  1419. package/dist/task-cli/cli.d.ts +39 -0
  1420. package/dist/task-cli/cli.js +773 -0
  1421. package/dist/task-cli/cli.js.map +1 -0
  1422. package/dist/task-cli/discovery.d.ts +32 -0
  1423. package/dist/task-cli/discovery.js +133 -0
  1424. package/dist/task-cli/discovery.js.map +1 -0
  1425. package/dist/task-cli/hint.d.ts +20 -0
  1426. package/dist/task-cli/hint.js +15 -0
  1427. package/dist/task-cli/hint.js.map +1 -0
  1428. package/dist/task-cli/http.d.ts +42 -0
  1429. package/dist/task-cli/http.js +110 -0
  1430. package/dist/task-cli/http.js.map +1 -0
  1431. package/dist/task-cli/projections.d.ts +68 -0
  1432. package/dist/task-cli/projections.js +76 -0
  1433. package/dist/task-cli/projections.js.map +1 -0
  1434. package/dist/task-cli/watch.d.ts +119 -0
  1435. package/dist/task-cli/watch.js +494 -0
  1436. package/dist/task-cli/watch.js.map +1 -0
  1437. package/dist/ui-state.d.ts +41 -0
  1438. package/dist/ui-state.js +93 -0
  1439. package/dist/ui-state.js.map +1 -0
  1440. package/dist/update-check.d.ts +2 -0
  1441. package/dist/update-check.js +28 -0
  1442. package/dist/update-check.js.map +1 -0
  1443. package/dist/use-command-notice.d.ts +1 -0
  1444. package/dist/use-command-notice.js +6 -0
  1445. package/dist/use-command-notice.js.map +1 -0
  1446. package/dist/voice/decide.d.ts +35 -0
  1447. package/dist/voice/decide.js +329 -0
  1448. package/dist/voice/decide.js.map +1 -0
  1449. package/dist/voice/decision-log.d.ts +48 -0
  1450. package/dist/voice/decision-log.js +117 -0
  1451. package/dist/voice/decision-log.js.map +1 -0
  1452. package/dist/voice/engine.d.ts +119 -0
  1453. package/dist/voice/engine.js +61 -0
  1454. package/dist/voice/engine.js.map +1 -0
  1455. package/dist/voice/fake-engine.d.ts +10 -0
  1456. package/dist/voice/fake-engine.js +192 -0
  1457. package/dist/voice/fake-engine.js.map +1 -0
  1458. package/dist/voice/jev-engine.d.ts +17 -0
  1459. package/dist/voice/jev-engine.js +110 -0
  1460. package/dist/voice/jev-engine.js.map +1 -0
  1461. package/dist/voice/pending-ask.d.ts +17 -0
  1462. package/dist/voice/pending-ask.js +51 -0
  1463. package/dist/voice/pending-ask.js.map +1 -0
  1464. package/dist/voice/routes.d.ts +115 -0
  1465. package/dist/voice/routes.js +92 -0
  1466. package/dist/voice/routes.js.map +1 -0
  1467. package/dist/voice/sentence.d.ts +21 -0
  1468. package/dist/voice/sentence.js +61 -0
  1469. package/dist/voice/sentence.js.map +1 -0
  1470. package/dist/voice/thresholds.d.ts +28 -0
  1471. package/dist/voice/thresholds.js +29 -0
  1472. package/dist/voice/thresholds.js.map +1 -0
  1473. package/dist/webhooks/cli.d.ts +18 -0
  1474. package/dist/webhooks/cli.js +127 -0
  1475. package/dist/webhooks/cli.js.map +1 -0
  1476. package/dist/webhooks/prompt.d.ts +5 -0
  1477. package/dist/webhooks/prompt.js +26 -0
  1478. package/dist/webhooks/prompt.js.map +1 -0
  1479. package/dist/workflows/active-run.d.ts +46 -0
  1480. package/dist/workflows/active-run.js +46 -0
  1481. package/dist/workflows/active-run.js.map +1 -0
  1482. package/dist/workflows/attachment-path.d.ts +6 -0
  1483. package/dist/workflows/attachment-path.js +40 -0
  1484. package/dist/workflows/attachment-path.js.map +1 -0
  1485. package/dist/workflows/completion-policy.d.ts +98 -0
  1486. package/dist/workflows/completion-policy.js +77 -0
  1487. package/dist/workflows/completion-policy.js.map +1 -0
  1488. package/dist/workflows/continuation-admission.d.ts +1229 -0
  1489. package/dist/workflows/continuation-admission.js +277 -0
  1490. package/dist/workflows/continuation-admission.js.map +1 -0
  1491. package/dist/workflows/execution-account.d.ts +13 -0
  1492. package/dist/workflows/execution-account.js +18 -0
  1493. package/dist/workflows/execution-account.js.map +1 -0
  1494. package/dist/workflows/grouped-command.d.ts +31 -0
  1495. package/dist/workflows/grouped-command.js +147 -0
  1496. package/dist/workflows/grouped-command.js.map +1 -0
  1497. package/dist/workflows/input-acknowledgement.d.ts +233 -0
  1498. package/dist/workflows/input-acknowledgement.js +41 -0
  1499. package/dist/workflows/input-acknowledgement.js.map +1 -0
  1500. package/dist/workflows/line-order.d.ts +5 -0
  1501. package/dist/workflows/line-order.js +25 -0
  1502. package/dist/workflows/line-order.js.map +1 -0
  1503. package/dist/workflows/load.d.ts +19 -0
  1504. package/dist/workflows/load.js +74 -0
  1505. package/dist/workflows/load.js.map +1 -0
  1506. package/dist/workflows/outcome-batches.d.ts +32 -0
  1507. package/dist/workflows/outcome-batches.js +55 -0
  1508. package/dist/workflows/outcome-batches.js.map +1 -0
  1509. package/dist/workflows/pasted-content.d.ts +60 -0
  1510. package/dist/workflows/pasted-content.js +69 -0
  1511. package/dist/workflows/pasted-content.js.map +1 -0
  1512. package/dist/workflows/question-routing.d.ts +16 -0
  1513. package/dist/workflows/question-routing.js +28 -0
  1514. package/dist/workflows/question-routing.js.map +1 -0
  1515. package/dist/workflows/run/attachment-writer.d.ts +34 -0
  1516. package/dist/workflows/run/attachment-writer.js +98 -0
  1517. package/dist/workflows/run/attachment-writer.js.map +1 -0
  1518. package/dist/workflows/run/auto-resume.d.ts +91 -0
  1519. package/dist/workflows/run/auto-resume.js +247 -0
  1520. package/dist/workflows/run/auto-resume.js.map +1 -0
  1521. package/dist/workflows/run/cezar-tools.d.ts +45 -0
  1522. package/dist/workflows/run/cezar-tools.js +117 -0
  1523. package/dist/workflows/run/cezar-tools.js.map +1 -0
  1524. package/dist/workflows/run/completion-gate.d.ts +77 -0
  1525. package/dist/workflows/run/completion-gate.js +93 -0
  1526. package/dist/workflows/run/completion-gate.js.map +1 -0
  1527. package/dist/workflows/run/compose.d.ts +181 -0
  1528. package/dist/workflows/run/compose.js +413 -0
  1529. package/dist/workflows/run/compose.js.map +1 -0
  1530. package/dist/workflows/run/constants.d.ts +48 -0
  1531. package/dist/workflows/run/constants.js +49 -0
  1532. package/dist/workflows/run/constants.js.map +1 -0
  1533. package/dist/workflows/run/continuation-acceptance.d.ts +70 -0
  1534. package/dist/workflows/run/continuation-acceptance.js +188 -0
  1535. package/dist/workflows/run/continuation-acceptance.js.map +1 -0
  1536. package/dist/workflows/run/continuation-session.d.ts +130 -0
  1537. package/dist/workflows/run/continuation-session.js +597 -0
  1538. package/dist/workflows/run/continuation-session.js.map +1 -0
  1539. package/dist/workflows/run/drop-active.d.ts +4 -0
  1540. package/dist/workflows/run/drop-active.js +11 -0
  1541. package/dist/workflows/run/drop-active.js.map +1 -0
  1542. package/dist/workflows/run/family-reconciler.d.ts +78 -0
  1543. package/dist/workflows/run/family-reconciler.js +308 -0
  1544. package/dist/workflows/run/family-reconciler.js.map +1 -0
  1545. package/dist/workflows/run/human-waits.d.ts +85 -0
  1546. package/dist/workflows/run/human-waits.js +163 -0
  1547. package/dist/workflows/run/human-waits.js.map +1 -0
  1548. package/dist/workflows/run/inbox-claims.d.ts +38 -0
  1549. package/dist/workflows/run/inbox-claims.js +108 -0
  1550. package/dist/workflows/run/inbox-claims.js.map +1 -0
  1551. package/dist/workflows/run/input-batch.d.ts +15 -0
  1552. package/dist/workflows/run/input-batch.js +32 -0
  1553. package/dist/workflows/run/input-batch.js.map +1 -0
  1554. package/dist/workflows/run/input-delivery.d.ts +202 -0
  1555. package/dist/workflows/run/input-delivery.js +818 -0
  1556. package/dist/workflows/run/input-delivery.js.map +1 -0
  1557. package/dist/workflows/run/input-receipts.d.ts +65 -0
  1558. package/dist/workflows/run/input-receipts.js +221 -0
  1559. package/dist/workflows/run/input-receipts.js.map +1 -0
  1560. package/dist/workflows/run/input-timers.d.ts +10 -0
  1561. package/dist/workflows/run/input-timers.js +32 -0
  1562. package/dist/workflows/run/input-timers.js.map +1 -0
  1563. package/dist/workflows/run/launch-context.d.ts +62 -0
  1564. package/dist/workflows/run/launch-context.js +119 -0
  1565. package/dist/workflows/run/launch-context.js.map +1 -0
  1566. package/dist/workflows/run/launch-read-resolver.d.ts +510 -0
  1567. package/dist/workflows/run/launch-read-resolver.js +658 -0
  1568. package/dist/workflows/run/launch-read-resolver.js.map +1 -0
  1569. package/dist/workflows/run/launch-settings.d.ts +10 -0
  1570. package/dist/workflows/run/launch-settings.js +11 -0
  1571. package/dist/workflows/run/launch-settings.js.map +1 -0
  1572. package/dist/workflows/run/live-session-state.d.ts +49 -0
  1573. package/dist/workflows/run/live-session-state.js +83 -0
  1574. package/dist/workflows/run/live-session-state.js.map +1 -0
  1575. package/dist/workflows/run/maintenance-claims.d.ts +47 -0
  1576. package/dist/workflows/run/maintenance-claims.js +83 -0
  1577. package/dist/workflows/run/maintenance-claims.js.map +1 -0
  1578. package/dist/workflows/run/memory-guard.d.ts +25 -0
  1579. package/dist/workflows/run/memory-guard.js +64 -0
  1580. package/dist/workflows/run/memory-guard.js.map +1 -0
  1581. package/dist/workflows/run/opening.d.ts +30 -0
  1582. package/dist/workflows/run/opening.js +6 -0
  1583. package/dist/workflows/run/opening.js.map +1 -0
  1584. package/dist/workflows/run/pending-ask-facts.d.ts +10 -0
  1585. package/dist/workflows/run/pending-ask-facts.js +19 -0
  1586. package/dist/workflows/run/pending-ask-facts.js.map +1 -0
  1587. package/dist/workflows/run/prompts.d.ts +74 -0
  1588. package/dist/workflows/run/prompts.js +140 -0
  1589. package/dist/workflows/run/prompts.js.map +1 -0
  1590. package/dist/workflows/run/queued-input.d.ts +108 -0
  1591. package/dist/workflows/run/queued-input.js +249 -0
  1592. package/dist/workflows/run/queued-input.js.map +1 -0
  1593. package/dist/workflows/run/queued-revival.d.ts +44 -0
  1594. package/dist/workflows/run/queued-revival.js +124 -0
  1595. package/dist/workflows/run/queued-revival.js.map +1 -0
  1596. package/dist/workflows/run/readonly-collections.d.ts +24 -0
  1597. package/dist/workflows/run/readonly-collections.js +33 -0
  1598. package/dist/workflows/run/readonly-collections.js.map +1 -0
  1599. package/dist/workflows/run/repo-root-lease.d.ts +34 -0
  1600. package/dist/workflows/run/repo-root-lease.js +71 -0
  1601. package/dist/workflows/run/repo-root-lease.js.map +1 -0
  1602. package/dist/workflows/run/resolved-wait-input-batch.d.ts +7 -0
  1603. package/dist/workflows/run/resolved-wait-input-batch.js +23 -0
  1604. package/dist/workflows/run/resolved-wait-input-batch.js.map +1 -0
  1605. package/dist/workflows/run/run-cancel.d.ts +36 -0
  1606. package/dist/workflows/run/run-cancel.js +130 -0
  1607. package/dist/workflows/run/run-cancel.js.map +1 -0
  1608. package/dist/workflows/run/run-finish.d.ts +72 -0
  1609. package/dist/workflows/run/run-finish.js +256 -0
  1610. package/dist/workflows/run/run-finish.js.map +1 -0
  1611. package/dist/workflows/run/run-guards.d.ts +15 -0
  1612. package/dist/workflows/run/run-guards.js +39 -0
  1613. package/dist/workflows/run/run-guards.js.map +1 -0
  1614. package/dist/workflows/run/run-line.d.ts +187 -0
  1615. package/dist/workflows/run/run-line.js +438 -0
  1616. package/dist/workflows/run/run-line.js.map +1 -0
  1617. package/dist/workflows/run/run-recovery.d.ts +116 -0
  1618. package/dist/workflows/run/run-recovery.js +422 -0
  1619. package/dist/workflows/run/run-recovery.js.map +1 -0
  1620. package/dist/workflows/run/run-settlement.d.ts +177 -0
  1621. package/dist/workflows/run/run-settlement.js +504 -0
  1622. package/dist/workflows/run/run-settlement.js.map +1 -0
  1623. package/dist/workflows/run/run-skills.d.ts +114 -0
  1624. package/dist/workflows/run/run-skills.js +233 -0
  1625. package/dist/workflows/run/run-skills.js.map +1 -0
  1626. package/dist/workflows/run/run-start.d.ts +64 -0
  1627. package/dist/workflows/run/run-start.js +126 -0
  1628. package/dist/workflows/run/run-start.js.map +1 -0
  1629. package/dist/workflows/run/run-workspace.d.ts +25 -0
  1630. package/dist/workflows/run/run-workspace.js +66 -0
  1631. package/dist/workflows/run/run-workspace.js.map +1 -0
  1632. package/dist/workflows/run/session-events.d.ts +155 -0
  1633. package/dist/workflows/run/session-events.js +282 -0
  1634. package/dist/workflows/run/session-events.js.map +1 -0
  1635. package/dist/workflows/run/step-decisions.d.ts +37 -0
  1636. package/dist/workflows/run/step-decisions.js +43 -0
  1637. package/dist/workflows/run/step-decisions.js.map +1 -0
  1638. package/dist/workflows/run/step-loop.d.ts +143 -0
  1639. package/dist/workflows/run/step-loop.js +488 -0
  1640. package/dist/workflows/run/step-loop.js.map +1 -0
  1641. package/dist/workflows/run/step-session.d.ts +65 -0
  1642. package/dist/workflows/run/step-session.js +280 -0
  1643. package/dist/workflows/run/step-session.js.map +1 -0
  1644. package/dist/workflows/run/steps.d.ts +13 -0
  1645. package/dist/workflows/run/steps.js +14 -0
  1646. package/dist/workflows/run/steps.js.map +1 -0
  1647. package/dist/workflows/run/subagent-waits.d.ts +138 -0
  1648. package/dist/workflows/run/subagent-waits.js +305 -0
  1649. package/dist/workflows/run/subagent-waits.js.map +1 -0
  1650. package/dist/workflows/run/tracked-work.d.ts +12 -0
  1651. package/dist/workflows/run/tracked-work.js +29 -0
  1652. package/dist/workflows/run/tracked-work.js.map +1 -0
  1653. package/dist/workflows/run/turn-bookkeeping.d.ts +67 -0
  1654. package/dist/workflows/run/turn-bookkeeping.js +173 -0
  1655. package/dist/workflows/run/turn-bookkeeping.js.map +1 -0
  1656. package/dist/workflows/run/turn-boundary.d.ts +215 -0
  1657. package/dist/workflows/run/turn-boundary.js +329 -0
  1658. package/dist/workflows/run/turn-boundary.js.map +1 -0
  1659. package/dist/workflows/run/turn-markers.d.ts +62 -0
  1660. package/dist/workflows/run/turn-markers.js +86 -0
  1661. package/dist/workflows/run/turn-markers.js.map +1 -0
  1662. package/dist/workflows/run/turn-nudges.d.ts +104 -0
  1663. package/dist/workflows/run/turn-nudges.js +244 -0
  1664. package/dist/workflows/run/turn-nudges.js.map +1 -0
  1665. package/dist/workflows/run/turn-settler.d.ts +161 -0
  1666. package/dist/workflows/run/turn-settler.js +469 -0
  1667. package/dist/workflows/run/turn-settler.js.map +1 -0
  1668. package/dist/workflows/run/types.d.ts +293 -0
  1669. package/dist/workflows/run/types.js +2 -0
  1670. package/dist/workflows/run/types.js.map +1 -0
  1671. package/dist/workflows/run/usage-accounting.d.ts +49 -0
  1672. package/dist/workflows/run/usage-accounting.js +213 -0
  1673. package/dist/workflows/run/usage-accounting.js.map +1 -0
  1674. package/dist/workflows/run/wait-engine.d.ts +1210 -0
  1675. package/dist/workflows/run/wait-engine.js +456 -0
  1676. package/dist/workflows/run/wait-engine.js.map +1 -0
  1677. package/dist/workflows/run/wait-kinds.d.ts +336 -0
  1678. package/dist/workflows/run/wait-kinds.js +471 -0
  1679. package/dist/workflows/run/wait-kinds.js.map +1 -0
  1680. package/dist/workflows/run/wait-registration-tool.d.ts +3 -0
  1681. package/dist/workflows/run/wait-registration-tool.js +68 -0
  1682. package/dist/workflows/run/wait-registration-tool.js.map +1 -0
  1683. package/dist/workflows/run/wait-registration.d.ts +7 -0
  1684. package/dist/workflows/run/wait-registration.js +39 -0
  1685. package/dist/workflows/run/wait-registration.js.map +1 -0
  1686. package/dist/workflows/run/wait-registry.d.ts +120 -0
  1687. package/dist/workflows/run/wait-registry.js +365 -0
  1688. package/dist/workflows/run/wait-registry.js.map +1 -0
  1689. package/dist/workflows/run/wait-watches.d.ts +13 -0
  1690. package/dist/workflows/run/wait-watches.js +12 -0
  1691. package/dist/workflows/run/wait-watches.js.map +1 -0
  1692. package/dist/workflows/run/wake-admission.d.ts +150 -0
  1693. package/dist/workflows/run/wake-admission.js +397 -0
  1694. package/dist/workflows/run/wake-admission.js.map +1 -0
  1695. package/dist/workflows/run/wake-ledger.d.ts +61 -0
  1696. package/dist/workflows/run/wake-ledger.js +78 -0
  1697. package/dist/workflows/run/wake-ledger.js.map +1 -0
  1698. package/dist/workflows/run/wake-retries.d.ts +31 -0
  1699. package/dist/workflows/run/wake-retries.js +93 -0
  1700. package/dist/workflows/run/wake-retries.js.map +1 -0
  1701. package/dist/workflows/run/worker-executions.d.ts +54 -0
  1702. package/dist/workflows/run/worker-executions.js +149 -0
  1703. package/dist/workflows/run/worker-executions.js.map +1 -0
  1704. package/dist/workflows/run/worker-orphans.d.ts +91 -0
  1705. package/dist/workflows/run/worker-orphans.js +275 -0
  1706. package/dist/workflows/run/worker-orphans.js.map +1 -0
  1707. package/dist/workflows/run/worker-questions.d.ts +169 -0
  1708. package/dist/workflows/run/worker-questions.js +464 -0
  1709. package/dist/workflows/run/worker-questions.js.map +1 -0
  1710. package/dist/workflows/run/workflow-position.d.ts +7 -0
  1711. package/dist/workflows/run/workflow-position.js +9 -0
  1712. package/dist/workflows/run/workflow-position.js.map +1 -0
  1713. package/dist/workflows/run/worktree-autosave.d.ts +48 -0
  1714. package/dist/workflows/run/worktree-autosave.js +175 -0
  1715. package/dist/workflows/run/worktree-autosave.js.map +1 -0
  1716. package/dist/workflows/run-scheduling.d.ts +38 -0
  1717. package/dist/workflows/run-scheduling.js +76 -0
  1718. package/dist/workflows/run-scheduling.js.map +1 -0
  1719. package/dist/workflows/run-states.d.ts +73 -0
  1720. package/dist/workflows/run-states.js +233 -0
  1721. package/dist/workflows/run-states.js.map +1 -0
  1722. package/dist/workflows/run.d.ts +275 -0
  1723. package/dist/workflows/run.js +626 -0
  1724. package/dist/workflows/run.js.map +1 -0
  1725. package/dist/workflows/turn-end.d.ts +119 -0
  1726. package/dist/workflows/turn-end.js +74 -0
  1727. package/dist/workflows/turn-end.js.map +1 -0
  1728. package/dist/workflows/types.d.ts +83 -0
  1729. package/dist/workflows/types.js +192 -0
  1730. package/dist/workflows/types.js.map +1 -0
  1731. package/dist/workflows/wait-scope.d.ts +75 -0
  1732. package/dist/workflows/wait-scope.js +115 -0
  1733. package/dist/workflows/wait-scope.js.map +1 -0
  1734. package/dist/workflows/waits.d.ts +58 -0
  1735. package/dist/workflows/waits.js +89 -0
  1736. package/dist/workflows/waits.js.map +1 -0
  1737. package/dist/workspace/agent-accounts.d.ts +135 -0
  1738. package/dist/workspace/agent-accounts.js +353 -0
  1739. package/dist/workspace/agent-accounts.js.map +1 -0
  1740. package/dist/workspace/agent-profiles.d.ts +125 -0
  1741. package/dist/workspace/agent-profiles.js +190 -0
  1742. package/dist/workspace/agent-profiles.js.map +1 -0
  1743. package/dist/workspace/backup-cli.d.ts +9 -0
  1744. package/dist/workspace/backup-cli.js +147 -0
  1745. package/dist/workspace/backup-cli.js.map +1 -0
  1746. package/dist/workspace/backups.d.ts +36 -0
  1747. package/dist/workspace/backups.js +249 -0
  1748. package/dist/workspace/backups.js.map +1 -0
  1749. package/dist/workspace/config.d.ts +179 -0
  1750. package/dist/workspace/config.js +396 -0
  1751. package/dist/workspace/config.js.map +1 -0
  1752. package/dist/workspace/migrations.d.ts +37 -0
  1753. package/dist/workspace/migrations.js +149 -0
  1754. package/dist/workspace/migrations.js.map +1 -0
  1755. package/dist/workspace/projects-cli.d.ts +27 -0
  1756. package/dist/workspace/projects-cli.js +175 -0
  1757. package/dist/workspace/projects-cli.js.map +1 -0
  1758. package/dist/workspace/projects.d.ts +131 -0
  1759. package/dist/workspace/projects.js +274 -0
  1760. package/dist/workspace/projects.js.map +1 -0
  1761. package/dist/workspace/root-probe-cache.d.ts +10 -0
  1762. package/dist/workspace/root-probe-cache.js +43 -0
  1763. package/dist/workspace/root-probe-cache.js.map +1 -0
  1764. package/dist/workspace/semaphore.d.ts +147 -0
  1765. package/dist/workspace/semaphore.js +196 -0
  1766. package/dist/workspace/semaphore.js.map +1 -0
  1767. package/dist/workspace/ui-state.d.ts +17 -0
  1768. package/dist/workspace/ui-state.js +59 -0
  1769. package/dist/workspace/ui-state.js.map +1 -0
  1770. package/dist/workspace/webhooks.d.ts +40 -0
  1771. package/dist/workspace/webhooks.js +146 -0
  1772. package/dist/workspace/webhooks.js.map +1 -0
  1773. package/dist/worktree-manifest.d.ts +165 -0
  1774. package/dist/worktree-manifest.js +535 -0
  1775. package/dist/worktree-manifest.js.map +1 -0
  1776. package/dist/worktree-setup.d.ts +88 -0
  1777. package/dist/worktree-setup.js +170 -0
  1778. package/dist/worktree-setup.js.map +1 -0
  1779. package/package.json +80 -4
  1780. package/scripts/benchmark-family-reconcile.ts +119 -0
  1781. package/scripts/benchmark-home.ts +32 -0
  1782. package/scripts/benchmark-project-probes.mjs +158 -0
  1783. package/scripts/benchmark-run-store-fixture.ts +432 -0
  1784. package/scripts/benchmark-run-store.ts +767 -0
  1785. package/scripts/benchmark-stuck-destroys.ts +144 -0
  1786. package/scripts/benchmark-workspace-events.ts +339 -0
  1787. package/scripts/check-pack.mjs +48 -0
  1788. package/scripts/inline-contract.mjs +165 -0
  1789. package/scripts/install-as-command.mjs +125 -0
  1790. package/scripts/measure-transcript-facts-health.mjs +103 -0
  1791. package/scripts/measure-transcript-facts-lag.mjs +69 -0
  1792. package/scripts/mock-autonomous.mjs +13 -0
  1793. package/scripts/mock-ci-tool.mjs +159 -0
  1794. package/scripts/mock-claude-model-refusal.mjs +76 -0
  1795. package/scripts/mock-claude-subagents.mjs +54 -0
  1796. package/scripts/mock-claude.mjs +800 -0
  1797. package/scripts/mock-codex-app-server.mjs +767 -0
  1798. package/scripts/mock-codex-coalesce-steer.mjs +59 -0
  1799. package/scripts/mock-codex-reverse-steer.mjs +143 -0
  1800. package/scripts/mock-codex-subagents.mjs +52 -0
  1801. package/scripts/mock-controls.d.mts +11 -0
  1802. package/scripts/mock-controls.mjs +13 -0
  1803. package/scripts/mock-cursor-hang-stdin-late.mjs +35 -0
  1804. package/scripts/mock-cursor-hang-stdin.mjs +30 -0
  1805. package/scripts/mock-cursor-print.mjs +237 -0
  1806. package/scripts/mock-deferred-turn-start.mjs +16 -0
  1807. package/scripts/mock-delivery-control.d.mts +23 -0
  1808. package/scripts/mock-delivery-control.mjs +80 -0
  1809. package/scripts/mock-env-record.mjs +11 -0
  1810. package/scripts/mock-failure-control.d.mts +13 -0
  1811. package/scripts/mock-failure-control.mjs +68 -0
  1812. package/scripts/mock-lifecycle.mjs +51 -0
  1813. package/scripts/mock-multi-pr.mjs +10 -0
  1814. package/scripts/mock-nudge.mjs +43 -0
  1815. package/scripts/mock-omp-rpc.mjs +536 -0
  1816. package/scripts/mock-opencode-serve.mjs +1000 -0
  1817. package/scripts/mock-pi-rpc.mjs +421 -0
  1818. package/scripts/mock-publish.mjs +13 -0
  1819. package/scripts/mock-runner-crash.mjs +64 -0
  1820. package/scripts/mock-scenario.d.mts +71 -0
  1821. package/scripts/mock-scenario.mjs +295 -0
  1822. package/scripts/mock-session-context.d.mts +5 -0
  1823. package/scripts/mock-session-context.mjs +56 -0
  1824. package/scripts/mock-silent-tail.mjs +93 -0
  1825. package/scripts/mock-turn-messages.mjs +5 -0
  1826. package/scripts/mock-wire-evidence.config.mjs +12 -0
  1827. package/scripts/mock-wire-recorder.mjs +66 -0
  1828. package/scripts/mock-wire-tools.mjs +7 -0
  1829. package/scripts/omp-ci-wait.mjs +29 -0
  1830. package/scripts/opencode-task-scope.d.mts +18 -0
  1831. package/scripts/opencode-task-scope.mjs +59 -0
  1832. package/scripts/parity-rows.ts +160 -0
  1833. package/scripts/pi-ci-wait.mjs +17 -0
  1834. package/scripts/pi-default-tools.mjs +22 -0
  1835. package/scripts/pi-project-skills.mjs +59 -0
  1836. package/scripts/pi-retry-degraded-service.mjs +16 -0
  1837. package/scripts/probe-cursor-print.mjs +699 -0
  1838. package/scripts/probe-session-env.ts +8 -0
  1839. package/scripts/product-env.mjs +6 -0
  1840. package/scripts/product-identity.mjs +12 -0
  1841. package/scripts/record-mock-wires.mjs +16 -0
  1842. package/scripts/seed-archive-profile.ts +53 -0
  1843. package/scripts/sync-readme.mjs +20 -0
  1844. package/scripts/test-process-usage.mjs +47 -0
  1845. package/scripts/transcript-facts-fixture.mjs +53 -0
  1846. package/scripts/transcript-facts-timing.mjs +42 -0
  1847. package/scripts/voice-replay.ts +50 -0
  1848. package/scripts/windows-workspace-identity.cs +208 -0
  1849. package/scripts/windows-workspace-identity.ps1 +10 -0
  1850. package/web/dist/assets/activity-motion-0wHCeUs7.css +1 -0
  1851. package/web/dist/assets/activity-motion-SGth5vR4.js +2 -0
  1852. package/web/dist/assets/activity-route-D8otXwOA.js +1 -0
  1853. package/web/dist/assets/agent-accounts-6EuZhy-N.js +18 -0
  1854. package/web/dist/assets/alert-dialog-vFjLgfK4.js +1 -0
  1855. package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
  1856. package/web/dist/assets/arrow-right-Dh_Lx3um.js +1 -0
  1857. package/web/dist/assets/bundle-mjs-BT31bpU6.js +1 -0
  1858. package/web/dist/assets/button-BEK7XKhS.js +3 -0
  1859. package/web/dist/assets/centered-state-CPgI8cCO.js +1 -0
  1860. package/web/dist/assets/cez-idle-BT0U4GN_.js +1 -0
  1861. package/web/dist/assets/chunk-BO2N2NFS-DE6qKn3r.js +131 -0
  1862. package/web/dist/assets/commit-diff-panel-DzyWgn1Z.js +1 -0
  1863. package/web/dist/assets/control-locations-CexZsd3M.js +1 -0
  1864. package/web/dist/assets/core-BCsw8oQw.js +12 -0
  1865. package/web/dist/assets/createLucideIcon-BHp8sXmF.js +1 -0
  1866. package/web/dist/assets/css-BsVw1vtW.js +1 -0
  1867. package/web/dist/assets/design-icons-DmYTv2r1.js +1 -0
  1868. package/web/dist/assets/diff-DBDiZl58.js +3 -0
  1869. package/web/dist/assets/diff-controls-DXyB0aDl.js +1 -0
  1870. package/web/dist/assets/diff-view-DiKAwVMH.js +11 -0
  1871. package/web/dist/assets/diff-woXpYk--.js +1 -0
  1872. package/web/dist/assets/dropdown-menu-DYflHa4m.js +1 -0
  1873. package/web/dist/assets/engine-javascript-FQvzRocB.js +141 -0
  1874. package/web/dist/assets/es2015-BO600Xfi.js +41 -0
  1875. package/web/dist/assets/eye-B8okNWXU.js +1 -0
  1876. package/web/dist/assets/file-preview-DTSRkmMo.js +2 -0
  1877. package/web/dist/assets/git-toolbar-U4s46kUd.js +1 -0
  1878. package/web/dist/assets/github-UaBp2yRF.js +1 -0
  1879. package/web/dist/assets/github-sidebar-model-czjLsgVJ.js +1 -0
  1880. package/web/dist/assets/global-activity-route-B2N2Nz0H.js +1 -0
  1881. package/web/dist/assets/go-rLFTqkRN.js +1 -0
  1882. package/web/dist/assets/highlighted-body-OFNGDK62-BL-MfFgl.js +1 -0
  1883. package/web/dist/assets/hourglass-B0lPAJvT.js +1 -0
  1884. package/web/dist/assets/html-BY76lXLH.js +1 -0
  1885. package/web/dist/assets/image-preview-XPCPclpG.js +1 -0
  1886. package/web/dist/assets/index-B18AqW0Z.css +1 -0
  1887. package/web/dist/assets/index-xj-3O89_.js +14 -0
  1888. package/web/dist/assets/index.global-D3Ga_D3Y.js +1 -0
  1889. package/web/dist/assets/info-DA1x_4bz.js +1 -0
  1890. package/web/dist/assets/javascript-BgS3c2Ky.js +1 -0
  1891. package/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  1892. package/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  1893. package/web/dist/assets/json-qhed-kSA.js +1 -0
  1894. package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
  1895. package/web/dist/assets/jsx-Bz0zcwM4.js +1 -0
  1896. package/web/dist/assets/lib-BxQXEXDF.js +1 -0
  1897. package/web/dist/assets/live-protocol-BDY7JJpT.js +64 -0
  1898. package/web/dist/assets/live-refetch-C-Ony2Yr.js +1 -0
  1899. package/web/dist/assets/live-worker-BWdeGzQX.ts +34 -0
  1900. package/web/dist/assets/loader-circle-CZkGEdoI.js +1 -0
  1901. package/web/dist/assets/markdown-BBDDg-4t.js +1 -0
  1902. package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
  1903. package/web/dist/assets/markdown-UdZ_toya.js +6 -0
  1904. package/web/dist/assets/mermaid-GHXKKRXX-C4jxqUUb.js +1 -0
  1905. package/web/dist/assets/mermaid.min-3O9YjB2l.js +1 -0
  1906. package/web/dist/assets/message-square-B-MD-8rD.js +1 -0
  1907. package/web/dist/assets/nav-row-styles-BE99gD3_.js +1 -0
  1908. package/web/dist/assets/popover-CpULuQSm.js +1 -0
  1909. package/web/dist/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
  1910. package/web/dist/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
  1911. package/web/dist/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
  1912. package/web/dist/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
  1913. package/web/dist/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
  1914. package/web/dist/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
  1915. package/web/dist/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  1916. package/web/dist/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  1917. package/web/dist/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  1918. package/web/dist/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  1919. package/web/dist/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  1920. package/web/dist/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  1921. package/web/dist/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
  1922. package/web/dist/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
  1923. package/web/dist/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
  1924. package/web/dist/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
  1925. package/web/dist/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
  1926. package/web/dist/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
  1927. package/web/dist/assets/preview-pane-connected-CgRK7GzB.js +3 -0
  1928. package/web/dist/assets/project-router-CimAoN1H.js +1 -0
  1929. package/web/dist/assets/project-scope-context-DMpvJqIm.js +4 -0
  1930. package/web/dist/assets/python-gzcpVVnB.js +1 -0
  1931. package/web/dist/assets/queries-BIOp8jLx.js +1 -0
  1932. package/web/dist/assets/react-runtime-CCIEwYL0.js +9 -0
  1933. package/web/dist/assets/repo-git-DVXNOJDT.css +1 -0
  1934. package/web/dist/assets/repo-git-XkyTfP9G.js +1 -0
  1935. package/web/dist/assets/report-reader-CZjYS5Aw.js +1 -0
  1936. package/web/dist/assets/report-title-D4Mbu2Er.js +1 -0
  1937. package/web/dist/assets/reports-B7QAzibP.css +1 -0
  1938. package/web/dist/assets/reports-D_2sFE1y.js +1 -0
  1939. package/web/dist/assets/rolldown-runtime-QTnfLwEv.js +1 -0
  1940. package/web/dist/assets/run-header-BwPDVPpg.js +2 -0
  1941. package/web/dist/assets/run-header-CC7VUTg9.css +1 -0
  1942. package/web/dist/assets/rust-Cfkwpbl8.js +1 -0
  1943. package/web/dist/assets/shellscript-CLZ0U2zV.js +1 -0
  1944. package/web/dist/assets/skill-empty-hint-YDmkqgo-.js +1 -0
  1945. package/web/dist/assets/skills-BkXe4p4q.js +1 -0
  1946. package/web/dist/assets/skills-DFBiAuJG.js +1 -0
  1947. package/web/dist/assets/skills-workflows-C21NystW.css +1 -0
  1948. package/web/dist/assets/sortable.esm-zp_qkYGV.js +5 -0
  1949. package/web/dist/assets/sql-BsFa4tDR.js +1 -0
  1950. package/web/dist/assets/tab-link-BIweqlIZ.js +1 -0
  1951. package/web/dist/assets/task-changes-USNHNm_T.js +1 -0
  1952. package/web/dist/assets/task-commits-kHqlOJe_.js +1 -0
  1953. package/web/dist/assets/task-files-wTWwLR39.js +1 -0
  1954. package/web/dist/assets/task-github-item-GzaQjkF1.js +1 -0
  1955. package/web/dist/assets/task-thread-BZX863UN.js +9 -0
  1956. package/web/dist/assets/task-thread-DtQ6uGfQ.css +1 -0
  1957. package/web/dist/assets/textarea-FAUkHz1K.js +1 -0
  1958. package/web/dist/assets/thread-state-CBzx2hmX.js +4 -0
  1959. package/web/dist/assets/toaster-pthKGWiE.js +1 -0
  1960. package/web/dist/assets/toml-CcmNWLt0.js +1 -0
  1961. package/web/dist/assets/tsx-udAQXfEw.js +1 -0
  1962. package/web/dist/assets/typescript-bsJCZSQ-.js +1 -0
  1963. package/web/dist/assets/use-hand-to-agent-state-Bav9eT3b.js +1 -0
  1964. package/web/dist/assets/use-hand-to-agent-state-C7esPJ-N.css +1 -0
  1965. package/web/dist/assets/useInfiniteQuery-CQI4BO4C.js +1 -0
  1966. package/web/dist/assets/useQuery-Bkq-x4Id.js +1 -0
  1967. package/web/dist/assets/workflows-D871Gx--.js +7 -0
  1968. package/web/dist/assets/ws-DspW9wBH.js +1 -0
  1969. package/web/dist/assets/yaml-rwi0_p6S.js +1 -0
  1970. package/web/dist/assets/zoomable-image-DNc6ORaF.js +1 -0
  1971. package/web/dist/cezarion-lockup-dark.svg +9 -0
  1972. package/web/dist/cezarion-lockup-light.svg +9 -0
  1973. package/web/dist/cezarion-mark-dark.svg +4 -0
  1974. package/web/dist/cezarion-mark-light.svg +4 -0
  1975. package/web/dist/index.html +90 -0
  1976. package/web/dist/live-worker.js +4 -0
@@ -0,0 +1,2881 @@
1
+ import { productEnv } from '../../core/product-env.js';
2
+ import { createDraftPr } from './draft-pr.js';
3
+ export { createDraftPr, buildPrBody } from './draft-pr.js';
4
+ import { defined } from '../../core/defined.js';
5
+ import { activeWorkflowRunsArgs, parseActiveWorkflowRuns } from '../../ci-wait/github.js';
6
+ import { pullRequestKey } from '../../runs/pull-requests.js';
7
+ import { matchesGithubPullRequest, mergedPullRequestObservations } from '../../merge-observations.js';
8
+ import { randomUUID } from 'node:crypto';
9
+ import { execFile } from 'node:child_process';
10
+ import { promisify } from 'node:util';
11
+ import { z } from 'zod';
12
+ import { GITHUB_COMMENT_COUNTS_MAX, GITHUB_SEARCH_MAX, REFERENCE_STATUS_MAX } from '../../contract/index.js';
13
+ import { fetchIssueProjects, fetchRepositoryProjects, fetchViewerLogin } from './github-filters.js';
14
+ import { fetchGithubMergeRequirements } from './github-merge-requirements.js';
15
+ import { mergePrSchema, normalizeMergeState } from './github-merge-state.js';
16
+ import { fetchTimelinePages, normalizeEvents, TIMELINE_PER_PAGE } from './github-timeline.js';
17
+ export { normalizeMergeState } from './github-merge-state.js';
18
+ export { fetchTimelinePages, ghTimelineEventSchema, normalizeEvents, TIMELINE_BUDGET_MS, TIMELINE_EVENT_CAP, TIMELINE_EVENT_KINDS, TIMELINE_MAX_PAGES, TIMELINE_MIN_PAGE_MS, } from './github-timeline.js';
19
+ import { refNumberFromUrl } from '../../contract/index.js';
20
+ /**
21
+ * The GitHub forge driver — all `gh`-CLI logic in one place, moved here from
22
+ * `src/server/github.ts` (tab listing) and `src/server/pr.ts` (draft PRs)
23
+ * behind the `ForgeDriver` seam. Those modules remain as thin delegates.
24
+ * `/api/github`'s response shape is this driver's serialization and is
25
+ * protected by BACKWARD_COMPATIBILITY.md — additive changes only.
26
+ */
27
+ const exec = promisify(execFile);
28
+ export const GH_PR_DIFF_FILE_CAP = 300;
29
+ export const GH_PR_PATCH_CAP = 512 * 1024;
30
+ export const GH_PR_DIFF_JSON_CAP = 4 * 1024 * 1024;
31
+ const ghPrFileSchema = z.object({
32
+ filename: z.string().min(1),
33
+ previous_filename: z.string().optional(),
34
+ status: z.enum(['added', 'modified', 'removed', 'renamed', 'copied', 'changed']),
35
+ additions: z.number().int().nonnegative(),
36
+ deletions: z.number().int().nonnegative(),
37
+ patch: z.string().optional(),
38
+ });
39
+ const ghPrHeadSchema = z.object({ headRefOid: z.string().regex(/^[0-9a-f]{40}$/i) });
40
+ export class GithubPrNotFoundError extends Error {
41
+ }
42
+ const prDiffCache = new Map();
43
+ /** Fetch no more than the three GitHub pages represented by the public 300-file response cap. */
44
+ export async function fetchPrFilePages(runPage) {
45
+ const rows = [];
46
+ for (let page = 1; page <= GH_PR_DIFF_FILE_CAP / 100; page++) {
47
+ const next = z.array(z.unknown()).parse(JSON.parse(await runPage(page)));
48
+ rows.push(...next);
49
+ if (next.length < 100)
50
+ break;
51
+ }
52
+ return rows;
53
+ }
54
+ export async function fetchGithubPrDiff(repoRoot, number, refresh = false) {
55
+ if (productEnv('DRY_RUN') === '1')
56
+ return mockGithubPrDiff(number);
57
+ try {
58
+ const head = ghPrHeadSchema.parse(JSON.parse(await gh(repoRoot, ['pr', 'view', String(number), '--json', 'headRefOid']))).headRefOid;
59
+ const key = `${repoRoot}\0${number}\0${head}`;
60
+ const hit = prDiffCache.get(key);
61
+ if (!refresh && hit && Date.now() - hit.at < CACHE_MS)
62
+ return hit.data;
63
+ const rows = z.array(ghPrFileSchema).parse(await fetchPrFilePages((page) => gh(repoRoot, ['api', `repos/{owner}/{repo}/pulls/${number}/files?per_page=100&page=${page}`], 30_000)));
64
+ const limited = rows.slice(0, GH_PR_DIFF_FILE_CAP);
65
+ // A full third page may have a successor. Without fetching a 301st file, conservatively call
66
+ // the response partial rather than claiming completeness we cannot prove.
67
+ let responseTruncated = rows.length >= GH_PR_DIFF_FILE_CAP;
68
+ const reasons = responseTruncated ? [`Only the first ${GH_PR_DIFF_FILE_CAP} files are shown.`] : [];
69
+ const files = limited.map((row) => {
70
+ let patch = row.patch;
71
+ let truncated = false;
72
+ let patchUnavailableReason;
73
+ if (patch !== undefined && Buffer.byteLength(patch, 'utf8') > GH_PR_PATCH_CAP) {
74
+ patch = undefined;
75
+ truncated = true;
76
+ patchUnavailableReason = 'too-large';
77
+ responseTruncated = true;
78
+ }
79
+ else if (patch === undefined) {
80
+ patchUnavailableReason = row.additions === 0 && row.deletions === 0 ? 'binary' : 'not-provided';
81
+ }
82
+ return {
83
+ path: row.filename,
84
+ ...(row.previous_filename ? { previousPath: row.previous_filename } : {}),
85
+ status: row.status,
86
+ additions: row.additions,
87
+ deletions: row.deletions,
88
+ ...(patch !== undefined ? { patch } : {}),
89
+ ...(patchUnavailableReason ? { patchUnavailableReason } : {}),
90
+ ...(truncated ? { truncated: true } : {}),
91
+ };
92
+ });
93
+ let kept = files;
94
+ while (kept.length > 0 &&
95
+ Buffer.byteLength(JSON.stringify({ available: true, number, headSha: head, files: kept }), 'utf8') >
96
+ GH_PR_DIFF_JSON_CAP) {
97
+ kept = kept.slice(0, -1);
98
+ responseTruncated = true;
99
+ }
100
+ if (kept.length < files.length)
101
+ reasons.push('The response size limit omitted some files.');
102
+ if (files.some((file) => file.truncated))
103
+ reasons.push('One or more patches exceeded the per-file limit.');
104
+ const data = {
105
+ available: true,
106
+ number,
107
+ headSha: head,
108
+ files: kept,
109
+ additions: rows.reduce((sum, row) => sum + row.additions, 0),
110
+ deletions: rows.reduce((sum, row) => sum + row.deletions, 0),
111
+ truncated: responseTruncated,
112
+ ...(reasons.length ? { reason: reasons.join(' ') } : {}),
113
+ };
114
+ prDiffCache.set(key, { at: Date.now(), data });
115
+ while (prDiffCache.size > 50)
116
+ prDiffCache.delete(prDiffCache.keys().next().value);
117
+ return data;
118
+ }
119
+ catch (err) {
120
+ const message = err instanceof Error ? err.message : String(err);
121
+ if (/HTTP 404|Could not resolve to a PullRequest|no pull requests found/i.test(message)) {
122
+ throw new GithubPrNotFoundError(`Pull request #${number} was not found`);
123
+ }
124
+ return {
125
+ available: false,
126
+ reason: /ENOENT/.test(message)
127
+ ? 'gh CLI not found — install it and run `gh auth login`'
128
+ : firstLine(message),
129
+ };
130
+ }
131
+ }
132
+ function mockGithubPrDiff(number) {
133
+ return {
134
+ available: true,
135
+ number,
136
+ headSha: '0123456789abcdef0123456789abcdef01234567',
137
+ additions: 15,
138
+ deletions: 4,
139
+ truncated: true,
140
+ reason: 'One or more patches were not provided by GitHub.',
141
+ files: [
142
+ { path: 'src/session.ts', status: 'modified', additions: 8, deletions: 3, patch: '@@ -1,3 +1,4 @@\n-old\n+new\n context' },
143
+ { path: 'src/new-name.ts', previousPath: 'src/old-name.ts', status: 'renamed', additions: 7, deletions: 1, patch: '@@ -1 +1 @@\n-old name\n+new name' },
144
+ { path: 'assets/logo.png', status: 'modified', additions: 0, deletions: 0, patchUnavailableReason: 'binary' },
145
+ { path: 'generated/output.txt', status: 'modified', additions: 0, deletions: 0, patchUnavailableReason: 'too-large', truncated: true },
146
+ ],
147
+ };
148
+ }
149
+ // `gh … --json` output — validated at the boundary, extras stripped.
150
+ const ghAuthor = z.object({ login: z.string() }).nullish();
151
+ // `color` is the 6-hex GitHub label color (no `#`), '' when gh omits it.
152
+ const ghLabel = z.object({ name: z.string(), color: z.string().default('') });
153
+ const ghIssueSchema = z.object({
154
+ number: z.number(),
155
+ title: z.string(),
156
+ author: ghAuthor,
157
+ createdAt: z.string(),
158
+ labels: z.array(ghLabel).default([]),
159
+ assignees: z.array(z.object({ login: z.string() })).default([]),
160
+ body: z.string().nullish(),
161
+ url: z.string(),
162
+ });
163
+ // One check run's `gh --json statusCheckRollup` entry — every field optional/nullish because
164
+ // gh's shape varies by check provider (exported so #400's unit tests can build fixtures).
165
+ export const ghCheckRunSchema = z.object({
166
+ state: z.string().nullish(),
167
+ status: z.string().nullish(),
168
+ conclusion: z.string().nullish(),
169
+ });
170
+ const ghStatusCheckRollup = z.array(ghCheckRunSchema).nullish();
171
+ const ghPrViewSchema = z.object({
172
+ number: z.number(),
173
+ url: z.string(),
174
+ state: z.string().default('OPEN'),
175
+ mergedAt: z.unknown().optional(),
176
+ isDraft: z.boolean().default(false),
177
+ statusCheckRollup: ghStatusCheckRollup,
178
+ });
179
+ const ghMergeResultSchema = z.object({
180
+ merged: z.boolean(),
181
+ message: z.string().nullish(),
182
+ sha: z.string().nullish(),
183
+ });
184
+ /** Exported for unit tests (#400) — collapses a zod-validated `statusCheckRollup` array down to
185
+ * the single enum the GitHub tab (list rows + detail badge) renders. */
186
+ export function rollupToChecks(rollup) {
187
+ if (!rollup || rollup.length === 0)
188
+ return null;
189
+ const states = rollup.map((r) => (r.conclusion || r.state || r.status || '').toUpperCase());
190
+ if (states.some((s) => ['FAILURE', 'ERROR', 'TIMED_OUT', 'ACTION_REQUIRED'].includes(s)))
191
+ return 'failing';
192
+ if (states.some((s) => ['PENDING', 'IN_PROGRESS', 'QUEUED', 'EXPECTED', ''].includes(s)))
193
+ return 'pending';
194
+ return 'passing';
195
+ }
196
+ // Keep account-directory and host selection attached to shared work. Never key by owner/name:
197
+ // separate local roots (including different remotes/accounts) must not share results.
198
+ function githubContext(repoRoot) {
199
+ const env = { GH_HOST: process.env.GH_HOST, GH_CONFIG_DIR: process.env.GH_CONFIG_DIR };
200
+ return { key: JSON.stringify([repoRoot, env.GH_HOST, env.GH_CONFIG_DIR]), env };
201
+ }
202
+ async function gh(repoRoot, args, timeout = 15_000, signal, context) {
203
+ const { stdout } = await exec('gh', args, {
204
+ cwd: repoRoot,
205
+ ...(context ? { env: { ...process.env, ...context.env } } : {}),
206
+ timeout,
207
+ maxBuffer: 50 * 1024 * 1024,
208
+ ...(signal ? { signal } : {}),
209
+ });
210
+ return stdout;
211
+ }
212
+ /** `gh api graphql` argv. `-f` sends every value as a String, and GitHub rejects a String where
213
+ * the query declares `Int!` ("Could not coerce value to Int", open-mercato/cezar#1366), so a
214
+ * number goes through `-F`. A string never does: `-F` would read `@file` and coerce
215
+ * `true`/numerals, which is not what an opaque cursor or a login means. */
216
+ export function ghGraphqlArgs(query, variables = {}) {
217
+ const args = ['api', 'graphql', '-f', `query=${query}`];
218
+ for (const [key, value] of Object.entries(variables)) {
219
+ args.push(typeof value === 'number' ? '-F' : '-f', `${key}=${value}`);
220
+ }
221
+ return args;
222
+ }
223
+ /** `owner/name` → `{ owner, name }`, or null when the handle isn't a clean two-part slug. */
224
+ export function parseOwnerName(nameWithOwner) {
225
+ const [owner, name, ...rest] = nameWithOwner.trim().split('/');
226
+ return owner && name && rest.length === 0 ? { owner, name } : null;
227
+ }
228
+ // Paged open lists defer comment counts; hydrate them through the counts route after paint.
229
+ /** GitHub GraphQL rejects `first` above 100 (EXCESSIVE_PAGINATION); larger limits walk pages. */
230
+ export const GH_GRAPHQL_PAGE_MAX = 100;
231
+ // Labels up to 100, which is what `gh issue list --json labels` read before the GraphQL page.
232
+ const LIST_NODE_FIELDS = 'number title author { login } createdAt url body labels(first: 100) { nodes { name color } }';
233
+ const listQuery = (side) => `
234
+ query ($owner: String!, $name: String!, $first: Int!, $after: String) {
235
+ repository(owner: $owner, name: $name) {
236
+ ${side}(first: $first, after: $after, states: OPEN, orderBy: {field: CREATED_AT, direction: DESC}) {
237
+ totalCount
238
+ pageInfo { hasNextPage endCursor }
239
+ nodes { ${LIST_NODE_FIELDS} ${side === 'issues' ? 'assignees(first: 20) { nodes { login } }' : 'isDraft additions deletions'} }
240
+ }
241
+ }
242
+ }`;
243
+ /** A GraphQL connection (`{ nodes }`) read as the flat array `gh … --json` used to return. */
244
+ const connectionOf = (item) => z.object({ nodes: z.array(item.nullable()).default([]) }).nullish()
245
+ .transform((connection) => (connection?.nodes ?? []).filter((node) => node != null));
246
+ const ghListNodeSchema = z.object({
247
+ number: z.number(),
248
+ title: z.string(),
249
+ author: ghAuthor,
250
+ createdAt: z.string(),
251
+ url: z.string(),
252
+ body: z.string().nullish(),
253
+ labels: connectionOf(ghLabel),
254
+ assignees: connectionOf(z.object({ login: z.string() })),
255
+ isDraft: z.boolean().nullish(),
256
+ additions: z.number().nullish(),
257
+ deletions: z.number().nullish(),
258
+ });
259
+ const ghListConnectionSchema = z.object({
260
+ totalCount: z.number().int().nonnegative(),
261
+ pageInfo: z.object({ hasNextPage: z.boolean(), endCursor: z.string().nullish() }),
262
+ nodes: z.array(ghListNodeSchema.nullable()),
263
+ });
264
+ function parseListConnection(out, side) {
265
+ const parsed = JSON.parse(out);
266
+ const message = parsed.errors?.map((e) => e.message).filter(Boolean).join(' ');
267
+ if (message)
268
+ throw new Error(message);
269
+ if (!parsed.data?.repository)
270
+ throw new Error('GitHub did not resolve this repository');
271
+ return ghListConnectionSchema.parse(parsed.data.repository[side]);
272
+ }
273
+ /**
274
+ * One side of the open list: up to `limit` rows from `cursor`, in requests of at most
275
+ * `GH_GRAPHQL_PAGE_MAX`. `nextCursor` is where the rows stop, or `null` once the side is
276
+ * exhausted. `done` asks for `first: 0`, which still answers `totalCount`, so an exhausted side
277
+ * keeps a fresh total without ever restarting from its first page (open-mercato/cezar#1366
278
+ * restarted it while the other side paged).
279
+ */
280
+ export async function fetchListConnection(run, owner, name, side, limit, cursor, done = false) {
281
+ const query = listQuery(side);
282
+ if (done) {
283
+ const page = parseListConnection(await run(query, { owner, name, first: 0 }), side);
284
+ return { totalCount: page.totalCount, nodes: [], nextCursor: null };
285
+ }
286
+ const nodes = [];
287
+ let after = cursor;
288
+ for (;;) {
289
+ const first = Math.min(GH_GRAPHQL_PAGE_MAX, limit - nodes.length);
290
+ const page = parseListConnection(await run(query, { owner, name, first, ...(after ? { after } : {}) }), side);
291
+ const rows = page.nodes.filter((node) => node !== null);
292
+ nodes.push(...rows);
293
+ const next = page.pageInfo.hasNextPage ? page.pageInfo.endCursor ?? null : null;
294
+ // An empty page that claims more would loop forever; read it as the end of the side.
295
+ if (next === null || rows.length === 0)
296
+ return { totalCount: page.totalCount, nodes, nextCursor: null };
297
+ if (next === after)
298
+ throw new Error('GitHub list cursor did not advance');
299
+ // A short page with more behind it still ends this request: the cursor resumes it exactly.
300
+ if (nodes.length >= limit || rows.length < first)
301
+ return { totalCount: page.totalCount, nodes, nextCursor: next };
302
+ after = next;
303
+ }
304
+ }
305
+ /* Reads degrade to `available: false` with a hint — never an error (plan rule
306
+ 7): no `gh`, no remote, offline all land on the same quiet path. A short
307
+ cache keeps tab switches from hammering the GitHub API; a cached fetch with
308
+ a bigger limit than asked serves fine (it's a superset). Keyed by repository/host/account context
309
+ (multi-project workspace, step 2.6) plus the page asked for: one project's — possibly private —
310
+ issues/PRs must never be served under another project's scope, and page 2 is never page 1.
311
+ Bounded like `commentsCache` so an unbounded workspace can't grow it without limit. */
312
+ let listRequestOrder = 0;
313
+ /** Per context, the order of the newest page-1 refresh. A refresh drops the context's later
314
+ * pages (their cursors were cut from the old first page), and a later page that started before
315
+ * it may answer its caller but never re-enters the cache. Bounded with `listCache`. */
316
+ const listRefreshOrder = new Map();
317
+ const listCache = new Map();
318
+ const LIST_CACHE_MAX = 50;
319
+ const CACHE_MS = 60_000;
320
+ /** Which page key holds a projects generation, so `fetchGithubProjects` never scans the cache.
321
+ * Installed and evicted in step with `listCache`, so it shares its bound. */
322
+ const projectGenerations = new Map();
323
+ /** Board discovery (the `projectsV2` walk) per context: page 2 pays for membership only. A
324
+ * failed discovery is dropped, so the next page or refresh asks again. */
325
+ const repositoryProjectsCache = new Map();
326
+ function dropListEntry(key) {
327
+ const generation = listCache.get(key)?.data.projectsGeneration;
328
+ if (generation && projectGenerations.get(generation) === key)
329
+ projectGenerations.delete(generation);
330
+ listCache.delete(key);
331
+ }
332
+ function discoverRepositoryProjects(contextKey, refresh, start) {
333
+ const hit = repositoryProjectsCache.get(contextKey);
334
+ if (!refresh && hit && Date.now() - hit.at < CACHE_MS)
335
+ return hit.projects;
336
+ const projects = start();
337
+ const entry = { at: Date.now(), projects };
338
+ repositoryProjectsCache.delete(contextKey);
339
+ repositoryProjectsCache.set(contextKey, entry);
340
+ projects.catch(() => {
341
+ if (repositoryProjectsCache.get(contextKey) === entry)
342
+ repositoryProjectsCache.delete(contextKey);
343
+ });
344
+ while (repositoryProjectsCache.size > LIST_CACHE_MAX) {
345
+ const oldest = repositoryProjectsCache.keys().next().value;
346
+ if (oldest === undefined)
347
+ break;
348
+ repositoryProjectsCache.delete(oldest);
349
+ }
350
+ return projects;
351
+ }
352
+ export const GH_MAX_LIMIT = 1000;
353
+ function listPageKey(contextKey, page) {
354
+ return JSON.stringify([contextKey, page.issuesCursor ?? null, page.prsCursor ?? null, !!page.issuesDone, !!page.prsDone]);
355
+ }
356
+ function isFirstGithubPage(page) {
357
+ return !page.issuesCursor && !page.prsCursor && !page.issuesDone && !page.prsDone;
358
+ }
359
+ function cachedGithubPage(hit, refresh, capped) {
360
+ if (!refresh && hit && Date.now() - hit.at < CACHE_MS && hit.limit >= capped)
361
+ return hit.data;
362
+ return undefined;
363
+ }
364
+ function rememberListRefresh(contextKey, order, key) {
365
+ listRefreshOrder.delete(contextKey);
366
+ listRefreshOrder.set(contextKey, order);
367
+ while (listRefreshOrder.size > LIST_CACHE_MAX) {
368
+ const oldest = listRefreshOrder.keys().next().value;
369
+ if (oldest === undefined)
370
+ break;
371
+ listRefreshOrder.delete(oldest);
372
+ }
373
+ for (const [cached, entry] of [...listCache]) {
374
+ if (entry.contextKey === contextKey && cached !== key)
375
+ dropListEntry(cached);
376
+ }
377
+ }
378
+ function githubProjectGraphql(repoRoot, context) {
379
+ // Optional project queries have one shared wall-clock budget. A missing scope, a cap,
380
+ // or malformed metadata must never turn an otherwise working list into unavailable.
381
+ const deadline = Date.now() + 15_000;
382
+ return (query, variables) => {
383
+ const remaining = deadline - Date.now();
384
+ if (remaining <= 0)
385
+ return Promise.reject(new Error('Project lookup timed out'));
386
+ return gh(repoRoot, ghGraphqlArgs(query, variables), Math.min(remaining, 8_000), undefined, context);
387
+ };
388
+ }
389
+ function recordLabelColor(labelColors, label) {
390
+ if (label.color && !labelColors[label.name])
391
+ labelColors[label.name] = label.color;
392
+ }
393
+ function flattenGithubIssue(issue, recordColor) {
394
+ issue.labels.forEach(recordColor);
395
+ return {
396
+ kind: 'issue',
397
+ number: issue.number,
398
+ title: issue.title,
399
+ author: issue.author?.login ?? '?',
400
+ createdAt: issue.createdAt,
401
+ labels: issue.labels.map((label) => label.name),
402
+ body: (issue.body ?? '').slice(0, 8_000),
403
+ url: issue.url,
404
+ comments: 0,
405
+ assignees: issue.assignees.map((assignee) => assignee.login),
406
+ };
407
+ }
408
+ function flattenGithubPr(pr, recordColor) {
409
+ pr.labels.forEach(recordColor);
410
+ const isDraft = pr.isDraft ?? false;
411
+ return {
412
+ kind: 'pr',
413
+ number: pr.number,
414
+ title: pr.title,
415
+ author: pr.author?.login ?? '?',
416
+ createdAt: pr.createdAt,
417
+ labels: [...pr.labels.map((label) => label.name), ...(isDraft ? ['draft'] : [])],
418
+ body: (pr.body ?? '').slice(0, 8_000),
419
+ url: pr.url,
420
+ comments: 0,
421
+ isDraft,
422
+ additions: pr.additions ?? 0,
423
+ deletions: pr.deletions ?? 0,
424
+ // Hydrated lazily by `fetchGithubChecks` for on-screen rows (#664) — the list no
425
+ // longer pays for the CI rollup of every open PR.
426
+ checks: null,
427
+ };
428
+ }
429
+ function applyPreviousProjectIds(issues, previous) {
430
+ for (const issue of issues) {
431
+ const known = previous?.issues.find((row) => row.number === issue.number)?.projectIds;
432
+ if (known !== undefined)
433
+ issue.projectIds = known;
434
+ }
435
+ }
436
+ function githubListData(input) {
437
+ return {
438
+ available: true,
439
+ repo: input.repo,
440
+ syncedAt: new Date().toISOString(),
441
+ issues: input.issues,
442
+ prs: input.prs,
443
+ labelColors: input.labelColors,
444
+ ...(input.viewerLogin ? { viewerLogin: input.viewerLogin } : {}),
445
+ projectsState: 'refreshing',
446
+ projectsGeneration: input.generation,
447
+ ...(input.previous?.projects ? { projects: input.previous.projects } : {}),
448
+ issuesTotal: input.issuesTotal,
449
+ prsTotal: input.prsTotal,
450
+ issuesNextCursor: input.issuesNextCursor,
451
+ prsNextCursor: input.prsNextCursor,
452
+ commentsDeferred: true,
453
+ };
454
+ }
455
+ function applyReadyProjectMembership(current, metadata) {
456
+ current.data = {
457
+ ...current.data,
458
+ projectsState: metadata.state,
459
+ projects: metadata.projects,
460
+ issues: current.data.issues.map((issue) => ({ ...issue, projectIds: metadata.membership?.[issue.number] ?? [] })),
461
+ };
462
+ }
463
+ function attachProjectMetadata(projectLookup, generation, key) {
464
+ return projectLookup.then((result) => {
465
+ const metadata = 'membership' in result
466
+ ? { generation, state: 'ready', projects: result.projects, membership: result.membership }
467
+ : { generation, state: 'unavailable', reason: result.projectsReason };
468
+ const current = listCache.get(key);
469
+ if (current?.data.projectsGeneration === generation) {
470
+ if (metadata.state === 'ready')
471
+ applyReadyProjectMembership(current, metadata);
472
+ else
473
+ current.data = { ...current.data, projectsState: metadata.state, projectsReason: metadata.reason };
474
+ }
475
+ return metadata;
476
+ });
477
+ }
478
+ function storeGithubPage(key, contextKey, order, capped, data, projects, generation) {
479
+ // Concurrent refreshes may finish in reverse order. An older list must not evict
480
+ // the newer generation (and invalidate the newer client's metadata follow-up).
481
+ if ((listCache.get(key)?.order ?? -1) < order && order >= (listRefreshOrder.get(contextKey) ?? 0)) {
482
+ dropListEntry(key); // re-insert so this key becomes the newest
483
+ listCache.set(key, { contextKey, order, at: Date.now(), limit: capped, data, projects });
484
+ projectGenerations.set(generation, key);
485
+ }
486
+ while (listCache.size > LIST_CACHE_MAX) {
487
+ const oldest = listCache.keys().next().value;
488
+ if (oldest === undefined)
489
+ break;
490
+ dropListEntry(oldest);
491
+ }
492
+ }
493
+ async function loadGithubPage(repoRoot, context, capped, page, key, hit, order, refresh) {
494
+ // No `statusCheckRollup` (#664): it is hydrated lazily per on-screen PR row via
495
+ // `fetchGithubChecks`. A big aggregate list still gets a little more wall clock.
496
+ const timeout = capped > 100 ? 30_000 : 15_000;
497
+ const ownerName = await resolveRepoHandleStrict(repoRoot, context);
498
+ if (!ownerName)
499
+ return { available: false, reason: 'Could not resolve this repository on GitHub.', issues: [], prs: [] };
500
+ const runGraphql = (query, variables) => gh(repoRoot, ghGraphqlArgs(query, variables), timeout, undefined, context);
501
+ const projectGraphql = githubProjectGraphql(repoRoot, context);
502
+ const firstPage = isFirstGithubPage(page);
503
+ const issueSide = fetchListConnection(runGraphql, ownerName.owner, ownerName.name, 'issues', capped, page.issuesCursor, page.issuesDone);
504
+ const projectLookup = fetchIssueProjects(projectGraphql, ownerName.owner, ownerName.name, async () => (await issueSide).nodes.map((node) => node.number), () => discoverRepositoryProjects(context.key, refresh, () => fetchRepositoryProjects(projectGraphql, ownerName.owner, ownerName.name)));
505
+ const [issuesPage, prsPage, viewerLogin] = await Promise.all([
506
+ issueSide,
507
+ fetchListConnection(runGraphql, ownerName.owner, ownerName.name, 'pullRequests', capped, page.prsCursor, page.prsDone),
508
+ // The viewer is the same on every page; only the first one asks.
509
+ firstPage
510
+ ? fetchViewerLogin((query) => gh(repoRoot, ghGraphqlArgs(query), 8_000, undefined, context))
511
+ : Promise.resolve(undefined),
512
+ ]);
513
+ // One repo-wide label→color map, filled as we flatten each item's labels.
514
+ const labelColors = {};
515
+ const recordColor = (label) => recordLabelColor(labelColors, label);
516
+ const issues = issuesPage.nodes.map((issue) => flattenGithubIssue(issue, recordColor));
517
+ const prs = prsPage.nodes.map((pr) => flattenGithubPr(pr, recordColor));
518
+ // Only verified values from the same repository may survive a refresh.
519
+ const previous = hit?.data.repo === `${ownerName.owner}/${ownerName.name}` ? hit.data : undefined;
520
+ applyPreviousProjectIds(issues, previous);
521
+ const generation = randomUUID();
522
+ const data = githubListData({
523
+ repo: `${ownerName.owner}/${ownerName.name}`,
524
+ issues,
525
+ prs,
526
+ labelColors,
527
+ viewerLogin,
528
+ generation,
529
+ previous,
530
+ issuesTotal: issuesPage.totalCount,
531
+ prsTotal: prsPage.totalCount,
532
+ issuesNextCursor: issuesPage.nextCursor,
533
+ prsNextCursor: prsPage.nextCursor,
534
+ });
535
+ const projects = attachProjectMetadata(projectLookup, generation, key);
536
+ storeGithubPage(key, context.key, order, capped, data, projects, generation);
537
+ return data;
538
+ }
539
+ export async function fetchGithub(repoRoot, refresh = false, limit = 30, page = {}) {
540
+ if (productEnv('DRY_RUN') === '1')
541
+ return mockGithubPage(limit, page);
542
+ const context = githubContext(repoRoot);
543
+ const capped = Math.min(Math.max(limit, 1), GH_MAX_LIMIT);
544
+ const key = listPageKey(context.key, page);
545
+ const hit = listCache.get(key);
546
+ const cached = cachedGithubPage(hit, refresh, capped);
547
+ if (cached)
548
+ return cached;
549
+ const order = ++listRequestOrder;
550
+ if (refresh && isFirstGithubPage(page))
551
+ rememberListRefresh(context.key, order, key);
552
+ try {
553
+ return await loadGithubPage(repoRoot, context, capped, page, key, hit, order, refresh);
554
+ }
555
+ catch (err) {
556
+ return { available: false, reason: ghFailureReason(err), issues: [], prs: [] };
557
+ }
558
+ }
559
+ /** A `gh` failure as one readable line. GitHub's own words come on stderr; the Node message
560
+ * starts with the whole argv, and a GraphQL argv is a multi-line query, so its first line is
561
+ * a fragment of that query rather than the reason (open-mercato/cezar#1366). */
562
+ export function ghFailureReason(err) {
563
+ const message = err instanceof Error ? err.message : String(err);
564
+ if (/ENOENT/.test(message))
565
+ return 'gh CLI not found — install it and run `gh auth login`';
566
+ const failure = (err ?? {});
567
+ const stderr = typeof failure.stderr === 'string' ? failure.stderr : '';
568
+ if (stderr.trim()) {
569
+ const lines = stderr.split('\n').map((line) => line.trim()).filter(Boolean);
570
+ return lines.find((line) => !/^Command failed:/i.test(line)) ?? lines[0];
571
+ }
572
+ if (failure.killed === true || typeof failure.signal === 'string')
573
+ return 'GitHub did not answer in time. Refresh to try again.';
574
+ // Without stderr, everything after "Command failed:" is the argv, which is query text.
575
+ if (/^Command failed:/i.test(message))
576
+ return 'GitHub request failed. Refresh to try again.';
577
+ return message.split('\n').map((line) => line.trim()).find(Boolean) ?? 'gh failed';
578
+ }
579
+ /** Bounded lookup already started by the list. Never launch work for an expired generation. */
580
+ export async function fetchGithubProjects(repoRoot, generation) {
581
+ const key = projectGenerations.get(generation);
582
+ const entry = key === undefined ? undefined : listCache.get(key);
583
+ // A generation from another repository, host or account never resolves here, even when
584
+ // its UUID is known: the context is part of what the generation was issued for.
585
+ if (!entry || entry.contextKey !== githubContext(repoRoot).key || entry.data.projectsGeneration !== generation) {
586
+ return { generation, state: 'unavailable', reason: 'Project metadata expired. Refresh to try again.' };
587
+ }
588
+ return entry.projects;
589
+ }
590
+ function firstLine(s) {
591
+ return s.split('\n').find((l) => l.trim().length > 0)?.trim() ?? 'gh failed';
592
+ }
593
+ // On-demand cross-state search stays separate from the hot open list.
594
+ /** Hits per search. `gh search`'s own default is 30; 50 matches the page size the incremental-
595
+ * loading spec settled on and keeps one search to a single `gh` round-trip. */
596
+ export const GH_SEARCH_MAX = GITHUB_SEARCH_MAX;
597
+ /** The `--json` field set each search path requests. Neither path asks for `additions`/`deletions`
598
+ * or `statusCheckRollup` — the same reason the list tier stopped (#664), those are the expensive
599
+ * per-PR lookups.
600
+ *
601
+ * `isDraft` is added for PRs only, and that is a hard requirement rather than a nicety: `gh search
602
+ * issues` does not define the field at all and rejects the whole call with `Unknown JSON field:
603
+ * "isDraft"`, which would turn every text query on the Issues tab into the "could not be searched"
604
+ * degrade. `gh {issue,pr} view` is the opposite — it tolerates the field for both kinds — so only
605
+ * the search path needs to discriminate. */
606
+ const SEARCH_FIELDS = 'number,title,author,createdAt,labels,body,url';
607
+ /** `gh search {prs,issues} --json` field list for `kind`. See `SEARCH_FIELDS` on why `isDraft`
608
+ * cannot be sent to the issues search. */
609
+ function searchJsonFields(kind) {
610
+ return kind === 'pr'
611
+ ? `${SEARCH_FIELDS},isDraft,commentsCount`
612
+ : `${SEARCH_FIELDS},assignees,commentsCount`;
613
+ }
614
+ /** `gh search issues|prs` returns `commentsCount` where the list tier gets its counts from a
615
+ * separate GraphQL walk. Same value, different spelling — normalized into `ForgeItem.comments`. */
616
+ const ghSearchHitSchema = ghIssueSchema.extend({
617
+ assignees: z.array(z.object({ login: z.string() })).optional(),
618
+ isDraft: z.boolean().default(false),
619
+ commentsCount: z.number().default(0),
620
+ });
621
+ /** `gh {issue,pr} view <n> --json …` — the exact-number path. Shares `ghIssueSchema`'s core;
622
+ * `isDraft`/`additions`/`deletions` are PR-only and default harmlessly for issues. */
623
+ const ghViewHitSchema = ghIssueSchema.extend({
624
+ state: z.string().optional(),
625
+ mergedAt: z.unknown().optional(),
626
+ assignees: z.array(z.object({ login: z.string() })).optional(),
627
+ isDraft: z.boolean().default(false),
628
+ additions: z.number().default(0),
629
+ deletions: z.number().default(0),
630
+ });
631
+ /** Flatten one validated hit into the `ForgeItem` the tab's rows already render. `checks: null`
632
+ * is what the list tier ships too since #664 — the glyph hydrates lazily via `/api/github/checks`
633
+ * — so a searched row and a listed row are indistinguishable to the UI. */
634
+ function toSearchItem(kind, hit, labelColors) {
635
+ for (const label of hit.labels) {
636
+ if (label.color && !labelColors[label.name])
637
+ labelColors[label.name] = label.color;
638
+ }
639
+ const item = {
640
+ kind,
641
+ number: hit.number,
642
+ title: hit.title,
643
+ author: hit.author?.login ?? '?',
644
+ createdAt: hit.createdAt,
645
+ labels: hit.labels.map((l) => l.name),
646
+ body: (hit.body ?? '').slice(0, 8_000),
647
+ url: hit.url,
648
+ comments: hit.commentsCount,
649
+ ...(kind === 'issue' && hit.assignees ? { assignees: hit.assignees.map(a => a.login) } : {}),
650
+ };
651
+ if (kind === 'pr') {
652
+ item.isDraft = hit.isDraft;
653
+ // Draft is a label on list rows as well as a flag — keep the parity so filtering by the
654
+ // `draft` chip behaves the same on a searched row.
655
+ if (hit.isDraft)
656
+ item.labels = [...item.labels, 'draft'];
657
+ if ('additions' in hit)
658
+ item.additions = hit.additions;
659
+ if ('deletions' in hit)
660
+ item.deletions = hit.deletions;
661
+ item.checks = null;
662
+ }
663
+ return item;
664
+ }
665
+ /** `gh {pr,issue} view <n>` diagnostics that name the REQUESTED item as absent. A bare
666
+ * "not found" / 404 or "Could not resolve to a Repository" also fails a view, but says the
667
+ * repository (or auth) is the problem, not that the number does not exist. */
668
+ const MISSING_ITEM_RE = /could not resolve to an? (?:pullrequest|issue|issueorpullrequest|pull request|issue or pull request)\b|no (?:pull requests|issues) found/i;
669
+ /** The exact `gh view` call plus its parse. `null` only for a confirmed-missing item; anything
670
+ * else (transport, auth, repository, unparseable output) throws. */
671
+ async function viewGithubHit(repoRoot, kind, number) {
672
+ try {
673
+ const out = await gh(repoRoot, [
674
+ kind === 'pr' ? 'pr' : 'issue',
675
+ 'view',
676
+ String(number),
677
+ '--json',
678
+ kind === 'pr' ? `${SEARCH_FIELDS},isDraft,additions,deletions,state,mergedAt` : `${SEARCH_FIELDS},assignees`,
679
+ ]);
680
+ const hit = ghViewHitSchema.parse(JSON.parse(out));
681
+ if (kind === 'pr' && hit.state === 'MERGED') {
682
+ const handle = await resolveRepoHandle(repoRoot);
683
+ if (handle)
684
+ mergedPullRequestObservations.observe({ owner: handle.owner, repo: handle.name, number }, hit);
685
+ }
686
+ return hit;
687
+ }
688
+ catch (err) {
689
+ if (MISSING_ITEM_RE.test(err instanceof Error ? err.message : String(err)))
690
+ return null;
691
+ throw err;
692
+ }
693
+ }
694
+ /** `view` exposes comment bodies rather than a count. Read the single issue resource
695
+ * (PRs share this endpoint) instead of paginating a potentially enormous thread.
696
+ * gh resolves placeholders in repoRoot, just as it resolves the preceding view call. */
697
+ async function finishViewHit(repoRoot, kind, number, hit, labelColors) {
698
+ const commentsCount = z.number().int().nonnegative().parse(JSON.parse(await gh(repoRoot, ['api', `repos/{owner}/{repo}/issues/${number}`, '--jq', '.comments'], 8_000)));
699
+ return toSearchItem(kind, { ...hit, commentsCount }, labelColors);
700
+ }
701
+ /**
702
+ * One issue or PR by number, in any state (#692). `null` when `gh` confirms the item is absent;
703
+ * throws on every other failure. `labelColors` is an optional out-param.
704
+ */
705
+ export async function viewGithubItem(repoRoot, kind, number, labelColors = {}) {
706
+ const hit = await viewGithubHit(repoRoot, kind, number);
707
+ return hit ? finishViewHit(repoRoot, kind, number, hit, labelColors) : null;
708
+ }
709
+ /**
710
+ * Search any state: exact numbers try a direct lookup, then text fallback; text search omits
711
+ * state restriction. Return availability and a reason on failure instead of throwing.
712
+ */
713
+ export async function searchGithubItems(repoRoot, kind, query, limit = GH_SEARCH_MAX) {
714
+ const trimmed = query.trim();
715
+ if (trimmed === '')
716
+ return { available: true, items: [] };
717
+ const capped = Math.min(Math.max(limit, 1), GH_SEARCH_MAX);
718
+ if (productEnv('DRY_RUN') === '1') {
719
+ const mock = mockGithub();
720
+ const pool = kind === 'issue' ? mock.issues : mock.prs;
721
+ const queryText = trimmed.toLowerCase();
722
+ const numeric = queryText.replace(/^#/, '');
723
+ const idOnly = /^\d+$/.test(numeric);
724
+ // The fixture obeys `limit` and flags `truncated` on the live path's own rule (#838). The
725
+ // catalog is a handful of rows, so the cap never bites in the demo itself — but offline mode
726
+ // is the only place anyone developing without `gh` exercises cap-and-truncate at all, and a
727
+ // dry-run that returned the whole pool unflagged would hide a regression in it.
728
+ const items = pool
729
+ .filter((item) => idOnly
730
+ ? String(item.number).includes(numeric)
731
+ : `#${item.number} ${item.title} ${item.author} ${item.body}`.toLowerCase().includes(queryText))
732
+ .slice(0, capped)
733
+ // Search rows defer checks to the same hydration endpoint as live results. Copy rather
734
+ // than mutate: the mock checks endpoint still reads the fixture's passing/failing values.
735
+ .map(item => item.kind === 'pr' ? { ...item, checks: null } : item);
736
+ return { available: true, items, truncated: items.length >= capped };
737
+ }
738
+ const labelColors = {};
739
+ const numeric = trimmed.replace(/^#/, '');
740
+ try {
741
+ if (/^\d+$/.test(numeric)) {
742
+ // `Number()` before interpolation: the regex already guarantees digits, but the number is
743
+ // user input reaching an argv, so it is normalized rather than passed through verbatim.
744
+ const number = Number(numeric);
745
+ // Any failure of the exact view call or its parse (and a missing number) falls through to
746
+ // the text search below. The comment count is outside that guard: its failure reports
747
+ // unavailable rather than an invented zero.
748
+ let hit = null;
749
+ try {
750
+ hit = await viewGithubHit(repoRoot, kind, number);
751
+ }
752
+ catch {
753
+ // fall through
754
+ }
755
+ if (hit) {
756
+ const item = await finishViewHit(repoRoot, kind, number, hit, labelColors);
757
+ return await hydrateSearchProjects(repoRoot, kind, { available: true, items: [item], labelColors });
758
+ }
759
+ }
760
+ // The memoized handle first (usually a hit). Its `null` is deliberately ambiguous — it swallows
761
+ // "gh is missing" and "no remote" alike — so on a miss, ask once more directly and let the real
762
+ // failure reach the catch below, where it becomes the same honest hint the list tier gives.
763
+ // Only a genuinely unparseable slug reaches the "no remote" reason.
764
+ const handle = (await resolveRepoHandle(repoRoot)) ??
765
+ parseOwnerName(await gh(repoRoot, ['repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner']));
766
+ if (!handle) {
767
+ return { available: false, reason: 'no GitHub remote to search', items: [] };
768
+ }
769
+ const out = await gh(repoRoot, [
770
+ 'search',
771
+ kind === 'pr' ? 'prs' : 'issues',
772
+ '--repo',
773
+ `${handle.owner}/${handle.name}`,
774
+ '--limit',
775
+ String(capped),
776
+ '--json',
777
+ searchJsonFields(kind),
778
+ // End-of-flags, then the query: `gh` parses a leading `-`/`--` as a flag otherwise, so a
779
+ // flag-shaped query would degrade the tab to "GitHub could not be searched: unknown flag",
780
+ // and the exact text `--web` would match `gh search`'s own `-w, --web` and open a browser
781
+ // on the cockpit's host. Behind `--` it is search text, which is all the box ever means.
782
+ '--',
783
+ trimmed,
784
+ ]);
785
+ const hits = z.array(ghSearchHitSchema).parse(JSON.parse(out));
786
+ // Older gh/GHES search treats repeated repo qualifiers as OR. Never hydrate a foreign
787
+ // hit's number with this repository's memberships or expose it as a local detail row.
788
+ const prefix = `/${handle.owner}/${handle.name}/${kind === 'pr' ? 'pull' : 'issues'}/`.toLowerCase();
789
+ if (hits.some(hit => {
790
+ try {
791
+ return new URL(hit.url).pathname.toLowerCase() !== `${prefix}${hit.number}`;
792
+ }
793
+ catch {
794
+ return true;
795
+ }
796
+ }))
797
+ return { available: false, reason: 'GitHub returned results outside this repository. Remove repository qualifiers and try again.', items: [] };
798
+ return await hydrateSearchProjects(repoRoot, kind, {
799
+ available: true,
800
+ items: hits.map((hit) => toSearchItem(kind, hit, labelColors)),
801
+ truncated: hits.length >= capped,
802
+ labelColors,
803
+ });
804
+ }
805
+ catch (err) {
806
+ const message = err instanceof Error ? err.message : String(err);
807
+ const reason = /ENOENT/.test(message)
808
+ ? 'gh CLI not found — install it and run `gh auth login`'
809
+ : firstLine(message);
810
+ return { available: false, reason, items: [] };
811
+ }
812
+ }
813
+ /** The list's metadata window contains only open issues. Search must look up its own window,
814
+ * including closed issues, with the same bounds and unknown-on-failure semantics (#90). */
815
+ async function hydrateSearchProjects(repoRoot, kind, data) {
816
+ if (kind !== 'issue' || data.items.length === 0)
817
+ return data;
818
+ const handle = await resolveRepoHandle(repoRoot);
819
+ if (!handle)
820
+ return { ...data, projectsReason: 'Project boards unavailable for this repository.' };
821
+ const deadline = Date.now() + 15_000;
822
+ const run = (query, variables) => {
823
+ const remaining = deadline - Date.now();
824
+ if (remaining <= 0)
825
+ return Promise.reject(new Error('Project lookup timed out'));
826
+ const args = ghGraphqlArgs(query, variables);
827
+ return gh(repoRoot, args, Math.min(remaining, 8_000));
828
+ };
829
+ const projects = await fetchIssueProjects(run, handle.owner, handle.name, data.items.map(item => item.number));
830
+ if ('projectsReason' in projects)
831
+ return { ...data, projectsReason: projects.projectsReason };
832
+ return { ...data, items: data.items.map(item => ({ ...item, projectIds: projects.membership[item.number] ?? [] })) };
833
+ }
834
+ /** The dry-run catalog, paged like the real list: numeric offset cursors per side, done flags
835
+ * honored, exact totals, and counts deferred to the counts route (hearsay-tools/cezarion#1183). */
836
+ function mockGithubPage(limit, page) {
837
+ const full = mockGithub();
838
+ const side = (rows, cursor, done) => {
839
+ const start = done ? rows.length : Number(cursor ?? 0);
840
+ const slice = rows.slice(start, start + Math.max(limit, 1));
841
+ const end = start + slice.length;
842
+ return { rows: slice.map((row) => ({ ...row, comments: 0 })), next: end < rows.length ? String(end) : null };
843
+ };
844
+ const issues = side(full.issues, page.issuesCursor, page.issuesDone);
845
+ const prs = side(full.prs, page.prsCursor, page.prsDone);
846
+ const firstPage = !page.issuesCursor && !page.prsCursor && !page.issuesDone && !page.prsDone;
847
+ const { viewerLogin, ...rest } = full;
848
+ return {
849
+ ...rest,
850
+ ...(firstPage && viewerLogin ? { viewerLogin } : {}),
851
+ issues: issues.rows,
852
+ prs: prs.rows,
853
+ issuesTotal: full.issues.length,
854
+ prsTotal: full.prs.length,
855
+ issuesNextCursor: issues.next,
856
+ prsNextCursor: prs.next,
857
+ commentsDeferred: true,
858
+ };
859
+ }
860
+ /** CEZ_DRY_RUN=1 — a small fixed catalog so the GitHub tab is demoable offline. */
861
+ function mockGithub() {
862
+ const mk = (over) => ({
863
+ author: 'mock',
864
+ createdAt: new Date(Date.now() - over.number * 3_600_000).toISOString(),
865
+ labels: [],
866
+ url: `https://github.com/mock/repo/${over.kind === 'pr' ? 'pull' : 'issues'}/${over.number}`,
867
+ comments: 0,
868
+ ...over,
869
+ });
870
+ return {
871
+ available: true,
872
+ repo: 'mock/repo',
873
+ viewerLogin: 'octocat',
874
+ projects: [{ id: 'mock-delivery', title: 'Delivery', url: 'https://github.com/users/mock/projects/1' }],
875
+ syncedAt: new Date().toISOString(),
876
+ issues: [
877
+ mk({ kind: 'issue', assignees: ['octocat'], projectIds: ['mock-delivery'], number: 142, title: 'Login form drops session on refresh', labels: ['bug', 'auth'], comments: 3, body: 'Repro: log in, hit reload — you land back on /login. The session cookie is set correctly, but the client store rehydrates before the cookie check resolves, so the auth guard redirects.' }),
878
+ mk({ kind: 'issue', assignees: ['ada'], projectIds: [], number: 139, title: 'Add --json flag to cez CLI output', labels: ['enhancement', 'cli'], comments: 1, body: 'For scripting it would help if `cez list` and `cez status` could emit machine-readable JSON instead of the table view.' }),
879
+ mk({ kind: 'issue', assignees: [], projectIds: ['mock-delivery'], number: 135, title: 'Flaky e2e: worktree cleanup race on cancel', labels: ['bug', 'flaky-test'], comments: 6, body: 'Cancelling a run while the agent holds a file lock leaves a dangling worktree. The next run on the same branch then fails with "worktree already exists".' }),
880
+ ],
881
+ prs: [
882
+ mk({ kind: 'pr', number: 128, title: 'Fix flaky auth test in CI', labels: ['tests'], checks: 'passing', additions: 6, deletions: 3, body: 'Loosens the timing assertion in refresh.test.ts to a realistic budget.' }),
883
+ mk({ kind: 'pr', number: 124, title: 'Rate limit /api/runs', labels: ['server', 'draft'], isDraft: true, checks: 'failing', additions: 118, deletions: 7, comments: 4, body: 'Draft: token-bucket middleware on the runs router. Still needs the config surface and README docs before review.' }),
884
+ ],
885
+ labelColors: {
886
+ bug: 'd73a4a',
887
+ auth: '5319e7',
888
+ enhancement: 'a2eeef',
889
+ cli: '0e8a16',
890
+ 'flaky-test': 'fbca04',
891
+ tests: 'c5def5',
892
+ server: '1d76db',
893
+ draft: '6a737d',
894
+ },
895
+ };
896
+ }
897
+ // ---- comment threads (#499 Phase 2) ----------------------------------------
898
+ // A lazy per-thread fetch behind `GET /api/github/comments/:kind/:number`: the
899
+ // conversation comments (issues endpoint — GitHub serves PR conversation
900
+ // comments there too), plus submitted reviews for PRs, normalized into one
901
+ // chronological `ForgeComment[]`. Its own bounded 60 s cache keeps an open
902
+ // detail view from re-fetching on every focus. Degrades to `available: false`
903
+ // exactly like the list fetch — never a 5xx.
904
+ const ghCommentUser = z.object({ login: z.string(), avatar_url: z.string().nullish() }).nullish();
905
+ const ghIssueCommentSchema = z.object({
906
+ id: z.number(),
907
+ user: ghCommentUser,
908
+ created_at: z.string(),
909
+ body: z.string().nullish(),
910
+ html_url: z.string(),
911
+ });
912
+ const ghReviewSchema = z.object({
913
+ id: z.number(),
914
+ user: ghCommentUser,
915
+ body: z.string().nullish(),
916
+ state: z.string(),
917
+ submitted_at: z.string().nullish(),
918
+ html_url: z.string(),
919
+ });
920
+ const REVIEW_STATE = {
921
+ APPROVED: 'approved',
922
+ CHANGES_REQUESTED: 'changes_requested',
923
+ COMMENTED: 'commented',
924
+ DISMISSED: 'dismissed',
925
+ };
926
+ /** First 200 thread entries, then `truncated`; each body sliced to 8 000 chars (same cap as
927
+ * item bodies). */
928
+ export const THREAD_ENTRY_CAP = 200;
929
+ const COMMENT_BODY_CAP = 8_000;
930
+ /** `gh api …/issues/{n}/comments` JSON → `ForgeComment[]`. Exported for unit tests. */
931
+ export function normalizeComments(raw) {
932
+ return z.array(ghIssueCommentSchema).parse(raw).map((c) => defined({
933
+ id: c.id,
934
+ author: c.user?.login ?? '?',
935
+ avatarUrl: c.user?.avatar_url ?? undefined,
936
+ createdAt: c.created_at,
937
+ body: (c.body ?? '').slice(0, COMMENT_BODY_CAP),
938
+ kind: 'comment',
939
+ url: c.html_url,
940
+ }));
941
+ }
942
+ /** `gh api …/pulls/{n}/reviews` JSON → `ForgeComment[]`. Reviews with an empty body AND state
943
+ * COMMENTED/PENDING carry no signal in a flat thread and are dropped; the rest map to
944
+ * `kind: 'review'` (Q4). Exported for unit tests. */
945
+ export function normalizeReviews(raw) {
946
+ return z
947
+ .array(ghReviewSchema)
948
+ .parse(raw)
949
+ .filter((r) => {
950
+ const state = r.state.toUpperCase();
951
+ const emptyBody = (r.body ?? '').trim().length === 0;
952
+ return !(emptyBody && (state === 'COMMENTED' || state === 'PENDING'));
953
+ })
954
+ .map((r) => defined({
955
+ id: r.id,
956
+ author: r.user?.login ?? '?',
957
+ avatarUrl: r.user?.avatar_url ?? undefined,
958
+ createdAt: r.submitted_at ?? '',
959
+ body: (r.body ?? '').slice(0, COMMENT_BODY_CAP),
960
+ kind: 'review',
961
+ reviewState: REVIEW_STATE[r.state.toUpperCase()],
962
+ url: r.html_url,
963
+ }));
964
+ }
965
+ /** Merge comment/review lists chronologically (oldest first) and apply the entry cap. Exported
966
+ * for unit tests. */
967
+ export function mergeThread(parts, cap = THREAD_ENTRY_CAP) {
968
+ const all = parts.flat().sort((a, b) => a.createdAt.localeCompare(b.createdAt));
969
+ const truncated = all.length > cap;
970
+ return { comments: truncated ? all.slice(0, cap) : all, truncated };
971
+ }
972
+ // Per-thread cache: keyed `repoRoot␀kind#number` (the root scopes the key — two projects each
973
+ // having a PR #42 must not collide; step 2.6), same 60 s TTL as the list cache but BOUNDED — a
974
+ // long browsing session can't grow it without limit (Map preserves insertion order → oldest
975
+ // first).
976
+ const commentsCache = new Map();
977
+ const COMMENTS_CACHE_MAX = 50;
978
+ function cacheComments(key, data) {
979
+ commentsCache.delete(key); // re-insert so this key becomes the newest
980
+ commentsCache.set(key, { at: Date.now(), data });
981
+ while (commentsCache.size > COMMENTS_CACHE_MAX) {
982
+ const oldest = commentsCache.keys().next().value;
983
+ if (oldest === undefined)
984
+ break;
985
+ commentsCache.delete(oldest);
986
+ }
987
+ }
988
+ /** Test-only: drop the per-thread cache so cases don't leak state into each other. */
989
+ export function __clearCommentsCacheForTests() {
990
+ commentsCache.clear();
991
+ }
992
+ // Single-item cache (#692): same key shape, TTL and LRU bound as `commentsCache`. Only a real
993
+ // answer (an item or a confirmed absence) is cached, never an unavailable one.
994
+ const itemCache = new Map();
995
+ function itemCacheKey(repoRoot, kind, number) {
996
+ return `${repoRoot}\0${kind}#${number}`;
997
+ }
998
+ export function forgetGithubItem(repoRoot, kind, number) {
999
+ itemCache.delete(itemCacheKey(repoRoot, kind, number));
1000
+ }
1001
+ /** Test-only: drop the single-item cache. */
1002
+ export function __clearGithubItemCacheForTests() {
1003
+ itemCache.clear();
1004
+ }
1005
+ export async function fetchGithubItem(repoRoot, kind, number, refresh = false) {
1006
+ if (productEnv('DRY_RUN') === '1') {
1007
+ const mock = mockGithub();
1008
+ const item = (kind === 'issue' ? mock.issues : mock.prs).find((i) => i.number === number);
1009
+ return item
1010
+ ? { available: true, item, labelColors: mock.labelColors ?? {} }
1011
+ : { available: true, item: null };
1012
+ }
1013
+ const key = itemCacheKey(repoRoot, kind, number);
1014
+ const hit = itemCache.get(key);
1015
+ if (!refresh && hit && Date.now() - hit.at < CACHE_MS)
1016
+ return hit.data;
1017
+ try {
1018
+ const forge = await detectGithub(repoRoot);
1019
+ if (!forge.available)
1020
+ return { available: false, reason: forge.reason ?? 'GitHub is unavailable' };
1021
+ const labelColors = {};
1022
+ const item = await viewGithubItem(repoRoot, kind, number, labelColors);
1023
+ const data = {
1024
+ available: true, item, ...(item ? { labelColors } : {}),
1025
+ };
1026
+ itemCache.delete(key); // re-insert so this key becomes the newest
1027
+ itemCache.set(key, { at: Date.now(), data });
1028
+ while (itemCache.size > COMMENTS_CACHE_MAX) {
1029
+ const oldest = itemCache.keys().next().value;
1030
+ if (oldest === undefined)
1031
+ break;
1032
+ itemCache.delete(oldest);
1033
+ }
1034
+ return data;
1035
+ }
1036
+ catch (err) {
1037
+ const message = err instanceof Error ? err.message : String(err);
1038
+ return {
1039
+ available: false,
1040
+ reason: /ENOENT/.test(message)
1041
+ ? 'gh CLI not found — install it and run `gh auth login`'
1042
+ : firstLine(message),
1043
+ };
1044
+ }
1045
+ }
1046
+ const repoHandleCache = new Map();
1047
+ const repoDiscoveryFlights = new Map();
1048
+ /** Each subscriber owns a lease, not the subprocess. Only the last cancellation aborts it.
1049
+ * Keep the rejecting promise intact so strict callers retain gh's diagnostic. */
1050
+ function sharedRepoHandle(repoRoot, signal, context = githubContext(repoRoot)) {
1051
+ if (signal?.aborted)
1052
+ return Promise.reject(signal.reason);
1053
+ const memo = repoHandleCache.get(context.key);
1054
+ if (memo !== undefined)
1055
+ return Promise.resolve(memo);
1056
+ let flight = repoDiscoveryFlights.get(context.key);
1057
+ if (!flight) {
1058
+ const controller = new AbortController();
1059
+ const created = {
1060
+ controller, owners: 0,
1061
+ promise: gh(repoRoot, ['repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner'], 15_000, controller.signal, context)
1062
+ .then(parseOwnerName),
1063
+ };
1064
+ created.promise = created.promise.then(handle => {
1065
+ // A disposed owner may complete late; it must not overwrite a replacement discovery.
1066
+ if (!controller.signal.aborted && repoDiscoveryFlights.get(context.key) === created) {
1067
+ repoHandleCache.set(context.key, handle);
1068
+ }
1069
+ return handle;
1070
+ }).finally(() => {
1071
+ if (repoDiscoveryFlights.get(context.key) === created)
1072
+ repoDiscoveryFlights.delete(context.key);
1073
+ });
1074
+ flight = created;
1075
+ repoDiscoveryFlights.set(context.key, flight);
1076
+ }
1077
+ const owned = flight;
1078
+ owned.owners++;
1079
+ return new Promise((resolve, reject) => {
1080
+ let released = false;
1081
+ const release = () => {
1082
+ if (released)
1083
+ return false;
1084
+ released = true;
1085
+ signal?.removeEventListener('abort', cancel);
1086
+ if (--owned.owners === 0 && repoDiscoveryFlights.get(context.key) === owned) {
1087
+ repoDiscoveryFlights.delete(context.key);
1088
+ owned.controller.abort();
1089
+ }
1090
+ return true;
1091
+ };
1092
+ const cancel = () => { if (release())
1093
+ reject(signal?.reason); };
1094
+ signal?.addEventListener('abort', cancel, { once: true });
1095
+ owned.promise.then(handle => { if (release())
1096
+ resolve(handle); }, error => { if (release())
1097
+ reject(error); });
1098
+ });
1099
+ }
1100
+ /** Test-only: drop the memoized repo handles. */
1101
+ export function __clearRepoHandleCacheForTests() {
1102
+ repoHandleCache.clear();
1103
+ for (const flight of repoDiscoveryFlights.values())
1104
+ flight.controller.abort();
1105
+ repoDiscoveryFlights.clear();
1106
+ }
1107
+ /** Local absence cannot improve through network retries. Authentication, timeouts and remote
1108
+ * errors deliberately stay retryable, including 404s for temporarily inaccessible repositories. */
1109
+ function isPermanentRepoFailure(error) {
1110
+ if (!(error instanceof Error))
1111
+ return false;
1112
+ if ('code' in error && error.code === 'ENOENT')
1113
+ return true;
1114
+ const stderr = 'stderr' in error && typeof error.stderr === 'string' ? error.stderr : '';
1115
+ return /not a git repository|no git remotes found|none of the git remotes configured for this repository point to a known GitHub host/i.test(`${error.message}\n${stderr}`);
1116
+ }
1117
+ /** Share the existing memo with ordinary discovery, but retain failure classification (#102).
1118
+ * An aborted owner must never publish its late result into this shared cache. */
1119
+ export async function discoverRepoHandle(repoRoot, signal) {
1120
+ const context = githubContext(repoRoot);
1121
+ if (signal?.aborted)
1122
+ return { status: 'cancelled' };
1123
+ try {
1124
+ const handle = await sharedRepoHandle(repoRoot, signal, context);
1125
+ if (signal?.aborted)
1126
+ return { status: 'cancelled' };
1127
+ return handle ? { status: 'resolved', handle } : { status: 'unknown' };
1128
+ }
1129
+ catch (error) {
1130
+ if (signal?.aborted)
1131
+ return { status: 'cancelled' };
1132
+ if (isPermanentRepoFailure(error)) {
1133
+ // Do not replace a successful discovery another caller completed while this one failed.
1134
+ const recovered = repoHandleCache.get(context.key);
1135
+ if (recovered)
1136
+ return { status: 'resolved', handle: recovered };
1137
+ // The live arming loop stops on unknown. Do not memoize local failures here: later cold
1138
+ // index/status requests must still discover an installed gh or a newly added remote.
1139
+ return { status: 'unknown' };
1140
+ }
1141
+ return { status: 'retryable' };
1142
+ }
1143
+ }
1144
+ /** Compatible fail-open lookup for existing API and cold-index callers. */
1145
+ export async function resolveRepoHandle(repoRoot, signal) {
1146
+ const result = await discoverRepoHandle(repoRoot, signal);
1147
+ return result.status === 'resolved' ? result.handle : null;
1148
+ }
1149
+ /** SHAs per rollup query. Aliases resolve independently, so a chunk that fails costs only its own
1150
+ * glyphs — but an unbounded alias list would eventually blow the query size limit. */
1151
+ export const COMMIT_CHECKS_CHUNK = 50;
1152
+ /** One aliased `object(oid:)` per SHA. `oid` requires a FULL 40-char SHA in both literal and
1153
+ * variable form (`Could not coerce value "babda63" to GitObjectID`), which constrains fixtures
1154
+ * rather than production data — the timeline always supplies full SHAs. */
1155
+ function commitChecksQuery(shas) {
1156
+ const aliases = shas
1157
+ .map((sha, i) => ` c${i}: object(oid: "${sha}") { ... on Commit { statusCheckRollup { state } } }`)
1158
+ .join('\n');
1159
+ return `query ($owner: String!, $name: String!) {\n repository(owner: $owner, name: $name) {\n${aliases}\n }\n}`;
1160
+ }
1161
+ const ghCommitChecksSchema = z.record(z.string(), z.object({ statusCheckRollup: z.object({ state: z.string().nullish() }).nullish() }).nullish());
1162
+ /**
1163
+ * Rolled-up CI state per commit SHA, as a `sha → checks` map.
1164
+ *
1165
+ * Batched and aliased so a 40-commit PR costs one subprocess, not forty. Verified against the live
1166
+ * API: each alias resolves independently and an unknown SHA comes back `null` rather than erroring
1167
+ * the batch, so partial results degrade cleanly.
1168
+ *
1169
+ * Degrades to an empty map on any failure — exactly as `fetchCommentCounts` does for counts. The
1170
+ * caller then leaves `checks` **absent**, which the UI renders as no glyph. Exported for tests;
1171
+ * `runGraphql` is injected so this is testable without shelling out.
1172
+ */
1173
+ export async function fetchCommitChecks(runGraphql, owner, name, shas, chunkSize = COMMIT_CHECKS_CHUNK) {
1174
+ const out = {};
1175
+ if (shas.length === 0)
1176
+ return out;
1177
+ for (let i = 0; i < shas.length; i += chunkSize) {
1178
+ const chunk = shas.slice(i, i + chunkSize);
1179
+ try {
1180
+ const raw = JSON.parse(await runGraphql(commitChecksQuery(chunk), { owner, name }));
1181
+ const repository = ghCommitChecksSchema.parse(raw?.data?.repository ?? {});
1182
+ chunk.forEach((sha, index) => {
1183
+ const node = repository[`c${index}`];
1184
+ if (!node)
1185
+ return; // unknown SHA → alias resolved null; leave `checks` absent
1186
+ // Adapt the single rollup state into the array shape `rollupToChecks` expects, so the
1187
+ // existing FAILURE/PENDING/SUCCESS vocabulary is reused rather than duplicated.
1188
+ out[sha] = node.statusCheckRollup
1189
+ ? rollupToChecks([{ state: node.statusCheckRollup.state, status: null, conclusion: null }])
1190
+ : null; // no CI configured — distinct from absent
1191
+ });
1192
+ }
1193
+ catch {
1194
+ // A failed chunk costs only its own glyphs; the rest still resolve.
1195
+ }
1196
+ }
1197
+ return out;
1198
+ }
1199
+ /** PR numbers per checks query. Aliases resolve independently (a failed chunk costs only its own
1200
+ * glyphs); bounded so an unbounded number list can't blow the query size limit. Also the route's
1201
+ * hard cap on how many PRs one request may ask about. */
1202
+ export const GH_CHECKS_MAX = 100;
1203
+ /** One aliased `pullRequest(number:)` per PR; the rolled-up CI state lives on the head commit. */
1204
+ function prChecksQuery(numbers) {
1205
+ const aliases = numbers
1206
+ .map((n, i) => ` p${i}: pullRequest(number: ${n}) { commits(last: 1) { nodes { commit { statusCheckRollup { state } } } } }`)
1207
+ .join('\n');
1208
+ return `query ($owner: String!, $name: String!) {\n repository(owner: $owner, name: $name) {\n${aliases}\n }\n}`;
1209
+ }
1210
+ const ghPrChecksSchema = z.record(z.string(), z
1211
+ .object({
1212
+ commits: z.object({
1213
+ nodes: z.array(z.object({
1214
+ commit: z.object({
1215
+ statusCheckRollup: z.object({ state: z.string().nullish() }).nullish(),
1216
+ }),
1217
+ })),
1218
+ }),
1219
+ })
1220
+ .nullish());
1221
+ /**
1222
+ * Rolled-up CI state per PR number, as a `number → glyph` map.
1223
+ *
1224
+ * Batched and aliased so a 100-PR window costs one subprocess, not a hundred. Each alias resolves
1225
+ * independently and an unknown number comes back `null` (alias resolved null → left absent), so
1226
+ * partial results degrade cleanly. Degrades to an empty map on any failure — exactly as
1227
+ * `fetchCommitChecks` does. Exported for tests; `runGraphql` is injected so this is testable
1228
+ * without shelling out.
1229
+ */
1230
+ export async function fetchPrChecks(runGraphql, owner, name, numbers, chunkSize = GH_CHECKS_MAX) {
1231
+ const out = {};
1232
+ if (numbers.length === 0)
1233
+ return out;
1234
+ for (let i = 0; i < numbers.length; i += chunkSize) {
1235
+ const chunk = numbers.slice(i, i + chunkSize);
1236
+ try {
1237
+ const raw = JSON.parse(await runGraphql(prChecksQuery(chunk), { owner, name }));
1238
+ const repository = ghPrChecksSchema.parse(raw?.data?.repository ?? {});
1239
+ chunk.forEach((number, index) => {
1240
+ const node = repository[`p${index}`];
1241
+ if (!node)
1242
+ return; // unknown PR → alias resolved null; leave the glyph absent
1243
+ const rollup = node.commits.nodes[0]?.commit.statusCheckRollup;
1244
+ // Adapt the single rollup state into the array shape `rollupToChecks` expects, reusing the
1245
+ // FAILURE/PENDING/SUCCESS vocabulary rather than duplicating it.
1246
+ out[number] = rollup
1247
+ ? rollupToChecks([{ state: rollup.state, status: null, conclusion: null }]) ?? null
1248
+ : null; // no CI configured — distinct from absent
1249
+ });
1250
+ }
1251
+ catch {
1252
+ // A failed chunk costs only its own glyphs; the rest still resolve.
1253
+ }
1254
+ }
1255
+ return out;
1256
+ }
1257
+ const itemDetailSchema = z.object({
1258
+ body: z.string().nullish(),
1259
+ additions: z.number().int().nonnegative().optional(),
1260
+ deletions: z.number().int().nonnegative().optional(),
1261
+ });
1262
+ /** One item's body (and a PR's diffstat), or `undefined` on any failure. */
1263
+ async function fetchItemDetail(repoRoot, kind, number) {
1264
+ try {
1265
+ const out = await gh(repoRoot, [kind, 'view', String(number), '--json', kind === 'pr' ? 'body,additions,deletions' : 'body']);
1266
+ const parsed = itemDetailSchema.parse(JSON.parse(out));
1267
+ return {
1268
+ body: (parsed.body ?? '').slice(0, COMMENT_BODY_CAP),
1269
+ ...(kind === 'pr' && parsed.additions !== undefined ? { additions: parsed.additions } : {}),
1270
+ ...(kind === 'pr' && parsed.deletions !== undefined ? { deletions: parsed.deletions } : {}),
1271
+ };
1272
+ }
1273
+ catch {
1274
+ return undefined;
1275
+ }
1276
+ }
1277
+ // Per-item comment counts (hearsay-tools/cezarion#1183): the list defers them, and the cockpit
1278
+ // asks for its loaded rows after paint. Keyed `context␀kind␀number`, same TTL and bound as checks.
1279
+ const commentCountsCache = new Map();
1280
+ const commentCountsSchema = z.object({
1281
+ data: z.object({
1282
+ repository: z.record(z.string(), z.object({ comments: z.object({ totalCount: z.number().int().nonnegative() }) }).nullable()),
1283
+ }),
1284
+ });
1285
+ /**
1286
+ * Comment counts for the given issue or PR numbers, in one aliased GraphQL query. A number GitHub
1287
+ * does not know is absent (unknown), never 0. Degrades to `{ available: false, reason }` — never a
1288
+ * throw. Numbers are de-duplicated, validated and capped at `GITHUB_COMMENT_COUNTS_MAX`.
1289
+ */
1290
+ export async function fetchGithubCommentCounts(repoRoot, kind, numbers, refresh = false) {
1291
+ if (productEnv('DRY_RUN') === '1')
1292
+ return mockGithubCommentCounts(kind, numbers);
1293
+ const context = githubContext(repoRoot);
1294
+ const wanted = [...new Set(numbers)].filter((n) => Number.isSafeInteger(n) && n > 0).slice(0, GITHUB_COMMENT_COUNTS_MAX);
1295
+ const counts = {};
1296
+ const misses = [];
1297
+ const now = Date.now();
1298
+ const keyOf = (n) => `${context.key}\0${kind}\0${n}`;
1299
+ for (const n of wanted) {
1300
+ const hit = commentCountsCache.get(keyOf(n));
1301
+ if (!refresh && hit && now - hit.at < CACHE_MS)
1302
+ counts[n] = hit.count;
1303
+ else
1304
+ misses.push(n);
1305
+ }
1306
+ if (misses.length === 0)
1307
+ return { available: true, counts };
1308
+ try {
1309
+ const handle = await resolveRepoHandleStrict(repoRoot, context);
1310
+ if (!handle)
1311
+ return { available: false, reason: 'Could not resolve this repository on GitHub.' };
1312
+ // Integers only in the aliases; owner and name travel as variables.
1313
+ const fields = misses.map((n) => `n${n}: issueOrPullRequest(number: ${n}) {
1314
+ ... on Issue { comments { totalCount } }
1315
+ ... on PullRequest { comments { totalCount } }
1316
+ }`).join('\n');
1317
+ // A number GitHub does not know comes back NOT_FOUND and makes `gh` exit 1 with the rest of
1318
+ // the batch on stdout; `refStatusGraphql` keeps that partial answer.
1319
+ const raw = await refStatusGraphql(repoRoot, context)(`query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { ${fields} } }`, { owner: handle.owner, name: handle.name });
1320
+ const rows = commentCountsSchema.parse(JSON.parse(raw)).data.repository;
1321
+ for (const n of misses) {
1322
+ const count = rows[`n${n}`]?.comments.totalCount;
1323
+ if (count === undefined)
1324
+ continue; // unknown number: absent, and asked again next time
1325
+ counts[n] = count;
1326
+ commentCountsCache.delete(keyOf(n));
1327
+ commentCountsCache.set(keyOf(n), { at: now, count });
1328
+ }
1329
+ while (commentCountsCache.size > CHECKS_CACHE_MAX) {
1330
+ const oldest = commentCountsCache.keys().next().value;
1331
+ if (oldest === undefined)
1332
+ break;
1333
+ commentCountsCache.delete(oldest);
1334
+ }
1335
+ return { available: true, counts };
1336
+ }
1337
+ catch (err) {
1338
+ return { available: false, reason: ghFailureReason(err) };
1339
+ }
1340
+ }
1341
+ /** CEZ_DRY_RUN=1 — the catalog's own counts, so the offline list shows its badges. */
1342
+ function mockGithubCommentCounts(kind, numbers) {
1343
+ const rows = kind === 'pr' ? mockGithub().prs : mockGithub().issues;
1344
+ const counts = {};
1345
+ for (const n of numbers) {
1346
+ const row = rows.find((item) => item.number === n);
1347
+ if (row)
1348
+ counts[n] = row.comments;
1349
+ }
1350
+ return { available: true, counts };
1351
+ }
1352
+ // Per-PR checks cache: keyed `repoRoot␀number`, same 60 s TTL as the list cache but BOUNDED. Lets
1353
+ // a repeated visible-window hydration within a minute serve cached glyphs instead of re-querying.
1354
+ const checksCache = new Map();
1355
+ const CHECKS_CACHE_MAX = 500;
1356
+ /** Test hook — the per-PR checks cache would otherwise leak state across cases in one process. */
1357
+ export function __clearChecksCacheForTests() {
1358
+ checksCache.clear();
1359
+ }
1360
+ /**
1361
+ * Lazy checks glyphs for the given PR numbers (route-facing). Resolves the repo handle once,
1362
+ * serves fresh cache entries, queries only the misses, and degrades to `{ available: false,
1363
+ * reason }` when `gh` or the handle is unavailable — never a throw, never a 5xx (plan rule 7).
1364
+ * Numbers are de-duplicated, validated, and capped at `GH_CHECKS_MAX`.
1365
+ */
1366
+ export async function fetchGithubChecks(repoRoot, numbers) {
1367
+ if (productEnv('DRY_RUN') === '1')
1368
+ return mockGithubChecks(numbers);
1369
+ const wanted = [...new Set(numbers)].filter((n) => Number.isInteger(n) && n > 0).slice(0, GH_CHECKS_MAX);
1370
+ const checks = {};
1371
+ const misses = [];
1372
+ const now = Date.now();
1373
+ for (const n of wanted) {
1374
+ const hit = checksCache.get(`${repoRoot}\0${n}`);
1375
+ if (hit && now - hit.at < CACHE_MS)
1376
+ checks[n] = hit.glyph;
1377
+ else
1378
+ misses.push(n);
1379
+ }
1380
+ if (misses.length === 0)
1381
+ return { available: true, checks };
1382
+ try {
1383
+ const repoOut = await gh(repoRoot, ['repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner']);
1384
+ const ownerName = parseOwnerName(repoOut);
1385
+ if (!ownerName)
1386
+ return { available: false, reason: 'repository handle unavailable' };
1387
+ const runGraphql = (query, variables) => {
1388
+ const args = ghGraphqlArgs(query, variables);
1389
+ return gh(repoRoot, args);
1390
+ };
1391
+ const fetched = await fetchPrChecks(runGraphql, ownerName.owner, ownerName.name, misses);
1392
+ for (const n of misses) {
1393
+ // Absent (unknown/closed PR) caches as `null` so a nonexistent number isn't re-queried.
1394
+ const glyph = n in fetched ? fetched[n] : null;
1395
+ checks[n] = glyph;
1396
+ checksCache.set(`${repoRoot}\0${n}`, { at: now, glyph });
1397
+ }
1398
+ while (checksCache.size > CHECKS_CACHE_MAX) {
1399
+ const oldest = checksCache.keys().next().value;
1400
+ if (oldest === undefined)
1401
+ break;
1402
+ checksCache.delete(oldest);
1403
+ }
1404
+ return { available: true, checks };
1405
+ }
1406
+ catch (err) {
1407
+ const message = err instanceof Error ? err.message : String(err);
1408
+ return {
1409
+ available: false,
1410
+ reason: /ENOENT/.test(message)
1411
+ ? 'gh CLI not found — install it and run `gh auth login`'
1412
+ : firstLine(message),
1413
+ };
1414
+ }
1415
+ }
1416
+ /** CEZ_DRY_RUN=1 — glyphs straight from the mock catalog so the offline demo shows checks. */
1417
+ function mockGithubChecks(numbers) {
1418
+ const byNumber = new Map(mockGithub().prs.map((p) => [p.number, (p.checks ?? null)]));
1419
+ const checks = {};
1420
+ for (const n of numbers)
1421
+ checks[n] = byNumber.get(n) ?? null;
1422
+ return { available: true, checks };
1423
+ }
1424
+ /** Numbers per kind in one ref-status query — the same bound, and for the same reasons, as
1425
+ * `GH_CHECKS_MAX`: aliases resolve independently, and the query size stays finite. Taken from the
1426
+ * contract rather than restated, because the cockpit caps its batches by the same number and a
1427
+ * client that guessed high would have every chip in a batch 400 instead of losing its tail. */
1428
+ export const GH_REF_STATUS_MAX = REFERENCE_STATUS_MAX;
1429
+ /** A requested reviewer, as either connection spells them. `login` covers `User` and `Bot`, `slug`
1430
+ * covers `Team`; `reviewerKey` folds them into one comparable string. */
1431
+ const reviewerRefSchema = z
1432
+ .object({
1433
+ __typename: z.string().nullish(),
1434
+ login: z.string().nullish(),
1435
+ slug: z.string().nullish(),
1436
+ })
1437
+ .nullish();
1438
+ /** One reviewer as a comparable key, or `null` when GitHub named nobody we can match on — which
1439
+ * must never compare equal to another unnamed reviewer, hence `null` rather than a shared "". */
1440
+ function reviewerKey(ref) {
1441
+ const name = ref?.login ?? ref?.slug;
1442
+ return name ? `${ref?.__typename ?? '?'}:${name}` : null;
1443
+ }
1444
+ const ghRefStatusPrSchema = z
1445
+ .object({
1446
+ state: z.string(),
1447
+ url: z.unknown().optional(),
1448
+ mergedAt: z.unknown().optional(),
1449
+ isDraft: z.boolean().nullish(),
1450
+ reviewDecision: z.string().nullish(),
1451
+ /** `MERGEABLE` | `CONFLICTING` | `UNKNOWN` — GitHub computes it in the background, so
1452
+ * `UNKNOWN` is normal on a PR that was just pushed and must never read as "clean". */
1453
+ mergeable: z.string().nullish(),
1454
+ commits: z
1455
+ .object({
1456
+ nodes: z.array(z.object({
1457
+ commit: z.object({
1458
+ committedDate: z.string().nullish(),
1459
+ /** `totalCount` says whether the head is a merge; the first parent is the branch's
1460
+ * previous tip, which is the newest commit that is actual WORK when it is. */
1461
+ parents: z
1462
+ .object({
1463
+ totalCount: z.number(),
1464
+ nodes: z.array(z.object({ committedDate: z.string().nullish() }).nullish()).nullish(),
1465
+ })
1466
+ .nullish(),
1467
+ statusCheckRollup: z.object({ state: z.string().nullish() }).nullish(),
1468
+ }),
1469
+ })),
1470
+ })
1471
+ .nullish(),
1472
+ reviews: z.object({ nodes: z.array(z.object({ submittedAt: z.string().nullish() })) }).nullish(),
1473
+ /** Who is on the hook RIGHT NOW. The reviewers are carried, not just the count, because a
1474
+ * request's date has to be matched to a request that still stands — see `reviewRequestedAt`. */
1475
+ reviewRequests: z
1476
+ .object({
1477
+ totalCount: z.number(),
1478
+ nodes: z.array(z.object({ requestedReviewer: reviewerRefSchema }).nullish()).nullish(),
1479
+ })
1480
+ .nullish(),
1481
+ timelineItems: z
1482
+ .object({
1483
+ nodes: z
1484
+ .array(z.object({ createdAt: z.string().nullish(), requestedReviewer: reviewerRefSchema }).nullish())
1485
+ .nullish(),
1486
+ })
1487
+ .nullish(),
1488
+ })
1489
+ .nullish();
1490
+ /**
1491
+ * When was the newest STILL-STANDING review request made?
1492
+ *
1493
+ * The two connections answer different halves and neither answers both: `reviewRequests` says who
1494
+ * is on the hook right now but carries no date, while a `ReviewRequestedEvent` carries the date and
1495
+ * survives the request being withdrawn. Reading the newest event on its own therefore dates a
1496
+ * request that may no longer exist — and a request added after a review and then removed would make
1497
+ * a live rejection read as answered, which is the exact bug the precedence above exists to fix.
1498
+ * Matching the two by reviewer is what keeps the date attached to a request that is really there.
1499
+ *
1500
+ * `null` when nothing matches, which the precedence reads as an undated request and treats
1501
+ * conservatively: the review stands rather than being dismissed on a guess.
1502
+ */
1503
+ function standingReviewRequestedAt(requests, timeline) {
1504
+ const standing = new Set();
1505
+ for (const node of requests?.nodes ?? []) {
1506
+ const key = reviewerKey(node?.requestedReviewer);
1507
+ if (key)
1508
+ standing.add(key);
1509
+ }
1510
+ if (standing.size === 0)
1511
+ return null;
1512
+ let newest = null;
1513
+ for (const node of timeline?.nodes ?? []) {
1514
+ if (!node?.createdAt)
1515
+ continue;
1516
+ const key = reviewerKey(node.requestedReviewer);
1517
+ if (!key || !standing.has(key))
1518
+ continue;
1519
+ if (!newest || isAfter(node.createdAt, newest))
1520
+ newest = node.createdAt;
1521
+ }
1522
+ return newest;
1523
+ }
1524
+ const ghRefStatusIssueSchema = z.object({ state: z.string(), stateReason: z.string().nullish() }).nullish();
1525
+ // The aliases hold two DIFFERENT shapes, so the record stays unvalidated here and each alias is
1526
+ // parsed by its own schema below. A union at this level would be wrong, not merely loose: zod
1527
+ // strips unknown keys, so a PR node matched by the issue arm would come back with `isDraft` and
1528
+ // `commits` silently removed.
1529
+ const ghRefStatusSchema = z.record(z.string(), z.unknown());
1530
+ /**
1531
+ * Query issueOrPullRequest once per number and use __typename for the actual kind. Include
1532
+ * branch-work and review timestamps for freshness decisions.
1533
+ */
1534
+ function refStatusQuery(numbers) {
1535
+ const aliases = numbers
1536
+ .map((n, i) => ` r${i}: issueOrPullRequest(number: ${n}) { __typename ... on PullRequest { url mergedAt state isDraft reviewDecision mergeable commits(last: 1) { nodes { commit { committedDate parents(first: 1) { totalCount nodes { committedDate } } statusCheckRollup { state } } } } reviews(last: 1, states: CHANGES_REQUESTED) { nodes { submittedAt } } reviewRequests(first: 20) { totalCount nodes { requestedReviewer { __typename ... on User { login } ... on Bot { login } ... on Team { slug } } } } timelineItems(last: 20, itemTypes: [REVIEW_REQUESTED_EVENT]) { nodes { ... on ReviewRequestedEvent { createdAt requestedReviewer { __typename ... on User { login } ... on Bot { login } ... on Team { slug } } } } } } ... on Issue { state stateReason } }`)
1537
+ .join('\n');
1538
+ return `query ($owner: String!, $name: String!) {\n repository(owner: $owner, name: $name) {\nnameWithOwner\n${aliases}\n }\n}`;
1539
+ }
1540
+ /**
1541
+ * Derive whose action is pending: terminal, draft, pending checks, current requested changes,
1542
+ * failing checks, review, then ready. Use dated review/work evidence; mergeability remains a
1543
+ * separate axis.
1544
+ */
1545
+ export function derivePrReferenceStatus(pr) {
1546
+ const state = pr.state.toUpperCase();
1547
+ if (state === 'MERGED')
1548
+ return 'merged';
1549
+ if (state === 'CLOSED')
1550
+ return 'closed';
1551
+ if (pr.isDraft)
1552
+ return 'draft';
1553
+ if (pr.checks === 'pending')
1554
+ return 'checks-pending';
1555
+ const decision = (pr.reviewDecision ?? '').toUpperCase();
1556
+ const changesRequested = decision === 'CHANGES_REQUESTED';
1557
+ // Has the author already answered the review — by asking for another look, or by pushing?
1558
+ // A standing request counts as the ask only if it POSTDATES the review; one made before it is a
1559
+ // reviewer who has not got to the PR yet, and on a PR where someone else rejected it that
1560
+ // request would otherwise mask the rejection indefinitely. With no review date at all it still
1561
+ // counts — that is the empty-`reviews` case above, where it is the only signal there is.
1562
+ const reRequested = pr.reviewRequested === true && (!pr.changesRequestedAt || isAfter(pr.reviewRequestedAt, pr.changesRequestedAt));
1563
+ // A push counts as the answer, judged by the newest commit that is actual WORK. GitHub's "Update
1564
+ // branch" button (and this cockpit's own "Resolve conflicts") writes `Merge branch 'main' into
1565
+ // <branch>` dated NOW, newer than any review while addressing none of it: the click people make
1566
+ // reflexively on a stale PR must not wipe a rejection off the chip. Two parents is what tells
1567
+ // that commit apart from work.
1568
+ //
1569
+ // Which is why a merge is not simply DISQUALIFYING: it is transparent. Reading only the head
1570
+ // would let a merge landing on top of a genuine fix erase that fix's answer and flip the chip
1571
+ // back to red, blaming an author who already responded — the same misattribution this whole
1572
+ // function exists to prevent, just pointed the other way. The merge's FIRST parent is the
1573
+ // branch's previous tip, so it carries the date of the work the merge sat on top of.
1574
+ const workCommittedAt = (pr.headParentCount ?? 1) < 2 ? pr.headCommittedAt : pr.headFirstParentCommittedAt;
1575
+ const pushed = isAfter(workCommittedAt, pr.changesRequestedAt);
1576
+ const answered = reRequested || pushed;
1577
+ if (changesRequested && !answered)
1578
+ return 'changes-requested';
1579
+ if (pr.checks === 'failing')
1580
+ return 'checks-failing';
1581
+ // `APPROVED` is the forge saying the review requirement IS MET, and it outranks a pending
1582
+ // request: a reviewer left on the list after someone else approved is a courtesy ask, not an
1583
+ // unmet gate. (A repo needing two approvals reports `REVIEW_REQUIRED` until it has both, so
1584
+ // `APPROVED` never arrives early.) Without this, an approved, green, mergeable pull request
1585
+ // reads "waiting for review" for as long as anyone stays listed — which is indefinitely.
1586
+ if (decision === 'APPROVED')
1587
+ return 'ready';
1588
+ if (changesRequested || decision === 'REVIEW_REQUIRED' || pr.reviewRequested === true) {
1589
+ return 'review-required';
1590
+ }
1591
+ return 'ready';
1592
+ }
1593
+ export function mergeabilityOf(state, mergeable) {
1594
+ if (state.toUpperCase() !== 'OPEN')
1595
+ return undefined;
1596
+ switch (mergeable?.toUpperCase()) {
1597
+ case 'MERGEABLE':
1598
+ return 'mergeable';
1599
+ case 'CONFLICTING':
1600
+ return 'conflicting';
1601
+ default:
1602
+ // Includes a field GitHub omitted entirely: not being told is not being told.
1603
+ return 'unknown';
1604
+ }
1605
+ }
1606
+ /** Did `later` happen AFTER `earlier` — a commit, or a re-request, landing past the review?
1607
+ * Unparseable or missing dates answer `false`, which is the conservative direction for both
1608
+ * callers: they only ever use a `true` to demote a review, so no answer must keep the review
1609
+ * current rather than silently dismiss one. */
1610
+ function isAfter(later, earlier) {
1611
+ const a = later ? Date.parse(later) : NaN;
1612
+ const b = earlier ? Date.parse(earlier) : NaN;
1613
+ return Number.isFinite(a) && Number.isFinite(b) && a > b;
1614
+ }
1615
+ /**
1616
+ * An issue's two signals as one word. `NOT_PLANNED` is kept apart from `completed` because they
1617
+ * are opposite outcomes — "we did it" vs "we won't" — and a task whose issue was declined must
1618
+ * not read as a task that landed.
1619
+ */
1620
+ export function deriveIssueReferenceStatus(issue) {
1621
+ if (issue.state.toUpperCase() !== 'CLOSED')
1622
+ return 'open';
1623
+ return (issue.stateReason ?? '').toUpperCase() === 'NOT_PLANNED' ? 'not-planned' : 'completed';
1624
+ }
1625
+ function refStatusGraphqlClean(raw, repository, owner, name) {
1626
+ return (!raw.errors || (Array.isArray(raw.errors) && raw.errors.length === 0)) &&
1627
+ typeof repository.nameWithOwner === 'string' && repository.nameWithOwner.toLowerCase() === `${owner}/${name}`.toLowerCase();
1628
+ }
1629
+ function prHeadCommit(pr) {
1630
+ return pr.commits?.nodes[0]?.commit;
1631
+ }
1632
+ function prRollupChecks(head) {
1633
+ const rollup = head?.statusCheckRollup;
1634
+ // Adapt the single rollup state into the array shape `rollupToChecks` expects,
1635
+ // reusing the FAILURE/PENDING/SUCCESS vocabulary rather than duplicating it.
1636
+ return rollup ? rollupToChecks([{ state: rollup.state, status: null, conclusion: null }]) ?? null : null;
1637
+ }
1638
+ function resolvePrReference(pr, raw, repository, owner, name, number) {
1639
+ if (refStatusGraphqlClean(raw, repository, owner, name)) {
1640
+ mergedPullRequestObservations.observe({ owner, repo: name, number }, { ...pr, number });
1641
+ }
1642
+ const head = prHeadCommit(pr);
1643
+ const mergeability = mergeabilityOf(pr.state, pr.mergeable);
1644
+ return {
1645
+ kind: 'pr',
1646
+ status: derivePrReferenceStatus({
1647
+ state: pr.state,
1648
+ isDraft: pr.isDraft,
1649
+ reviewDecision: pr.reviewDecision,
1650
+ checks: prRollupChecks(head),
1651
+ headCommittedAt: head?.committedDate,
1652
+ // Two parents = "Update branch", which is dated now and answers nothing on its own —
1653
+ // so the first parent's date, the work it sat on top of, is carried with it.
1654
+ headParentCount: head?.parents?.totalCount,
1655
+ headFirstParentCommittedAt: head?.parents?.nodes?.[0]?.committedDate,
1656
+ // `reviews(last: 1, states: CHANGES_REQUESTED)` — the timestamp only. WHETHER changes
1657
+ // are requested stays `reviewDecision`'s answer, which is the one that accounts for
1658
+ // dismissed and superseded reviews.
1659
+ changesRequestedAt: pr.reviews?.nodes[0]?.submittedAt,
1660
+ reviewRequested: (pr.reviewRequests?.totalCount ?? 0) > 0,
1661
+ // WHEN that standing request was made. `reviewRequests` carries no date of its own,
1662
+ // and without one an old request looks exactly like a re-request.
1663
+ reviewRequestedAt: standingReviewRequestedAt(pr.reviewRequests, pr.timelineItems),
1664
+ }),
1665
+ // The tri-state, not a boolean: `unknown` has to survive as far as the cache, which
1666
+ // is what decides to ask again in seconds rather than in a minute.
1667
+ ...(mergeability ? { mergeable: mergeability } : {}),
1668
+ };
1669
+ }
1670
+ function applyRefStatusNode(out, node, raw, repository, owner, name, number) {
1671
+ // Absent alias → the number is not in this repository at all. Left out of the map, which
1672
+ // is what the route reports as "not found" and the chip paints as neutral.
1673
+ if (!node)
1674
+ return;
1675
+ if (node.__typename === 'PullRequest') {
1676
+ const pr = ghRefStatusPrSchema.parse(node);
1677
+ if (!pr)
1678
+ return;
1679
+ out.resolved[number] = resolvePrReference(pr, raw, repository, owner, name, number);
1680
+ return;
1681
+ }
1682
+ if (node.__typename === 'Issue') {
1683
+ const issue = ghRefStatusIssueSchema.parse(node);
1684
+ if (!issue)
1685
+ return;
1686
+ out.resolved[number] = { kind: 'issue', status: deriveIssueReferenceStatus(issue) };
1687
+ }
1688
+ }
1689
+ async function fetchRefStatusChunk(runGraphql, owner, name, chunk, out) {
1690
+ try {
1691
+ const raw = JSON.parse(await runGraphql(refStatusQuery(chunk), { owner, name }));
1692
+ const repository = ghRefStatusSchema.parse(raw?.data?.repository ?? {});
1693
+ chunk.forEach((number, index) => {
1694
+ applyRefStatusNode(out, repository[`r${index}`], raw, repository, owner, name, number);
1695
+ });
1696
+ }
1697
+ catch (err) {
1698
+ // A failed chunk costs only its own numbers; the rest still resolve. They are recorded as
1699
+ // FAILED rather than left absent, so nothing downstream mistakes them for "no such number".
1700
+ out.failed.push(...chunk);
1701
+ out.reason ??= firstLine(err instanceof Error ? err.message : String(err));
1702
+ }
1703
+ }
1704
+ /**
1705
+ * Status per NUMBER, resolved to whatever that number actually is.
1706
+ *
1707
+ * Batched and aliased like `fetchPrChecks`: one subprocess per chunk, each alias resolving
1708
+ * independently, and a number the forge does not have simply staying absent from `resolved` (its
1709
+ * chip then renders neutral, as it did before this seam existed). A failed chunk costs only its
1710
+ * own numbers, and says so rather than letting them look absent. Exported for tests; `runGraphql`
1711
+ * is injected so this is testable without shelling out.
1712
+ */
1713
+ export async function fetchRefStatuses(runGraphql, owner, name, numbers, chunkSize = GH_REF_STATUS_MAX) {
1714
+ const out = { resolved: {}, failed: [] };
1715
+ if (numbers.length === 0)
1716
+ return out;
1717
+ for (let i = 0; i < numbers.length; i += chunkSize) {
1718
+ await fetchRefStatusChunk(runGraphql, owner, name, numbers.slice(i, i + chunkSize), out);
1719
+ }
1720
+ return out;
1721
+ }
1722
+ // Per-reference cache: keyed `repoRoot␀number` — by NUMBER, not by kind, because the kind is now
1723
+ // something the forge answers rather than something the caller asserts. Same 60 s TTL and bounded
1724
+ // shape as the checks cache; `null` is a cached "this repository has no such number", so a
1725
+ // transcript-scraped number from another repo is not re-queried on every table repaint.
1726
+ //
1727
+ // `unknownSince` is when this reference FIRST came back with its mergeability still being
1728
+ // computed, carried across refreshes so the fast recheck below is bounded to that first window
1729
+ // rather than restarting on every answer that is still `unknown`.
1730
+ const refStatusCache = new Map();
1731
+ const REF_STATUS_CACHE_MAX = 500;
1732
+ /** Test-only: drop the per-reference cache so cases don't leak state into each other. */
1733
+ export function __clearRefStatusCacheForTests() {
1734
+ refStatusCache.clear();
1735
+ refStatusFlights.clear();
1736
+ }
1737
+ /** Test-only: warm the cache the way the lazy route would have, so a reader can be tested
1738
+ * without a forge behind it. */
1739
+ export function __seedRefStatusCacheForTests(repoRoot, entries) {
1740
+ for (const [number, resolved] of entries) {
1741
+ refStatusCache.set(refStatusKey(repoRoot, number), { at: Date.now(), resolved });
1742
+ }
1743
+ }
1744
+ /**
1745
+ * Invalidate one reference after a known mutation. Let the next forge read establish status
1746
+ * rather than caching a guessed result.
1747
+ */
1748
+ export function forgetRefStatus(repoRoot, number) {
1749
+ refStatusCache.delete(refStatusKey(repoRoot, number));
1750
+ refStatusFlights.delete(refStatusKey(repoRoot, number));
1751
+ }
1752
+ /**
1753
+ * Everything the cache ALREADY knows about these numbers. Never spawns `gh`, never awaits.
1754
+ *
1755
+ * This is what lets a status ride along with the rows that carry the references, instead of the
1756
+ * cockpit fetching it separately a moment later: the run index reads whatever is warm and ships
1757
+ * it, and a cold entry is simply absent — the lazy `/github/ref-status` route stays the thing that
1758
+ * actually goes and asks.
1759
+ *
1760
+ * Because it cannot cost anything, the caller may pass a SUPERSET of the numbers it will really
1761
+ * display. That matters: deciding which of a run's references a chip shows is the cockpit's rule
1762
+ * (#407, #526), deliberately not duplicated server-side, and a cache read does not need to know —
1763
+ * it can look up every number a run mentions and let the client pick.
1764
+ */
1765
+ export function readCachedRefStatuses(repoRoot, numbers) {
1766
+ const out = { prs: {}, issues: {} };
1767
+ const now = Date.now();
1768
+ for (const number of new Set(numbers)) {
1769
+ const hit = refStatusCache.get(refStatusKey(repoRoot, number));
1770
+ if (!hit || !hit.resolved || now - hit.at >= refStatusTtl(hit.resolved, hit.unknownSince, now))
1771
+ continue;
1772
+ out[hit.resolved.kind === 'pr' ? 'prs' : 'issues'][number] = hit.resolved.status;
1773
+ }
1774
+ return out;
1775
+ }
1776
+ // The `#N` in a forge URL. Defined beside the run search matcher, which reads it too (#864).
1777
+ export { refNumberFromUrl } from '../../contract/index.js';
1778
+ /** A closed issue or an abandoned PR can be REOPENED, so this is long rather than forever — but
1779
+ * it is the rare event, and re-asking a hundred settled references every minute to catch it is
1780
+ * the wrong trade. */
1781
+ const REF_STATUS_CLOSED_TTL = 10 * 60_000;
1782
+ /** A merged pull request is merged forever: GitHub has no un-merge. Capped at a day only so a
1783
+ * long-lived server eventually re-reads rather than trusting a value from another era. */
1784
+ const REF_STATUS_MERGED_TTL = 24 * 60 * 60_000;
1785
+ /**
1786
+ * How long a cached answer stays fresh — by how changeable that answer IS.
1787
+ *
1788
+ * One TTL for everything gets both ends wrong: it re-asks about a merged PR (which cannot change)
1789
+ * every minute, and it is the only thing standing between a running CI job and a stale chip. What
1790
+ * a reader wants rechecked is precisely what is still moving.
1791
+ *
1792
+ * A number the repository does not have keeps the short TTL: it is usually a wrong number, but it
1793
+ * is also what a reference to a not-yet-created PR looks like, and re-asking is cheap.
1794
+ */
1795
+ function refStatusTtl(entry, unknownSince, now = Date.now()) {
1796
+ if (!entry)
1797
+ return CACHE_MS;
1798
+ // Mergeability GitHub has not finished computing is not an answer to cache for a minute. It is
1799
+ // the normal reply for the first seconds after a push, and holding it that long is what let a
1800
+ // conflicting pull request read "Ready to merge" until the page was reloaded. Ask again in
1801
+ // seconds instead — and only while it is still plausibly being computed, so a repository that
1802
+ // answers `UNKNOWN` indefinitely settles back to the ordinary cadence rather than spawning `gh`
1803
+ // every few seconds forever.
1804
+ if (entry.mergeable === 'unknown' &&
1805
+ unknownSince !== undefined &&
1806
+ now - unknownSince < MERGEABILITY_UNKNOWN_WINDOW_MS) {
1807
+ return MERGEABILITY_UNKNOWN_TTL_MS;
1808
+ }
1809
+ switch (entry.status) {
1810
+ case 'merged':
1811
+ return REF_STATUS_MERGED_TTL;
1812
+ case 'closed':
1813
+ case 'completed':
1814
+ case 'not-planned':
1815
+ return REF_STATUS_CLOSED_TTL;
1816
+ default:
1817
+ return CACHE_MS;
1818
+ }
1819
+ }
1820
+ /** How long a status can be trusted to stay put — `null` when it can never change again. The
1821
+ * cadence half of `refStatusTtl`, and deliberately the same function: a value the cache would
1822
+ * still be serving is a value there is no point asking for, and a value it would NOT serve —
1823
+ * mergeability still being computed — is one the cockpit should come back for just as soon. */
1824
+ function refStatusRecheckAfter(entry, unknownSince, now = Date.now()) {
1825
+ if (entry?.status === 'merged')
1826
+ return null; // GitHub has no un-merge
1827
+ return refStatusTtl(entry, unknownSince, now);
1828
+ }
1829
+ /** How long the WHOLE answer holds — the soonest any single reference in it could differ. `null`
1830
+ * only when every one of them is immutable, which is what tells the cockpit to stop scheduling.
1831
+ * Taking the per-reference values rather than the entries, because one of them may be on the fast
1832
+ * mergeability cadence and the batch has to travel at the speed of its most impatient member. */
1833
+ function batchRecheckAfter(rechecks) {
1834
+ let soonest = null;
1835
+ for (const after of rechecks) {
1836
+ if (after === null)
1837
+ continue;
1838
+ soonest = soonest === null ? after : Math.min(soonest, after);
1839
+ }
1840
+ return soonest;
1841
+ }
1842
+ /**
1843
+ * How long a still-computing mergeability holds, and for how long that fast cadence applies.
1844
+ *
1845
+ * Five seconds because that is the shape of the thing being waited for: GitHub kicks off the
1846
+ * merge-base computation when asked and usually has it by the next request. Bounded to a minute
1847
+ * because a value that is STILL unknown after that is not a computation in flight any more — it is
1848
+ * a repository that will not answer, and re-asking it every five seconds forever costs a `gh`
1849
+ * subprocess a second for nothing.
1850
+ */
1851
+ const MERGEABILITY_UNKNOWN_TTL_MS = 5_000;
1852
+ const MERGEABILITY_UNKNOWN_WINDOW_MS = 60_000;
1853
+ /** A forge that could not be reached is worth retrying, and worth not hammering: a workspace with
1854
+ * no `gh` installed would otherwise spawn a subprocess a minute, forever, to be told the same
1855
+ * thing. Five minutes is the same order as the cockpit's own reconnect cadence. */
1856
+ const REF_STATUS_RETRY_MS = 5 * 60_000;
1857
+ /**
1858
+ * Use failed gh GraphQL stdout only when it contains usable data.repository. Preserve successful
1859
+ * aliases in partial responses; rethrow malformed or unresolved-repository failures.
1860
+ */
1861
+ function refStatusGraphql(repoRoot, context = githubContext(repoRoot)) {
1862
+ return async (query, variables) => {
1863
+ const args = ghGraphqlArgs(query, variables);
1864
+ try {
1865
+ return await gh(repoRoot, args, 15_000, undefined, context);
1866
+ }
1867
+ catch (err) {
1868
+ const stdout = err.stdout;
1869
+ if (typeof stdout === 'string' && hasResolvedRepository(stdout))
1870
+ return stdout;
1871
+ throw err;
1872
+ }
1873
+ };
1874
+ }
1875
+ /**
1876
+ * The repo handle, memoized in the same map `resolveRepoHandle` uses — but rethrowing instead of
1877
+ * answering `null`.
1878
+ *
1879
+ * The memo is the point: without it every recheck tick spawned a second subprocess to re-learn an
1880
+ * `owner/name` that changes approximately never, doubling the cost of the one query that actually
1881
+ * carries information. The difference from `resolveRepoHandle` is the failure: this route turns a
1882
+ * `gh` error into the payload's `reason` — "gh CLI not found — install it and run `gh auth login`"
1883
+ * is what the chip's tooltip now shows a user — and a `null` would erase which failure it was.
1884
+ */
1885
+ async function resolveRepoHandleStrict(repoRoot, context = githubContext(repoRoot)) {
1886
+ return sharedRepoHandle(repoRoot, undefined, context);
1887
+ }
1888
+ /** Is this a reply we can still read aliases out of? `repository: null` says the handle did not
1889
+ * resolve, which is a real failure and must not be read as "the numbers are all missing". */
1890
+ function hasResolvedRepository(stdout) {
1891
+ try {
1892
+ const parsed = JSON.parse(stdout);
1893
+ const repository = parsed?.data?.repository;
1894
+ return typeof repository === 'object' && repository !== null;
1895
+ }
1896
+ catch {
1897
+ return false;
1898
+ }
1899
+ }
1900
+ // Ownership is per reference, before discovery starts. Overlapping requests join just the
1901
+ // numbers they need; extra numbers form their own batch without a batching timer or new delay.
1902
+ // At capacity, untracked work answers its callers without caching: it has no ownership token
1903
+ // to protect publication against invalidation or newer requests. Retained ownership is at most 500.
1904
+ const refStatusFlights = new Map();
1905
+ async function sharedRefStatusMisses(repoRoot, numbers, context) {
1906
+ const pending = new Map();
1907
+ const owned = [];
1908
+ for (const n of numbers) {
1909
+ const flight = refStatusFlights.get(refStatusKey(repoRoot, n, context));
1910
+ if (flight)
1911
+ pending.set(n, flight);
1912
+ else
1913
+ owned.push(n);
1914
+ }
1915
+ if (owned.length) {
1916
+ const batch = (async () => {
1917
+ const handle = await resolveRepoHandleStrict(repoRoot, context);
1918
+ if (!handle)
1919
+ throw new Error('repository handle unavailable');
1920
+ return fetchRefStatuses(refStatusGraphql(repoRoot, context), handle.owner, handle.name, owned);
1921
+ })();
1922
+ for (const n of owned) {
1923
+ const key = refStatusKey(repoRoot, n, context);
1924
+ const tracked = refStatusFlights.size < REF_STATUS_CACHE_MAX;
1925
+ const promise = batch.then(result => {
1926
+ if (result.failed.includes(n))
1927
+ return { reason: result.reason ?? 'GitHub could not be reached' };
1928
+ // Date fresh results at arrival; keep the original unknown-mergeability retry window.
1929
+ const storedAt = Date.now();
1930
+ const entry = result.resolved[n] ?? null;
1931
+ const unknownSince = entry?.mergeable === 'unknown'
1932
+ ? (refStatusCache.get(key)?.unknownSince ?? storedAt) : undefined;
1933
+ const value = { resolved: entry, ...(unknownSince === undefined ? {} : { unknownSince }) };
1934
+ if (tracked && refStatusFlights.get(key) === promise) {
1935
+ refStatusCache.set(key, { at: storedAt, ...value });
1936
+ while (refStatusCache.size > REF_STATUS_CACHE_MAX)
1937
+ refStatusCache.delete(refStatusCache.keys().next().value);
1938
+ }
1939
+ return value;
1940
+ }).finally(() => {
1941
+ if (refStatusFlights.get(key) === promise)
1942
+ refStatusFlights.delete(key);
1943
+ });
1944
+ if (tracked)
1945
+ refStatusFlights.set(key, promise);
1946
+ pending.set(n, promise);
1947
+ }
1948
+ }
1949
+ return Promise.all(numbers.map(async (n) => [n, await pending.get(n)]));
1950
+ }
1951
+ /**
1952
+ * Route-facing reference status. Resolves the repo handle once, serves fresh cache entries,
1953
+ * queries only the misses, and degrades to `{ available: false, reason }` when `gh` or the handle
1954
+ * is unavailable — never a throw, never a 5xx.
1955
+ *
1956
+ * The two request lists are merged into ONE set of numbers: issues and pull requests share a
1957
+ * repository's numbering space, so a number is one thing and asking about it twice would be asking
1958
+ * the same question twice. What comes back is filed by what each number turned out to BE, which is
1959
+ * why a chip whose kind the cockpit guessed wrong still gets the right status.
1960
+ */
1961
+ export async function fetchGithubRefStatus(repoRoot, input) {
1962
+ const context = githubContext(repoRoot);
1963
+ const asPrs = sanitizeRefNumbers(input.prs);
1964
+ const asIssues = sanitizeRefNumbers(input.issues);
1965
+ if (productEnv('DRY_RUN') === '1')
1966
+ return mockGithubRefStatus(asPrs, asIssues);
1967
+ const wanted = [...new Set([...asPrs, ...asIssues])];
1968
+ const resolved = { prs: {}, issues: {} };
1969
+ // How soon each reference in the answer could differ — what the batch's cadence is the minimum
1970
+ // of. Per reference rather than per batch because mergeability still being computed is worth
1971
+ // coming back for in seconds while everything else holds for a minute.
1972
+ const rechecks = [];
1973
+ // The second axis, and a list rather than a map because it is nearly always empty: only the
1974
+ // pull requests the forge actively called CONFLICTING are named (see `mergeabilityOf`).
1975
+ const conflicts = [];
1976
+ const file = (number, entry, unknownSince) => {
1977
+ rechecks.push(refStatusRecheckAfter(entry, unknownSince));
1978
+ if (!entry)
1979
+ return;
1980
+ resolved[entry.kind === 'pr' ? 'prs' : 'issues'][number] = entry.status;
1981
+ if (entry.mergeable === 'conflicting')
1982
+ conflicts.push(number);
1983
+ };
1984
+ const misses = [];
1985
+ const now = Date.now();
1986
+ for (const n of wanted) {
1987
+ const hit = refStatusCache.get(refStatusKey(repoRoot, n, context));
1988
+ if (!hit || now - hit.at >= refStatusTtl(hit.resolved, hit.unknownSince, now))
1989
+ misses.push(n);
1990
+ else
1991
+ file(n, hit.resolved, hit.unknownSince);
1992
+ }
1993
+ if (misses.length === 0) {
1994
+ return {
1995
+ available: true,
1996
+ prs: resolved.prs,
1997
+ issues: resolved.issues,
1998
+ conflicts,
1999
+ recheckAfterMs: batchRecheckAfter(rechecks),
2000
+ };
2001
+ }
2002
+ try {
2003
+ const entries = await sharedRefStatusMisses(repoRoot, misses, context);
2004
+ let failure;
2005
+ for (const [n, result] of entries) {
2006
+ if ('reason' in result)
2007
+ failure ??= result.reason;
2008
+ else
2009
+ file(n, result.resolved, result.unknownSince);
2010
+ }
2011
+ // Anything unasked makes the whole answer `unavailable`, deliberately. The alternative is a
2012
+ // payload where a number we could not reach is indistinguishable from one that does not exist,
2013
+ // and the cockpit would paint "not found on this repository" over a perfectly good PR — the
2014
+ // exact defect this route was reported for. The successes are cached either way, so the next
2015
+ // request costs only the numbers that failed and usually answers in full.
2016
+ if (failure !== undefined) {
2017
+ return {
2018
+ available: false,
2019
+ reason: failure,
2020
+ recheckAfterMs: REF_STATUS_RETRY_MS,
2021
+ };
2022
+ }
2023
+ return {
2024
+ available: true,
2025
+ prs: resolved.prs,
2026
+ issues: resolved.issues,
2027
+ conflicts,
2028
+ recheckAfterMs: batchRecheckAfter(rechecks),
2029
+ };
2030
+ }
2031
+ catch (err) {
2032
+ const message = err instanceof Error ? err.message : String(err);
2033
+ return {
2034
+ available: false,
2035
+ reason: /ENOENT/.test(message)
2036
+ ? 'gh CLI not found — install it and run `gh auth login`'
2037
+ : firstLine(message),
2038
+ recheckAfterMs: REF_STATUS_RETRY_MS,
2039
+ };
2040
+ }
2041
+ }
2042
+ /** NUL separator, as everywhere else here: two projects each having a #42 must not collide. */
2043
+ function refStatusKey(repoRoot, number, context = githubContext(repoRoot)) {
2044
+ return `${context.key}\0#${number}`;
2045
+ }
2046
+ function sanitizeRefNumbers(numbers) {
2047
+ return [...new Set(numbers ?? [])].filter((n) => Number.isInteger(n) && n > 0).slice(0, GH_REF_STATUS_MAX);
2048
+ }
2049
+ /** CEZ_DRY_RUN=1 — statuses derived from the mock catalog so the offline demo paints real chips. */
2050
+ function mockGithubRefStatus(prs, issues) {
2051
+ const catalog = mockGithub();
2052
+ const byPr = new Map(catalog.prs.map((p) => [p.number, p]));
2053
+ const byIssue = new Set(catalog.issues.map((i) => i.number));
2054
+ const out = { available: true, prs: {}, issues: {}, recheckAfterMs: CACHE_MS };
2055
+ for (const n of prs) {
2056
+ const pr = byPr.get(n);
2057
+ if (pr) {
2058
+ out.prs[n] = derivePrReferenceStatus({
2059
+ state: 'OPEN',
2060
+ isDraft: pr.isDraft,
2061
+ reviewDecision: null,
2062
+ checks: (pr.checks ?? null),
2063
+ });
2064
+ }
2065
+ }
2066
+ for (const n of issues)
2067
+ if (byIssue.has(n))
2068
+ out.issues[n] = 'open';
2069
+ return out;
2070
+ }
2071
+ /**
2072
+ * The conversation thread for one issue/PR, lazily. `{owner}`/`{repo}` in the gh api paths are
2073
+ * filled from the worktree's remote by gh itself, so no extra handle lookup. Everything degrades:
2074
+ * gh missing/offline → `{ available: false, reason }`, a 404 → a "not found" hint — never a throw.
2075
+ *
2076
+ * Since #525 the thread is sourced from `/issues/{n}/timeline`, which returns comments AND events
2077
+ * in one stream: `commented` rows go through the unchanged `normalizeComments`, the rest through
2078
+ * `normalizeEvents`. `comments[]` therefore keeps its exact pre-#525 shape, contents and cap
2079
+ * (BACKWARD_COMPATIBILITY.md) — see the top-up below for the one case that needed defending.
2080
+ */
2081
+ function commentsCacheKey(repoRoot, kind, number) {
2082
+ // NUL separator: cannot appear in a filesystem path, so roots can never alias.
2083
+ return `${repoRoot}\0${kind}#${number}`;
2084
+ }
2085
+ function cachedGithubComments(key, refresh) {
2086
+ const hit = commentsCache.get(key);
2087
+ if (!refresh && hit && Date.now() - hit.at < CACHE_MS)
2088
+ return hit.data;
2089
+ return undefined;
2090
+ }
2091
+ function legacyIssueComments(repoRoot, number) {
2092
+ // PR conversation comments live on the issues endpoint too — always use it for the body thread.
2093
+ return () => gh(repoRoot, ['api', `repos/{owner}/{repo}/issues/${number}/comments`, '--paginate']);
2094
+ }
2095
+ function parseUnknownRows(raw) {
2096
+ return z.array(z.unknown()).parse(JSON.parse(raw));
2097
+ }
2098
+ function commentedTimelineRows(rows) {
2099
+ return rows.filter((row) => row?.event === 'commented');
2100
+ }
2101
+ async function fetchIssueTimelinePages(repoRoot, number) {
2102
+ return fetchTimelinePages((page, timeoutMs) => gh(repoRoot, [
2103
+ 'api',
2104
+ '-H',
2105
+ 'Accept: application/vnd.github+json',
2106
+ `repos/{owner}/{repo}/issues/${number}/timeline?per_page=${TIMELINE_PER_PAGE}&page=${page}`,
2107
+ ], timeoutMs));
2108
+ }
2109
+ async function topUpTimelineComments(commentRows, stoppedShort, legacyComments) {
2110
+ // Comments top-up. The two ENTRY caps are independent, but the FETCH budget is not: today's
2111
+ // call is an unbounded --paginate over a comments-only endpoint, so every comment is fetched
2112
+ // and mergeThread picks the oldest 200 from the complete set. Under the timeline those same
2113
+ // 200 slots are filled from at most 1000 rows in which events compete with comments — so on
2114
+ // a 1500-row thread with 250 interleaved comments, comments[] would return ~167 where today
2115
+ // it returns 200. Same §2 defect class as a combined entry cap, arriving via the source set.
2116
+ //
2117
+ // The trigger deliberately over-fires: it cannot distinguish "comments were cut off" from
2118
+ // "this thread just has few comments", because the second is only knowable by fetching them.
2119
+ // So on an event-heavy thread with 30 comments it re-fetches 30 complete comments. Accepted
2120
+ // — it fires only past ~1000 timeline rows, behind the 60 s LRU, and the alternative is a §2
2121
+ // regression. In the other direction the threshold is provably sound: with ≥200 commented
2122
+ // rows already in the oldest-first prefix, mergeThread's oldest-200 cut lies inside it.
2123
+ if (!(stoppedShort && commentRows.length < THREAD_ENTRY_CAP))
2124
+ return commentRows;
2125
+ // Its OWN nested catch. The inner handler's remedy is the fallback — which is this very
2126
+ // call — and the outer handler returns { available: false, comments: [] }, emptying a
2127
+ // thread that was about to render. So a thrown top-up is swallowed: keep the commented
2128
+ // rows the timeline already returned and carry on. That is at worst the short list the
2129
+ // safeguard existed to avoid, never worse than not having attempted it.
2130
+ try {
2131
+ return parseUnknownRows(await legacyComments());
2132
+ }
2133
+ catch {
2134
+ // keep the timeline's own commented rows
2135
+ return commentRows;
2136
+ }
2137
+ }
2138
+ async function loadGithubThreadSource(repoRoot, number, legacyComments) {
2139
+ const source = {
2140
+ commentRows: [],
2141
+ events: undefined,
2142
+ eventsTruncated: false,
2143
+ stoppedShort: false,
2144
+ };
2145
+ try {
2146
+ const pages = await fetchIssueTimelinePages(repoRoot, number);
2147
+ source.stoppedShort = pages.stoppedShort;
2148
+ source.commentRows = commentedTimelineRows(pages.rows);
2149
+ const normalized = normalizeEvents(pages.rows);
2150
+ source.events = normalized.events;
2151
+ source.eventsTruncated = normalized.truncated;
2152
+ source.commentRows = await topUpTimelineComments(source.commentRows, source.stoppedShort, legacyComments);
2153
+ return source;
2154
+ }
2155
+ catch (timelineErr) {
2156
+ // Scoped INSIDE the existing outer catch on purpose: the outer handler's /404|not found/i
2157
+ // branch would otherwise turn a timeline 404 into an empty thread. ENOENT is the one failure
2158
+ // the fallback cannot rescue — nothing will work, and a second spawn fails identically.
2159
+ const message = timelineErr instanceof Error ? timelineErr.message : String(timelineErr);
2160
+ if (/ENOENT/.test(message))
2161
+ throw timelineErr;
2162
+ // Every other endpoint-level failure substitutes the legacy comments call. It is a
2163
+ // substitution, not a retry — a different endpoint, which typically still answers. (A 403
2164
+ // attaches to the token rather than the endpoint, so it cannot succeed either; not
2165
+ // special-cased, since rate-limit replies are immediate and it costs one fast spawn.)
2166
+ source.commentRows = parseUnknownRows(await legacyComments());
2167
+ source.events = undefined;
2168
+ return source;
2169
+ }
2170
+ }
2171
+ function applyCommitChecks(events, checks) {
2172
+ for (const event of events) {
2173
+ if (event.kind !== 'committed' || !event.sha)
2174
+ continue;
2175
+ if (event.sha in checks) {
2176
+ delete event.checks;
2177
+ Object.assign(event, defined({ checks: checks[event.sha] }));
2178
+ }
2179
+ }
2180
+ }
2181
+ async function attachCommitChecks(repoRoot, events) {
2182
+ // Per-commit CI (#525 Phase 2). One extra subprocess per opened thread that contains commits,
2183
+ // behind the same 60 s LRU — a per-thread-open cost, not per-render. Every failure path here
2184
+ // leaves `checks` ABSENT rather than null, so commits simply render unglyphed: the fetch
2185
+ // degrades to "no data" and the render decides what absence looks like.
2186
+ const commitShas = (events ?? []).flatMap((event) => (event.kind === 'committed' && event.sha ? [event.sha] : []));
2187
+ if (commitShas.length === 0)
2188
+ return;
2189
+ const handle = await resolveRepoHandle(repoRoot);
2190
+ if (!handle)
2191
+ return;
2192
+ const runGraphql = (query, variables) => {
2193
+ const args = ghGraphqlArgs(query, variables);
2194
+ return gh(repoRoot, args);
2195
+ };
2196
+ applyCommitChecks(events ?? [], await fetchCommitChecks(runGraphql, handle.owner, handle.name, commitShas));
2197
+ }
2198
+ async function mergeGithubThread(repoRoot, kind, number, commentRows) {
2199
+ const parts = [normalizeComments(commentRows)];
2200
+ if (kind === 'pr') {
2201
+ const reviewsOut = await gh(repoRoot, ['api', `repos/{owner}/{repo}/pulls/${number}/reviews`, '--paginate']);
2202
+ parts.push(normalizeReviews(JSON.parse(reviewsOut)));
2203
+ }
2204
+ return mergeThread(parts);
2205
+ }
2206
+ function commentsFailureReason(err) {
2207
+ const message = err instanceof Error ? err.message : String(err);
2208
+ if (/ENOENT/.test(message))
2209
+ return 'gh CLI not found — install it and run `gh auth login`';
2210
+ if (/404|not found/i.test(message))
2211
+ return 'not found on GitHub — it may be closed or deleted';
2212
+ return firstLine(message);
2213
+ }
2214
+ async function loadGithubComments(repoRoot, kind, number, key) {
2215
+ const legacyComments = legacyIssueComments(repoRoot, number);
2216
+ // The detail tier (hearsay-tools/cezarion#1183): the list no longer has to carry a body, so
2217
+ // the pane asks for it here, in parallel with the thread. It is attached only to a thread
2218
+ // that loaded, and a failed view never costs the thread.
2219
+ const detail = fetchItemDetail(repoRoot, kind, number);
2220
+ try {
2221
+ const source = await loadGithubThreadSource(repoRoot, number, legacyComments);
2222
+ await attachCommitChecks(repoRoot, source.events);
2223
+ const { comments, truncated } = await mergeGithubThread(repoRoot, kind, number, source.commentRows);
2224
+ const data = defined({
2225
+ available: true,
2226
+ comments,
2227
+ // OR-folded exactly as before, so the pre-existing >200-comments trigger is preserved
2228
+ // rather than replaced.
2229
+ truncated: truncated || source.eventsTruncated || source.stoppedShort || undefined,
2230
+ });
2231
+ if (source.events)
2232
+ data.events = source.events;
2233
+ const itemDetail = await detail;
2234
+ if (itemDetail)
2235
+ data.detail = itemDetail;
2236
+ cacheComments(key, data);
2237
+ return data;
2238
+ }
2239
+ catch (err) {
2240
+ return { available: false, reason: commentsFailureReason(err), comments: [] };
2241
+ }
2242
+ }
2243
+ export async function fetchGithubComments(repoRoot, kind, number, refresh = false) {
2244
+ if (productEnv('DRY_RUN') === '1')
2245
+ return mockGithubComments(kind, number);
2246
+ const key = commentsCacheKey(repoRoot, kind, number);
2247
+ const cached = cachedGithubComments(key, refresh);
2248
+ if (cached)
2249
+ return cached;
2250
+ return loadGithubComments(repoRoot, kind, number, key);
2251
+ }
2252
+ /** CEZ_DRY_RUN=1 — a small fixed thread (one image-bearing comment, plus a review for PRs) so
2253
+ * the whole feature is demoable and e2e-testable offline. */
2254
+ function mockGithubComments(kind, number) {
2255
+ const base = Date.now() - 3_600_000;
2256
+ const at = (offset) => new Date(base + offset).toISOString();
2257
+ const comments = [
2258
+ {
2259
+ id: 1,
2260
+ author: 'ada',
2261
+ avatarUrl: 'https://avatars.githubusercontent.com/u/1?v=4',
2262
+ createdAt: at(0),
2263
+ body: 'Thanks for the report — I can reproduce. Which browser were you on?\n\n```\nchrome 126, macOS\n```',
2264
+ kind: 'comment',
2265
+ url: 'https://github.com/mock/repo/issues/1#issuecomment-1',
2266
+ },
2267
+ {
2268
+ id: 2,
2269
+ author: 'lin',
2270
+ createdAt: at(600_000),
2271
+ body: 'Here is the failing screen:\n\n![failure](https://avatars.githubusercontent.com/u/2?v=4)',
2272
+ kind: 'comment',
2273
+ url: 'https://github.com/mock/repo/issues/1#issuecomment-2',
2274
+ },
2275
+ ];
2276
+ if (kind === 'pr') {
2277
+ comments.push({
2278
+ id: 3,
2279
+ author: 'grace',
2280
+ avatarUrl: 'https://avatars.githubusercontent.com/u/3?v=4',
2281
+ createdAt: at(1_200_000),
2282
+ body: 'Looks good overall — please add a regression test before this lands.',
2283
+ kind: 'review',
2284
+ reviewState: 'changes_requested',
2285
+ url: 'https://github.com/mock/repo/pull/1#pullrequestreview-3',
2286
+ });
2287
+ }
2288
+ // Timeline events (#525) so the whole feature is demoable and e2e-testable offline. Deliberately
2289
+ // covers the cases that are easy to get wrong rather than one of each: a multi-commit run by ONE
2290
+ // author (exercises the client-side grouping) with MIXED check states (passing/failing/pending
2291
+ // plus one `null` = no CI configured), a label change, a cross-reference, and — for PRs — a
2292
+ // merge. SHAs are full 40-char because the rollup query's `oid` rejects abbreviated ones, and a
2293
+ // fixture that cheated there would not survive being pasted into a real query.
2294
+ const sha = (seed) => seed.repeat(40).slice(0, 40);
2295
+ const events = [
2296
+ {
2297
+ id: 'evt-100',
2298
+ kind: 'labeled',
2299
+ actor: 'ada',
2300
+ avatarUrl: 'https://avatars.githubusercontent.com/u/1?v=4',
2301
+ createdAt: at(300_000),
2302
+ label: { name: 'bug', color: 'd73a4a' },
2303
+ },
2304
+ {
2305
+ id: `evt-${sha('a')}`,
2306
+ kind: 'committed',
2307
+ actor: 'Lin Zhao',
2308
+ createdAt: at(900_000),
2309
+ sha: sha('a'),
2310
+ message: 'fix(session): keep the refresh token on reload',
2311
+ checks: 'passing',
2312
+ },
2313
+ {
2314
+ id: `evt-${sha('b')}`,
2315
+ kind: 'committed',
2316
+ actor: 'Lin Zhao',
2317
+ createdAt: at(960_000),
2318
+ sha: sha('b'),
2319
+ message: 'test(session): cover the reload path',
2320
+ checks: 'failing',
2321
+ },
2322
+ {
2323
+ id: `evt-${sha('c')}`,
2324
+ kind: 'committed',
2325
+ actor: 'Lin Zhao',
2326
+ createdAt: at(1_020_000),
2327
+ sha: sha('c'),
2328
+ message: 'chore: appease the linter',
2329
+ checks: 'pending',
2330
+ },
2331
+ {
2332
+ id: `evt-${sha('d')}`,
2333
+ kind: 'committed',
2334
+ actor: 'Lin Zhao',
2335
+ createdAt: at(1_080_000),
2336
+ sha: sha('d'),
2337
+ message: 'docs: note the new behavior',
2338
+ checks: null, // no CI configured — renders no glyph, distinct from absent
2339
+ },
2340
+ {
2341
+ id: 'evt-101',
2342
+ kind: 'cross-referenced',
2343
+ actor: 'grace',
2344
+ avatarUrl: 'https://avatars.githubusercontent.com/u/3?v=4',
2345
+ createdAt: at(1_500_000),
2346
+ refNumber: 42,
2347
+ refTitle: 'Session handling rewrite',
2348
+ refIsPr: true,
2349
+ url: 'https://github.com/mock/repo/pull/42',
2350
+ },
2351
+ ];
2352
+ if (kind === 'pr') {
2353
+ events.push({
2354
+ id: 'evt-102',
2355
+ kind: 'merged',
2356
+ actor: 'grace',
2357
+ avatarUrl: 'https://avatars.githubusercontent.com/u/3?v=4',
2358
+ createdAt: at(1_800_000),
2359
+ });
2360
+ }
2361
+ // The detail tier (hearsay-tools/cezarion#1183): the catalog row's own body and diffstat.
2362
+ const row = number === undefined ? undefined
2363
+ : (kind === 'pr' ? mockGithub().prs : mockGithub().issues).find((item) => item.number === number);
2364
+ const detail = row ? {
2365
+ body: row.body ?? '',
2366
+ ...(kind === 'pr' ? { additions: row.additions ?? 0, deletions: row.deletions ?? 0 } : {}),
2367
+ } : undefined;
2368
+ return { available: true, comments, events, ...(detail ? { detail } : {}) };
2369
+ }
2370
+ // ---- the driver -------------------------------------------------------------
2371
+ /**
2372
+ * Cached availability probe so `GET /api/health` never pays a full listing. Keyed by repo root:
2373
+ * every registered project reads its own slot, so the boot project's constant health refresh can't
2374
+ * evict another project's answer (a single slot made `GET /automations` flap between "still being
2375
+ * checked" and available). One in-flight probe per root dedupes a burst of reads.
2376
+ */
2377
+ const DETECT_CACHE_MAX = 64;
2378
+ const detectCache = new Map();
2379
+ const detectInflight = new Map();
2380
+ async function probeGithub(repoRoot) {
2381
+ let result;
2382
+ try {
2383
+ await gh(repoRoot, ['repo', 'view', '--json', 'nameWithOwner'], 5_000);
2384
+ result = { available: true };
2385
+ }
2386
+ catch (err) {
2387
+ const message = err instanceof Error ? err.message : String(err);
2388
+ result = {
2389
+ available: false,
2390
+ reason: /ENOENT/.test(message)
2391
+ ? 'gh CLI not found — install it and run `gh auth login`'
2392
+ : firstLine(message),
2393
+ };
2394
+ }
2395
+ detectCache.delete(repoRoot); // re-insert so Map order tracks recency
2396
+ detectCache.set(repoRoot, { at: Date.now(), result });
2397
+ while (detectCache.size > DETECT_CACHE_MAX) {
2398
+ const oldest = detectCache.keys().next().value;
2399
+ if (oldest === undefined)
2400
+ break;
2401
+ detectCache.delete(oldest);
2402
+ }
2403
+ return result;
2404
+ }
2405
+ async function detectGithub(repoRoot) {
2406
+ if (productEnv('DRY_RUN') === '1')
2407
+ return { available: true };
2408
+ const hit = detectCache.get(repoRoot);
2409
+ if (hit && Date.now() - hit.at < CACHE_MS)
2410
+ return hit.result;
2411
+ const pending = detectInflight.get(repoRoot);
2412
+ if (pending)
2413
+ return pending;
2414
+ const probe = probeGithub(repoRoot).finally(() => detectInflight.delete(repoRoot));
2415
+ detectInflight.set(repoRoot, probe);
2416
+ return probe;
2417
+ }
2418
+ /**
2419
+ * Non-blocking availability for `GET /api/health` (#major-health-latency): serves the last-known
2420
+ * probe immediately (stale-while-revalidate) and only returns `null` on a cold start, before the
2421
+ * first probe has ever warmed the cache. It NEVER shells out to `gh` on the request that reads it,
2422
+ * so health stays under the bookmarklet's 800 ms port budget (a `gh repo view` round-trip is
2423
+ * ~500–650 ms on its own). `null` is contract-safe — the whole `forge` field is additive, so
2424
+ * "unknown until warm" is a valid answer.
2425
+ *
2426
+ * Serving the stale value while revalidating is what keeps the GitHub nav item from flickering:
2427
+ * without it, every time the 60 s cache expired this returned `null` for one 5 s health poll,
2428
+ * dropping `forge.available` and blinking the sidebar item out until the background probe warmed.
2429
+ */
2430
+ export function detectGithubCached(repoRoot) {
2431
+ if (productEnv('DRY_RUN') === '1')
2432
+ return { available: true };
2433
+ const hit = detectCache.get(repoRoot);
2434
+ const cached = hit?.result ?? null;
2435
+ const fresh = hit !== undefined && Date.now() - hit.at < CACHE_MS;
2436
+ if (!fresh) {
2437
+ void detectGithub(repoRoot).catch(() => { }); // revalidate off the request path
2438
+ }
2439
+ return cached; // last-known value while revalidating; null only until the first probe warms
2440
+ }
2441
+ const mergeStateCache = new Map();
2442
+ const mergeInflight = new Set();
2443
+ const MERGE_CACHE_MS = 15_000;
2444
+ async function fetchPrMergeState(repoRoot, repoRef, number, refresh = false) {
2445
+ if (productEnv('DRY_RUN') === '1') {
2446
+ return {
2447
+ available: true,
2448
+ mergeState: normalizeMergeState({
2449
+ number,
2450
+ title: 'Dry-run pull request',
2451
+ url: `https://github.com/mock/repo/pull/${number}`,
2452
+ state: 'OPEN',
2453
+ isDraft: false,
2454
+ headRefName: 'feat/dry-run',
2455
+ baseRefName: 'main',
2456
+ headRefOid: '0123456789abcdef0123456789abcdef01234567',
2457
+ mergeable: 'MERGEABLE',
2458
+ mergeStateStatus: 'CLEAN',
2459
+ reviewDecision: 'APPROVED',
2460
+ statusCheckRollup: [{ name: 'test', conclusion: 'SUCCESS', detailsUrl: 'https://github.com/mock/repo/actions' }],
2461
+ }, { allow_merge_commit: true, allow_squash_merge: true, allow_rebase_merge: true }, { readable: true, requiredChecks: ['test'] }),
2462
+ };
2463
+ }
2464
+ if (!repoRef)
2465
+ return { available: false, reason: 'GitHub remote could not be resolved' };
2466
+ const key = `${repoRoot}:${number}`;
2467
+ const hit = mergeStateCache.get(key);
2468
+ if (!refresh && hit && Date.now() - hit.at < MERGE_CACHE_MS)
2469
+ return hit.value;
2470
+ try {
2471
+ const prOut = await gh(repoRoot, [
2472
+ 'pr', 'view', String(number), '--json',
2473
+ 'number,title,url,state,isDraft,headRefName,baseRefName,headRefOid,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,mergedAt',
2474
+ ]);
2475
+ const parsedPr = mergePrSchema.parse(JSON.parse(prOut));
2476
+ if (!matchesGithubPullRequest({ ...repoRef, number }, parsedPr))
2477
+ return { available: false, reason: 'GitHub returned a different pull request.' };
2478
+ mergedPullRequestObservations.observe({ ...repoRef, number }, parsedPr);
2479
+ const [policyOut, requirements] = await Promise.all([
2480
+ gh(repoRoot, ['api', `repos/${repoRef.owner}/${repoRef.repo}`]),
2481
+ fetchGithubMergeRequirements((args) => gh(repoRoot, args), repoRef.owner, repoRef.repo, parsedPr.baseRefName, number),
2482
+ ]);
2483
+ const value = {
2484
+ available: true,
2485
+ mergeState: normalizeMergeState(parsedPr, JSON.parse(policyOut), requirements),
2486
+ };
2487
+ mergeStateCache.set(key, { at: Date.now(), value });
2488
+ return value;
2489
+ }
2490
+ catch (error) {
2491
+ return { available: false, reason: firstLine(error instanceof Error ? error.message : String(error)) };
2492
+ }
2493
+ }
2494
+ export function evictGithubProjectCaches(repoRoot) {
2495
+ const contextKey = githubContext(repoRoot).key;
2496
+ listCache.forEach((entry, key) => {
2497
+ if (entry.contextKey === contextKey)
2498
+ dropListEntry(key);
2499
+ });
2500
+ repositoryProjectsCache.delete(contextKey);
2501
+ commentCountsCache.forEach((_value, key) => {
2502
+ if (key.startsWith(`${contextKey}\0`))
2503
+ commentCountsCache.delete(key);
2504
+ });
2505
+ mergeStateCache.forEach((_value, key) => {
2506
+ if (key.startsWith(`${repoRoot}:`))
2507
+ mergeStateCache.delete(key);
2508
+ });
2509
+ commentsCache.forEach((_value, key) => {
2510
+ if (key.startsWith(`${repoRoot}\0`))
2511
+ commentsCache.delete(key);
2512
+ });
2513
+ itemCache.forEach((_value, key) => {
2514
+ if (key.startsWith(`${repoRoot}\0`))
2515
+ itemCache.delete(key);
2516
+ });
2517
+ }
2518
+ function mergeCancelledResult() {
2519
+ return { merged: false, status: 409, error: 'Automatic merging was cancelled.', code: 'cancelled' };
2520
+ }
2521
+ function mergeConflictResult(error, code, current) {
2522
+ return { merged: false, status: 409, error, code, current };
2523
+ }
2524
+ function mergePullRequestPreflight(current, input) {
2525
+ if (current.headSha !== input.expectedHeadSha) {
2526
+ return mergeConflictResult('The pull request head changed. Review the new commits before merging.', 'stale-head', current);
2527
+ }
2528
+ if (input.expectedHeadRef !== undefined && current.headRef !== input.expectedHeadRef) {
2529
+ return mergeConflictResult('The pull request branch changed.', 'stale-branch', current);
2530
+ }
2531
+ if (input.requirePassingChecks && (current.checks.length === 0 || current.checks.some(check => check.state !== 'passing'))) {
2532
+ return mergeConflictResult('Automatic merging requires at least one check and every current-head check passing.', 'checks-not-passing', current);
2533
+ }
2534
+ if (!current.methods.includes(input.method)) {
2535
+ return mergeConflictResult('That merge method is no longer enabled.', 'disabled-method', current);
2536
+ }
2537
+ if (!mergePreflightAllowed(current, !input.requirePassingChecks && input.overrideRules)) {
2538
+ return mergeConflictResult(current.blockers[0]?.message ?? 'The pull request is not eligible to merge.', current.eligibility, current);
2539
+ }
2540
+ return null;
2541
+ }
2542
+ async function mergeActiveCiConflict(repoRoot, repoRef, current, input) {
2543
+ if (!(input.requirePassingChecks && productEnv('DRY_RUN') !== '1'))
2544
+ return null;
2545
+ if (!repoRef)
2546
+ return { merged: false, status: 404, error: 'GitHub repository not found.' };
2547
+ const active = parseActiveWorkflowRuns(await gh(repoRoot, activeWorkflowRunsArgs({
2548
+ repository: `${repoRef.owner}/${repoRef.repo}`, headSha: current.headSha,
2549
+ })));
2550
+ if (input.canProceed?.() === false)
2551
+ return mergeCancelledResult();
2552
+ if (active)
2553
+ return mergeConflictResult('Waiting for active CI workflows on the current PR head.', 'checks-active', current);
2554
+ return null;
2555
+ }
2556
+ async function executeGithubMerge(repoRoot, repoRef, number, input, current) {
2557
+ if (productEnv('DRY_RUN') === '1') {
2558
+ evictGithubProjectCaches(repoRoot);
2559
+ return { merged: true, number, url: current.url, method: input.method, mergeCommitSha: 'abcdef0123456789abcdef0123456789abcdef01' };
2560
+ }
2561
+ if (!repoRef)
2562
+ return { merged: false, status: 404, error: 'GitHub repository not found.' };
2563
+ if (input.canProceed?.() === false)
2564
+ return mergeCancelledResult();
2565
+ const out = await gh(repoRoot, [
2566
+ 'api', '--method', 'PUT', `repos/${repoRef.owner}/${repoRef.repo}/pulls/${number}/merge`,
2567
+ '-f', `merge_method=${input.method}`, '-f', `sha=${input.expectedHeadSha}`,
2568
+ ]);
2569
+ // Once GitHub accepted the mutation, report its real outcome even if intent changed in flight.
2570
+ const result = ghMergeResultSchema.parse(JSON.parse(out));
2571
+ if (!result.merged)
2572
+ return mergeConflictResult(result.message ?? 'GitHub refused the merge.', 'github-blocked', current);
2573
+ evictGithubProjectCaches(repoRoot);
2574
+ return { merged: true, number, url: current.url, method: input.method, ...(result.sha ? { mergeCommitSha: result.sha } : {}) };
2575
+ }
2576
+ function mergePullRequestFailure(error) {
2577
+ const message = firstLine(error instanceof Error ? error.message : String(error));
2578
+ const status = /403|permission|forbidden/i.test(message) ? 403 : /404|not found/i.test(message) ? 404 : 502;
2579
+ return { merged: false, status, error: status === 403 ? 'GitHub permission denied.' : status === 404 ? 'Pull request or repository not found.' : 'GitHub could not complete the merge.' };
2580
+ }
2581
+ async function mergePullRequest(repoRoot, repoRef, number, input) {
2582
+ const key = `${repoRoot}:${number}`;
2583
+ if (mergeInflight.has(key))
2584
+ return { merged: false, status: 409, error: 'A merge is already in progress.', code: 'concurrent' };
2585
+ mergeInflight.add(key);
2586
+ try {
2587
+ if (input.canProceed?.() === false)
2588
+ return mergeCancelledResult();
2589
+ const fresh = await fetchPrMergeState(repoRoot, repoRef, number, true);
2590
+ if (input.canProceed?.() === false)
2591
+ return mergeCancelledResult();
2592
+ if (!fresh.available)
2593
+ return { merged: false, status: 502, error: fresh.reason };
2594
+ const current = fresh.mergeState;
2595
+ const preflight = mergePullRequestPreflight(current, input);
2596
+ if (preflight)
2597
+ return preflight;
2598
+ const ciConflict = await mergeActiveCiConflict(repoRoot, repoRef, current, input);
2599
+ if (ciConflict)
2600
+ return ciConflict;
2601
+ return await executeGithubMerge(repoRoot, repoRef, number, input, current);
2602
+ }
2603
+ catch (error) {
2604
+ return mergePullRequestFailure(error);
2605
+ }
2606
+ finally {
2607
+ mergeInflight.delete(key);
2608
+ }
2609
+ }
2610
+ export function mergePreflightAllowed(current, overrideRules = false) {
2611
+ return current.canMerge || (overrideRules && current.canOverride);
2612
+ }
2613
+ function readyCancelledResult() {
2614
+ return { available: false, reason: 'Marking the pull request ready was cancelled.' };
2615
+ }
2616
+ function readyPullRequestInputInvalid(number, input) {
2617
+ return !Number.isSafeInteger(number) || number < 1 || !/^[0-9a-f]{40}$/.test(input.expectedHeadSha) || !input.expectedHeadRef;
2618
+ }
2619
+ function readyPullRequestIdentityConflict(pr, repoRef, number, input) {
2620
+ if (!matchesGithubPullRequest({ ...repoRef, number }, pr))
2621
+ return { available: false, reason: 'GitHub returned a different pull request.' };
2622
+ if (pr.headRefOid !== input.expectedHeadSha)
2623
+ return { available: false, reason: 'The pull request head changed.' };
2624
+ if (pr.headRefName !== input.expectedHeadRef)
2625
+ return { available: false, reason: 'The pull request branch changed.' };
2626
+ if (pr.state !== 'OPEN')
2627
+ return { available: false, reason: 'The pull request is no longer open.' };
2628
+ return null;
2629
+ }
2630
+ async function markDraftPullRequestReady(repoRoot, repoRef, number, input) {
2631
+ if (input.canProceed?.() === false)
2632
+ return readyCancelledResult();
2633
+ await gh(repoRoot, ['pr', 'ready', String(number), '--repo', `github.com/${repoRef.owner}/${repoRef.repo}`]);
2634
+ evictGithubProjectCaches(repoRoot);
2635
+ if (input.canProceed?.() === false)
2636
+ return readyCancelledResult();
2637
+ return null;
2638
+ }
2639
+ async function readyPullRequestOwnership(repoRoot, repoRef, number, input) {
2640
+ if (!repoRef)
2641
+ return { available: false, reason: 'GitHub remote could not be resolved' };
2642
+ const pr = mergePrSchema.pick({ number: true, url: true, state: true, isDraft: true, headRefName: true, headRefOid: true }).parse(JSON.parse(await gh(repoRoot, [
2643
+ 'pr', 'view', String(number), '--json', 'number,url,state,isDraft,headRefName,headRefOid',
2644
+ ])));
2645
+ if (input.canProceed?.() === false)
2646
+ return readyCancelledResult();
2647
+ const identity = readyPullRequestIdentityConflict(pr, repoRef, number, input);
2648
+ if (identity)
2649
+ return identity;
2650
+ if (!pr.isDraft)
2651
+ return null;
2652
+ return markDraftPullRequestReady(repoRoot, repoRef, number, input);
2653
+ }
2654
+ async function refreshReadyPullRequestState(repoRoot, repoRef, number, input) {
2655
+ const fresh = await fetchPrMergeState(repoRoot, repoRef, number, true);
2656
+ if (input.canProceed?.() === false)
2657
+ return readyCancelledResult();
2658
+ if (fresh.available && fresh.mergeState.headSha !== input.expectedHeadSha)
2659
+ return { available: false, reason: 'The pull request head changed.' };
2660
+ if (fresh.available && fresh.mergeState.headRef !== input.expectedHeadRef)
2661
+ return { available: false, reason: 'The pull request branch changed.' };
2662
+ return fresh;
2663
+ }
2664
+ /** gh has no compare-and-swap ready operation. Check ownership immediately before marking ready,
2665
+ * then refresh again; the later merge has its own head guard and GitHub's atomic SHA check. */
2666
+ async function readyPullRequest(repoRoot, repoRef, number, input) {
2667
+ if (readyPullRequestInputInvalid(number, input)) {
2668
+ return { available: false, reason: 'A valid pull request, head SHA and task branch are required.' };
2669
+ }
2670
+ const key = `${repoRoot}:${number}`;
2671
+ if (mergeInflight.has(key))
2672
+ return { available: false, reason: 'A pull request mutation is already in progress.' };
2673
+ mergeInflight.add(key);
2674
+ try {
2675
+ if (input.canProceed?.() === false)
2676
+ return readyCancelledResult();
2677
+ if (productEnv('DRY_RUN') !== '1') {
2678
+ const ownership = await readyPullRequestOwnership(repoRoot, repoRef, number, input);
2679
+ if (ownership)
2680
+ return ownership;
2681
+ }
2682
+ return await refreshReadyPullRequestState(repoRoot, repoRef, number, input);
2683
+ }
2684
+ catch {
2685
+ return { available: false, reason: 'GitHub could not mark the pull request ready for review.' };
2686
+ }
2687
+ finally {
2688
+ mergeInflight.delete(key);
2689
+ }
2690
+ }
2691
+ const GH_PR_STATES = {
2692
+ MERGED: 'merged',
2693
+ CLOSED: 'closed',
2694
+ };
2695
+ export function createGithubDriver(repoRoot, repoRef) {
2696
+ return {
2697
+ kind: 'github',
2698
+ discoverRepository: (signal) => discoverRepoHandle(repoRoot, signal),
2699
+ resolveRepository: (signal) => resolveRepoHandle(repoRoot, signal),
2700
+ mergedPullRequests: (identities, signal) => lookupMergedPullRequests(repoRoot, identities, signal),
2701
+ list: (opts) => fetchGithub(repoRoot, opts?.refresh, opts?.limit, opts),
2702
+ projects: (generation) => fetchGithubProjects(repoRoot, generation),
2703
+ item: (kind, number, opts) => fetchGithubItem(repoRoot, kind, number, opts?.refresh),
2704
+ comments: (kind, number, opts) => fetchGithubComments(repoRoot, kind, number, opts?.refresh),
2705
+ commentCounts: (kind, numbers, opts) => fetchGithubCommentCounts(repoRoot, kind, numbers, opts?.refresh),
2706
+ checks: (numbers) => fetchGithubChecks(repoRoot, numbers),
2707
+ refStatus: (input) => fetchGithubRefStatus(repoRoot, input),
2708
+ cachedRefStatuses: (numbers) => readCachedRefStatuses(repoRoot, numbers),
2709
+ branchPushed: (numbers) => {
2710
+ // A push is the event that changes what the chips say about this task's pull requests —
2711
+ // its checks start again, and its MERGEABILITY is recomputed from scratch. Both are cached
2712
+ // per number, so without this the cockpit would keep showing the pre-push answer (up to a
2713
+ // minute of "Ready to merge" for a branch that has just been rewritten) about a push the
2714
+ // user watched this server make.
2715
+ for (const number of numbers)
2716
+ forgetRefStatus(repoRoot, number);
2717
+ },
2718
+ detect: () => detectGithub(repoRoot),
2719
+ detectCached: () => detectGithubCached(repoRoot),
2720
+ listIssues: async (opts) => (await fetchGithub(repoRoot, opts?.refresh, opts?.limit, opts)).issues,
2721
+ listPRs: async (opts) => (await fetchGithub(repoRoot, opts?.refresh, opts?.limit, opts)).prs,
2722
+ // The open-only list tier's escape hatch (#730) — this is the only path that can reach a
2723
+ // closed or merged item.
2724
+ searchItems: (kind, query, opts) => searchGithubItems(repoRoot, kind, query, opts?.limit),
2725
+ viewItem: (kind, number) => viewGithubItem(repoRoot, kind, number),
2726
+ prDiff: (number, opts) => fetchGithubPrDiff(repoRoot, number, opts?.refresh),
2727
+ createPR: async (input) => {
2728
+ const outcome = await createDraftPr(input);
2729
+ if (outcome.ok) {
2730
+ // A number the cockpit asked about BEFORE the pull request existed is cached as "this
2731
+ // repository has no such number" — which is exactly what a `CEZ:PR=901` marker declared
2732
+ // ahead of the push looks like. It exists now.
2733
+ const createdNumber = refNumberFromUrl(outcome.url);
2734
+ if (createdNumber !== null)
2735
+ forgetRefStatus(repoRoot, createdNumber);
2736
+ }
2737
+ return outcome;
2738
+ },
2739
+ // Null covers everything from "no PR yet" to "gh missing" — the callers
2740
+ // (Create PR → View PR flip) treat all of it as "nothing to link".
2741
+ prStatus: async (branch) => {
2742
+ if (productEnv('DRY_RUN') === '1')
2743
+ return null;
2744
+ try {
2745
+ const out = await gh(repoRoot, ['pr', 'view', branch, '--json', 'number,url,state,isDraft,statusCheckRollup,mergedAt']);
2746
+ const pr = ghPrViewSchema.parse(JSON.parse(out));
2747
+ if (repoRef)
2748
+ mergedPullRequestObservations.observe({ ...repoRef, number: pr.number }, pr);
2749
+ return {
2750
+ number: pr.number,
2751
+ url: pr.url,
2752
+ state: GH_PR_STATES[pr.state.toUpperCase()] ?? 'open',
2753
+ isDraft: pr.isDraft,
2754
+ checks: rollupToChecks(pr.statusCheckRollup) ?? null,
2755
+ };
2756
+ }
2757
+ catch {
2758
+ return null;
2759
+ }
2760
+ },
2761
+ prMergeState: (number, opts) => fetchPrMergeState(repoRoot, repoRef, number, opts?.refresh),
2762
+ readyPR: (number, input) => readyPullRequest(repoRoot, repoRef, number, input),
2763
+ mergePR: async (number, input) => {
2764
+ const result = await mergePullRequest(repoRoot, repoRef, number, input);
2765
+ if (result.merged) {
2766
+ // We just changed this pull request, so what the ref-status cache holds about it is now
2767
+ // known-stale — and its TTL would keep every chip showing the PRE-merge status for up to
2768
+ // a minute after the user watched this server merge it. Forget it; the next reader asks
2769
+ // the forge, gets `merged`, and then stops polling it at all.
2770
+ forgetRefStatus(repoRoot, number);
2771
+ forgetGithubItem(repoRoot, 'pr', number);
2772
+ }
2773
+ return result;
2774
+ },
2775
+ viewUrl: (kind, ref) => {
2776
+ if (!repoRef)
2777
+ return null;
2778
+ const base = `https://github.com/${repoRef.owner}/${repoRef.repo}`;
2779
+ // Branch names may contain '/' — encode per segment, keep the slashes.
2780
+ const path = String(ref).split('/').map(encodeURIComponent).join('/');
2781
+ switch (kind) {
2782
+ case 'repo':
2783
+ return base;
2784
+ case 'issue':
2785
+ return `${base}/issues/${path}`;
2786
+ case 'pr':
2787
+ return `${base}/pull/${path}`;
2788
+ case 'branch':
2789
+ return `${base}/tree/${path}`;
2790
+ case 'commit':
2791
+ return `${base}/commit/${path}`;
2792
+ }
2793
+ },
2794
+ };
2795
+ }
2796
+ /** A missing alias must not poison unrelated families. Discard partial data and requery only
2797
+ * unaffected aliases once; archive authority still comes from an error-free response. */
2798
+ async function mergedPullRequestChunk(runGraphql, initial) {
2799
+ let chunk = initial;
2800
+ for (let attempt = 0; attempt < 2 && chunk.length; attempt++) {
2801
+ const query = `query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { nameWithOwner ${chunk.map(pr => `pr${pr.number}: pullRequest(number: ${pr.number}) { number url state mergedAt }`).join(' ')} } }`;
2802
+ let response;
2803
+ let failure;
2804
+ try {
2805
+ response = JSON.parse(await runGraphql(query, { owner: chunk[0].owner, name: chunk[0].repo }));
2806
+ }
2807
+ catch (error) {
2808
+ // gh exits 1 on GraphQL errors, with the structured envelope on stdout. Neither stderr
2809
+ // nor timeout/abort output is evidence that one particular alias was missing.
2810
+ const rejected = z.object({ code: z.literal(1), stdout: z.string() }).safeParse(error);
2811
+ if (!rejected.success)
2812
+ throw error;
2813
+ try {
2814
+ response = JSON.parse(rejected.data.stdout);
2815
+ }
2816
+ catch {
2817
+ throw error;
2818
+ }
2819
+ failure = error;
2820
+ }
2821
+ const partial = z.object({
2822
+ errors: z.array(z.object({ type: z.literal('NOT_FOUND'), path: z.tuple([z.literal('repository'), z.string()]) })).nonempty(),
2823
+ data: z.object({ repository: z.object({ nameWithOwner: z.string() }) }),
2824
+ }).safeParse(response);
2825
+ const aliases = new Set(chunk.map(pr => `pr${pr.number}`));
2826
+ if (attempt === 0 && partial.success &&
2827
+ partial.data.data.repository.nameWithOwner.toLowerCase() === `${chunk[0].owner}/${chunk[0].repo}`.toLowerCase() &&
2828
+ partial.data.errors.every(error => aliases.has(error.path[1]))) {
2829
+ const missing = new Set(partial.data.errors.map(error => error.path[1]));
2830
+ chunk = chunk.filter(pr => !missing.has(`pr${pr.number}`));
2831
+ continue;
2832
+ }
2833
+ if (failure)
2834
+ throw failure;
2835
+ return response;
2836
+ }
2837
+ return undefined;
2838
+ }
2839
+ /** Strict archive authority: no partial GraphQL success, redirects, guessed identity or date. */
2840
+ export async function fetchMergedPullRequests(runGraphql, identities) {
2841
+ const repositories = new Map();
2842
+ for (const identity of new Map(identities.map(pr => [pullRequestKey(pr), pr])).values()) {
2843
+ const key = `${identity.owner}/${identity.repo}`;
2844
+ repositories.set(key, [...(repositories.get(key) ?? []), identity]);
2845
+ }
2846
+ const merged = [];
2847
+ for (const [repository, prs] of repositories) {
2848
+ for (let offset = 0; offset < prs.length; offset += 25) {
2849
+ const chunk = prs.slice(offset, offset + 25);
2850
+ const response = await mergedPullRequestChunk(runGraphql, chunk);
2851
+ const parsed = z.object({ errors: z.array(z.unknown()).optional(), data: z.object({ repository: z.object({ nameWithOwner: z.string() }).passthrough().nullable() }) }).safeParse(response);
2852
+ if (!parsed.success || parsed.data.errors?.length || parsed.data.data.repository?.nameWithOwner.toLowerCase() !== repository.toLowerCase())
2853
+ continue;
2854
+ for (const pr of chunk) {
2855
+ const fact = mergedPullRequestObservations.observe(pr, parsed.data.data.repository[`pr${pr.number}`]);
2856
+ if (fact)
2857
+ merged.push(fact);
2858
+ }
2859
+ }
2860
+ }
2861
+ return merged;
2862
+ }
2863
+ export async function lookupMergedPullRequests(repoRoot, identities, signal) {
2864
+ signal.throwIfAborted();
2865
+ if (productEnv('DRY_RUN') === '1')
2866
+ return [];
2867
+ const unique = [...new Map(identities.map(pr => [pullRequestKey(pr), pr])).values()];
2868
+ const known = unique.flatMap(pr => mergedPullRequestObservations.get(pr) ?? []);
2869
+ const missing = unique.filter(pr => !mergedPullRequestObservations.get(pr));
2870
+ if (missing.length === 0)
2871
+ return known;
2872
+ const context = githubContext(repoRoot);
2873
+ const fetched = await fetchMergedPullRequests(async (query, variables) => {
2874
+ const args = ['api', 'graphql', '--hostname', 'github.com', '-f', `query=${query}`];
2875
+ for (const [key, value] of Object.entries(variables))
2876
+ args.push('-f', `${key}=${value}`);
2877
+ return gh(repoRoot, args, 15_000, signal, context);
2878
+ }, missing);
2879
+ return [...known, ...fetched];
2880
+ }
2881
+ //# sourceMappingURL=github.js.map