@atolis-hq/wake 0.2.97 → 0.3.1

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 (443) hide show
  1. package/README.md +5 -5
  2. package/dist/src/activities/agent/agent-activity-definition.js +26 -0
  3. package/dist/src/activities/agent/agent-activity.js +171 -0
  4. package/dist/src/activities/agent/agent-result.js +149 -0
  5. package/dist/src/activities/contracts/activity.js +10 -0
  6. package/dist/src/activities/contracts/config.js +2 -0
  7. package/dist/src/activities/contracts/event-schema.js +302 -0
  8. package/dist/src/activities/contracts/events.js +42 -0
  9. package/dist/src/activities/contracts/identifiers.js +29 -0
  10. package/dist/src/activities/contracts/registry.js +71 -0
  11. package/dist/src/activities/contracts/streams.js +17 -0
  12. package/dist/src/activities/contracts/vocabulary.js +58 -0
  13. package/dist/src/activities/index.js +21 -0
  14. package/dist/src/activities/pr/activity-support.js +111 -0
  15. package/dist/src/activities/pr/application.js +158 -0
  16. package/dist/src/activities/pr/approve.js +85 -0
  17. package/dist/src/activities/pr/capability.js +11 -0
  18. package/dist/src/activities/pr/decision-claim.js +76 -0
  19. package/dist/src/activities/pr/event-drafts.js +66 -0
  20. package/dist/src/activities/pr/intent.js +37 -0
  21. package/dist/src/activities/pr/merge-authority-gate.js +14 -0
  22. package/dist/src/activities/pr/merge.js +110 -0
  23. package/dist/src/activities/pr/policy.js +135 -0
  24. package/dist/src/activities/pr/projection.js +130 -0
  25. package/dist/src/activities/pr/vocabulary.js +30 -0
  26. package/dist/src/activities/review/authorization.js +22 -0
  27. package/dist/src/activities/review/contracts.js +22 -0
  28. package/dist/src/activities/review/signals.js +3 -0
  29. package/dist/src/bootstrap/analytics-projection.js +26 -0
  30. package/dist/src/bootstrap/board-projection.js +337 -0
  31. package/dist/src/bootstrap/composition-root.js +382 -0
  32. package/dist/src/bootstrap/config/load-config.js +51 -0
  33. package/dist/src/bootstrap/config/root-schema.js +134 -0
  34. package/dist/src/bootstrap/external-ref.js +12 -0
  35. package/dist/src/bootstrap/fake-provider-files.js +44 -0
  36. package/dist/src/bootstrap/fake-scenarios.js +22 -0
  37. package/dist/src/bootstrap/index.js +48 -0
  38. package/dist/src/bootstrap/initialise.js +436 -0
  39. package/dist/src/bootstrap/paths.js +15 -0
  40. package/dist/src/bootstrap/projection-runtime.js +27 -0
  41. package/dist/src/bootstrap/runner-quota-reporter.js +21 -0
  42. package/dist/src/bootstrap/runner-registry.js +30 -0
  43. package/dist/src/bootstrap/schedule-checkpoint-store.js +49 -0
  44. package/dist/src/bootstrap/self-update-application.js +195 -0
  45. package/dist/src/bootstrap/self-update-failure-log.js +33 -0
  46. package/dist/src/bootstrap/source-update-port.js +47 -0
  47. package/dist/src/bootstrap/status-publish-activity.js +42 -0
  48. package/dist/src/bootstrap/surface-api-applications.js +325 -0
  49. package/dist/src/bootstrap/surface-api-execution-applications.js +95 -0
  50. package/dist/src/bootstrap/surface-api-metadata.js +12 -0
  51. package/dist/src/bootstrap/surface-api-projection-pages.js +22 -0
  52. package/dist/src/bootstrap/surface-api-run-context.js +5 -0
  53. package/dist/src/bootstrap/surface-api-transcripts.js +57 -0
  54. package/dist/src/bootstrap/surface-api-work-applications.js +199 -0
  55. package/dist/src/bootstrap/surface-applications.js +7 -0
  56. package/dist/src/bootstrap/surface-cli-applications.js +701 -0
  57. package/dist/src/bootstrap/update-ledger.js +61 -0
  58. package/dist/src/bootstrap/update-maintenance-lease.js +176 -0
  59. package/dist/src/{version.js → bootstrap/version.js} +35 -51
  60. package/dist/src/control-plane/application/advance-once.js +184 -0
  61. package/dist/src/control-plane/application/control-plane-projection.js +42 -0
  62. package/dist/src/control-plane/application/control-plane-service.js +42 -0
  63. package/dist/src/control-plane/application/intake-pipeline.js +29 -0
  64. package/dist/src/control-plane/application/runner-control-service.js +66 -0
  65. package/dist/src/control-plane/application/runner-pipeline.js +57 -0
  66. package/dist/src/control-plane/application/schedule-service.js +47 -0
  67. package/dist/src/control-plane/application/work-cancellation-policy.js +31 -0
  68. package/dist/src/control-plane/contracts/commands.js +10 -0
  69. package/dist/src/control-plane/contracts/config.js +22 -0
  70. package/dist/src/control-plane/contracts/events.js +81 -0
  71. package/dist/src/control-plane/contracts/streams.js +5 -0
  72. package/dist/src/control-plane/domain/dispatch-policy.js +15 -0
  73. package/dist/src/control-plane/domain/quota-policy.js +26 -0
  74. package/dist/src/control-plane/domain/runner-quota.js +35 -0
  75. package/dist/src/control-plane/domain/schedule-policy.js +46 -0
  76. package/dist/src/control-plane/index.js +20 -0
  77. package/dist/src/control-plane/infrastructure/intake-host.js +24 -0
  78. package/dist/src/control-plane/infrastructure/resident-host.js +45 -0
  79. package/dist/src/control-plane/infrastructure/tick-host.js +35 -0
  80. package/dist/src/execution/application/activation-claim.js +50 -0
  81. package/dist/src/execution/application/active-run-cancellation.js +14 -0
  82. package/dist/src/execution/application/execution-activity.js +97 -0
  83. package/dist/src/execution/application/execution-projection.js +17 -0
  84. package/dist/src/execution/application/execution-service.js +372 -0
  85. package/dist/src/execution/application/execution-validation.js +26 -0
  86. package/dist/src/execution/application/recovery-service.js +212 -0
  87. package/dist/src/execution/application/run-lifecycle.js +115 -0
  88. package/dist/src/execution/application/run-liveness-service.js +80 -0
  89. package/dist/src/execution/application/run-repository.js +27 -0
  90. package/dist/src/execution/contracts/commands.js +1 -0
  91. package/dist/src/execution/contracts/config.js +35 -0
  92. package/dist/src/execution/contracts/control-plane.js +1 -0
  93. package/dist/src/execution/contracts/event-factory.js +53 -0
  94. package/dist/src/execution/contracts/events.js +274 -0
  95. package/dist/src/execution/contracts/identifiers.js +5 -0
  96. package/dist/src/execution/contracts/liveness.js +1 -0
  97. package/dist/src/execution/contracts/results.js +1 -0
  98. package/dist/src/execution/contracts/runner.js +15 -0
  99. package/dist/src/execution/contracts/streams.js +10 -0
  100. package/dist/src/execution/contracts/views.js +1 -0
  101. package/dist/src/execution/contracts/vocabulary.js +43 -0
  102. package/dist/src/execution/contracts/workspace.js +1 -0
  103. package/dist/src/execution/domain/run-result.js +34 -0
  104. package/dist/src/execution/domain/run.js +142 -0
  105. package/dist/src/execution/index.js +31 -0
  106. package/dist/src/execution/infrastructure/agent-runner-adapter.js +114 -0
  107. package/dist/src/execution/infrastructure/process-execution.js +29 -0
  108. package/dist/src/execution/infrastructure/prompt-templates.js +42 -0
  109. package/dist/src/execution/infrastructure/runners/claude.js +120 -0
  110. package/dist/src/execution/infrastructure/runners/codex.js +143 -0
  111. package/dist/src/execution/infrastructure/runners/command.js +4 -0
  112. package/dist/src/execution/infrastructure/runners/cursor.js +69 -0
  113. package/dist/src/execution/infrastructure/runners/fake-scenarios.js +99 -0
  114. package/dist/src/execution/infrastructure/runners/fake.js +65 -0
  115. package/dist/src/execution/infrastructure/runners/registry.js +26 -0
  116. package/dist/src/execution/infrastructure/transcript-store.js +223 -0
  117. package/dist/src/execution/infrastructure/transcripts.js +11 -0
  118. package/dist/src/execution/infrastructure/workspace/fake-workspace.js +19 -0
  119. package/dist/src/execution/infrastructure/workspace/git-workspace.js +241 -0
  120. package/dist/src/integrations/application/agent-run-publication-reactor.js +111 -0
  121. package/dist/src/integrations/application/artifact-registration-reactor.js +159 -0
  122. package/dist/src/integrations/application/poll-service.js +25 -0
  123. package/dist/src/integrations/application/terminal-agent-run-report.js +40 -0
  124. package/dist/src/integrations/application/work-admission.js +31 -0
  125. package/dist/src/integrations/application/work-conclusion.js +19 -0
  126. package/dist/src/integrations/contracts/artifact-events.js +60 -0
  127. package/dist/src/integrations/contracts/artifact-vocabulary.js +9 -0
  128. package/dist/src/integrations/contracts/config.js +14 -0
  129. package/dist/src/integrations/contracts/identifiers.js +5 -0
  130. package/dist/src/integrations/contracts/intake-rules.js +14 -0
  131. package/dist/src/integrations/contracts/intake.js +1 -0
  132. package/dist/src/integrations/contracts/outcome-vocabulary.js +7 -0
  133. package/dist/src/integrations/contracts/provider.js +37 -0
  134. package/dist/src/integrations/contracts/streams.js +10 -0
  135. package/dist/src/integrations/delivery/application/delivery-outcome-reactor.js +75 -0
  136. package/dist/src/integrations/delivery/application/delivery-projector.js +165 -0
  137. package/dist/src/integrations/delivery/application/delivery-service.js +173 -0
  138. package/dist/src/integrations/delivery/contracts/commands.js +1 -0
  139. package/dist/src/integrations/delivery/contracts/config.js +1 -0
  140. package/dist/src/integrations/delivery/contracts/event-factory.js +18 -0
  141. package/dist/src/integrations/delivery/contracts/events.js +86 -0
  142. package/dist/src/integrations/delivery/contracts/intents.js +89 -0
  143. package/dist/src/integrations/delivery/contracts/views.js +1 -0
  144. package/dist/src/integrations/delivery/contracts/vocabulary.js +22 -0
  145. package/dist/src/integrations/fake/durable-delivery-provider.js +48 -0
  146. package/dist/src/integrations/fake/external-sink.js +9 -0
  147. package/dist/src/integrations/fake/external-source.js +44 -0
  148. package/dist/src/integrations/fake/inbound-translator.js +130 -0
  149. package/dist/src/integrations/fake/provider.js +95 -0
  150. package/dist/src/integrations/github/application/agent-context-reader.js +54 -0
  151. package/dist/src/integrations/github/application/agent-run-comment.js +94 -0
  152. package/dist/src/integrations/github/application/comment-history-reader.js +43 -0
  153. package/dist/src/integrations/github/application/inbound-context.js +10 -0
  154. package/dist/src/integrations/github/application/inbound-review-signals.js +159 -0
  155. package/dist/src/integrations/github/application/inbound-translator.js +231 -0
  156. package/dist/src/integrations/github/application/inbound-watch-gate-signals.js +84 -0
  157. package/dist/src/integrations/github/application/intake-policy.js +23 -0
  158. package/dist/src/integrations/github/application/outbound-translator.js +47 -0
  159. package/dist/src/integrations/github/application/poll-service.js +29 -0
  160. package/dist/src/integrations/github/application/pull-request-translation.js +10 -0
  161. package/dist/src/integrations/github/application/resource-links.js +8 -0
  162. package/dist/src/integrations/github/application/review-command-translator.js +16 -0
  163. package/dist/src/integrations/github/application/wake-labels.js +95 -0
  164. package/dist/src/integrations/github/contracts/config.js +47 -0
  165. package/dist/src/integrations/github/contracts/events.js +127 -0
  166. package/dist/src/integrations/github/contracts/external-key.js +19 -0
  167. package/dist/src/integrations/github/contracts/payloads.js +9 -0
  168. package/dist/src/integrations/github/contracts/vocabulary.js +55 -0
  169. package/dist/src/integrations/github/index.js +31 -0
  170. package/dist/src/integrations/github/infrastructure/client-reads.js +214 -0
  171. package/dist/src/integrations/github/infrastructure/client.js +132 -0
  172. package/dist/src/integrations/github/infrastructure/content-fingerprint.js +10 -0
  173. package/dist/src/integrations/github/infrastructure/delivery.js +31 -0
  174. package/dist/src/integrations/github/infrastructure/etag-cache.js +56 -0
  175. package/dist/src/integrations/github/infrastructure/gh-auth.js +19 -0
  176. package/dist/src/integrations/github/infrastructure/issue-source.js +109 -0
  177. package/dist/src/integrations/github/infrastructure/pr-source.js +186 -0
  178. package/dist/src/integrations/github/infrastructure/review-source.js +70 -0
  179. package/dist/src/integrations/github/infrastructure/source.js +108 -0
  180. package/dist/src/integrations/github/provider.js +96 -0
  181. package/dist/src/integrations/index.js +30 -0
  182. package/dist/src/kernel/contracts/checkpoint-store.js +1 -0
  183. package/dist/src/kernel/contracts/clock.js +1 -0
  184. package/dist/src/kernel/contracts/commands.js +1 -0
  185. package/dist/src/kernel/contracts/event-journal.js +2 -0
  186. package/dist/src/kernel/contracts/event-schema.js +54 -0
  187. package/dist/src/kernel/contracts/events.js +11 -0
  188. package/dist/src/kernel/contracts/id-generator.js +1 -0
  189. package/dist/src/kernel/contracts/identifiers.js +8 -0
  190. package/dist/src/kernel/contracts/projection-store.js +1 -0
  191. package/dist/src/kernel/contracts/relations.js +1 -0
  192. package/dist/src/kernel/contracts/schema.js +17 -0
  193. package/dist/src/kernel/contracts/vocabulary.js +6 -0
  194. package/dist/src/kernel/domain/event-envelope.js +29 -0
  195. package/dist/src/kernel/domain/match-mode.js +11 -0
  196. package/dist/src/kernel/domain/relation.js +10 -0
  197. package/dist/src/kernel/index.js +17 -0
  198. package/dist/src/{lib/clock.js → kernel/infrastructure/system-clock.js} +3 -3
  199. package/dist/src/kernel/infrastructure/ulid-id-generator.js +9 -0
  200. package/dist/src/main.js +137 -1030
  201. package/dist/src/orchestration/application/accept-activity-outcome.js +25 -0
  202. package/dist/src/orchestration/application/accept-signal.js +35 -0
  203. package/dist/src/orchestration/application/advance-workflow.js +160 -0
  204. package/dist/src/orchestration/application/coordination-claims.js +107 -0
  205. package/dist/src/orchestration/application/group-budget-recorder.js +43 -0
  206. package/dist/src/orchestration/application/orchestration-projection.js +20 -0
  207. package/dist/src/orchestration/application/orchestration-repository.js +36 -0
  208. package/dist/src/orchestration/application/orchestration-service.js +82 -0
  209. package/dist/src/orchestration/application/request-child.js +118 -0
  210. package/dist/src/orchestration/application/signal-reactor.js +17 -0
  211. package/dist/src/orchestration/application/start-workflow.js +51 -0
  212. package/dist/src/orchestration/application/watch-reactor.js +82 -0
  213. package/dist/src/orchestration/contracts/activity-outcome.js +18 -0
  214. package/dist/src/orchestration/contracts/commands.js +1 -0
  215. package/dist/src/orchestration/contracts/config.js +128 -0
  216. package/dist/src/orchestration/contracts/event-decoder.js +263 -0
  217. package/dist/src/orchestration/contracts/events.js +41 -0
  218. package/dist/src/orchestration/contracts/identifiers.js +35 -0
  219. package/dist/src/orchestration/contracts/streams.js +38 -0
  220. package/dist/src/orchestration/contracts/views.js +1 -0
  221. package/dist/src/orchestration/contracts/vocabulary.js +30 -0
  222. package/dist/src/orchestration/domain/activation-policy.js +55 -0
  223. package/dist/src/orchestration/domain/approval-defaults.js +17 -0
  224. package/dist/src/orchestration/domain/child-policy.js +63 -0
  225. package/dist/src/orchestration/domain/compiler.js +162 -0
  226. package/dist/src/orchestration/domain/coordination-events.js +25 -0
  227. package/dist/src/orchestration/domain/decision-events.js +47 -0
  228. package/dist/src/orchestration/domain/interpreter.js +49 -0
  229. package/dist/src/orchestration/domain/operator-retry-policy.js +34 -0
  230. package/dist/src/orchestration/domain/retry-policy.js +26 -0
  231. package/dist/src/orchestration/domain/signal-policy.js +120 -0
  232. package/dist/src/orchestration/domain/supplemental-policy.js +61 -0
  233. package/dist/src/orchestration/domain/transition.js +55 -0
  234. package/dist/src/orchestration/domain/workflow-graph.js +41 -0
  235. package/dist/src/orchestration/domain/workflow-instance-events.js +236 -0
  236. package/dist/src/orchestration/domain/workflow-instance.js +41 -0
  237. package/dist/src/orchestration/domain/workflow-selector.js +21 -0
  238. package/dist/src/orchestration/index.js +19 -0
  239. package/dist/src/persistence/application/projection-runner.js +79 -0
  240. package/dist/src/persistence/filesystem/file-checkpoint-store.js +35 -0
  241. package/dist/src/persistence/filesystem/file-event-journal.js +143 -0
  242. package/dist/src/persistence/filesystem/file-lock.js +91 -0
  243. package/dist/src/persistence/filesystem/file-projection-store.js +64 -0
  244. package/dist/src/persistence/index.js +8 -0
  245. package/dist/src/persistence/memory/in-memory-checkpoint-store.js +14 -0
  246. package/dist/src/persistence/memory/in-memory-event-journal.js +88 -0
  247. package/dist/src/persistence/memory/in-memory-projection-store.js +17 -0
  248. package/dist/src/resources/application/lookup-projections.js +48 -0
  249. package/dist/src/resources/application/resource-lookup.js +16 -0
  250. package/dist/src/resources/application/resource-projections.js +81 -0
  251. package/dist/src/resources/application/resource-repository.js +18 -0
  252. package/dist/src/resources/application/resource-service.js +83 -0
  253. package/dist/src/resources/contracts/commands.js +1 -0
  254. package/dist/src/resources/contracts/config.js +2 -0
  255. package/dist/src/resources/contracts/events.js +83 -0
  256. package/dist/src/resources/contracts/identifiers.js +15 -0
  257. package/dist/src/resources/contracts/streams.js +6 -0
  258. package/dist/src/resources/contracts/views.js +1 -0
  259. package/dist/src/resources/contracts/vocabulary.js +67 -0
  260. package/dist/src/resources/domain/correlation.js +8 -0
  261. package/dist/src/resources/domain/resource.js +58 -0
  262. package/dist/src/resources/index.js +14 -0
  263. package/dist/src/surfaces/api/contracts/activities.js +1 -0
  264. package/dist/src/surfaces/api/contracts/board.js +2 -0
  265. package/dist/src/surfaces/api/contracts/common.js +46 -0
  266. package/dist/src/surfaces/api/contracts/control-plane.js +5 -0
  267. package/dist/src/surfaces/api/contracts/events.js +1 -0
  268. package/dist/src/surfaces/api/contracts/execution.js +1 -0
  269. package/dist/src/surfaces/api/contracts/index.js +13 -0
  270. package/dist/src/surfaces/api/contracts/observability.js +1 -0
  271. package/dist/src/surfaces/api/contracts/orchestration.js +1 -0
  272. package/dist/src/surfaces/api/contracts/resources.js +1 -0
  273. package/dist/src/surfaces/api/contracts/status.js +1 -0
  274. package/dist/src/surfaces/api/contracts/system.js +1 -0
  275. package/dist/src/surfaces/api/contracts/transport-values.js +39 -0
  276. package/dist/src/surfaces/api/contracts/work.js +17 -0
  277. package/dist/src/surfaces/api/http-server.js +121 -0
  278. package/dist/src/surfaces/api/presenters/activities.js +1 -0
  279. package/dist/src/surfaces/api/presenters/board.js +3 -0
  280. package/dist/src/surfaces/api/presenters/control-plane.js +1 -0
  281. package/dist/src/surfaces/api/presenters/events.js +1 -0
  282. package/dist/src/surfaces/api/presenters/execution.js +59 -0
  283. package/dist/src/surfaces/api/presenters/observability.js +1 -0
  284. package/dist/src/surfaces/api/presenters/orchestration.js +19 -0
  285. package/dist/src/surfaces/api/presenters/resources.js +15 -0
  286. package/dist/src/surfaces/api/presenters/status.js +3 -0
  287. package/dist/src/surfaces/api/presenters/system.js +14 -0
  288. package/dist/src/surfaces/api/presenters/work.js +16 -0
  289. package/dist/src/surfaces/api/problem-details.js +9 -0
  290. package/dist/src/surfaces/api/router.js +13 -0
  291. package/dist/src/surfaces/api/routes/activities.js +1 -0
  292. package/dist/src/surfaces/api/routes/applications.js +1 -0
  293. package/dist/src/surfaces/api/routes/commands.js +82 -0
  294. package/dist/src/surfaces/api/routes/control-plane.js +6 -0
  295. package/dist/src/surfaces/api/routes/events.js +1 -0
  296. package/dist/src/surfaces/api/routes/execution.js +8 -0
  297. package/dist/src/surfaces/api/routes/index.js +18 -0
  298. package/dist/src/surfaces/api/routes/observability.js +1 -0
  299. package/dist/src/surfaces/api/routes/orchestration.js +1 -0
  300. package/dist/src/surfaces/api/routes/pagination.js +78 -0
  301. package/dist/src/surfaces/api/routes/read.js +135 -0
  302. package/dist/src/surfaces/api/routes/resources.js +1 -0
  303. package/dist/src/surfaces/api/routes/responses.js +67 -0
  304. package/dist/src/surfaces/api/routes/system.js +1 -0
  305. package/dist/src/surfaces/api/routes/work.js +9 -0
  306. package/dist/src/surfaces/cli/commands/api.js +1 -0
  307. package/dist/src/surfaces/cli/commands/audit.js +10 -0
  308. package/dist/src/surfaces/cli/commands/correlate.js +2 -0
  309. package/dist/src/surfaces/cli/commands/doctor.js +6 -0
  310. package/dist/src/surfaces/cli/commands/init.js +39 -0
  311. package/dist/src/surfaces/cli/commands/sandbox-entrypoint.js +33 -0
  312. package/dist/src/surfaces/cli/commands/sandbox-setup.js +29 -0
  313. package/dist/src/surfaces/cli/commands/sandbox.js +105 -0
  314. package/dist/src/surfaces/cli/commands/self-update.js +38 -0
  315. package/dist/src/surfaces/cli/commands/smoke.js +16 -0
  316. package/dist/src/surfaces/cli/commands/start.js +1 -0
  317. package/dist/src/surfaces/cli/commands/stop.js +13 -0
  318. package/dist/src/surfaces/cli/commands/tick.js +1 -0
  319. package/dist/src/surfaces/cli/commands/ui.js +2 -0
  320. package/dist/src/surfaces/cli/commands/validate-state.js +5 -0
  321. package/dist/src/surfaces/cli/infrastructure/docker-cli.js +221 -0
  322. package/dist/src/surfaces/cli/infrastructure/process-log.js +86 -0
  323. package/dist/src/surfaces/cli/main.js +140 -0
  324. package/dist/src/surfaces/cli/usage.js +33 -0
  325. package/dist/src/surfaces/contracts/config.js +34 -0
  326. package/dist/src/surfaces/index.js +47 -0
  327. package/dist/src/surfaces/web-assets/assets/index-Bfn4FwZG.css +1 -0
  328. package/dist/src/surfaces/web-assets/assets/index-ipB6JPGU.js +1090 -0
  329. package/dist/src/surfaces/web-assets/index.html +13 -0
  330. package/dist/src/surfaces/web-host/asset-source.js +1 -0
  331. package/dist/src/surfaces/web-host/packaged-assets.js +44 -0
  332. package/dist/src/work/application/work-projection.js +59 -0
  333. package/dist/src/work/application/work-repository.js +18 -0
  334. package/dist/src/work/application/work-service.js +117 -0
  335. package/dist/src/work/contracts/commands.js +1 -0
  336. package/dist/src/work/contracts/config.js +2 -0
  337. package/dist/src/work/contracts/events.js +103 -0
  338. package/dist/src/work/contracts/identifiers.js +5 -0
  339. package/dist/src/work/contracts/streams.js +6 -0
  340. package/dist/src/work/contracts/views.js +1 -0
  341. package/dist/src/work/contracts/vocabulary.js +6 -0
  342. package/dist/src/work/domain/work-item.js +89 -0
  343. package/dist/src/work/index.js +11 -0
  344. package/docker/Dockerfile +10 -6
  345. package/docker/Dockerfile.packaged +3 -3
  346. package/package.json +29 -4
  347. package/prompts/implement.md +2 -2
  348. package/dist/src/adapters/claude/claude-runner.js +0 -529
  349. package/dist/src/adapters/codex/codex-runner.js +0 -503
  350. package/dist/src/adapters/cursor/cursor-runner.js +0 -418
  351. package/dist/src/adapters/docker/docker-cli.js +0 -150
  352. package/dist/src/adapters/fake/fake-artifact-verifier.js +0 -14
  353. package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +0 -77
  354. package/dist/src/adapters/fake/fake-resource-index.js +0 -21
  355. package/dist/src/adapters/fake/fake-runner.js +0 -77
  356. package/dist/src/adapters/fake/fake-ticketing-system.js +0 -185
  357. package/dist/src/adapters/fake/fake-workspace-manager.js +0 -81
  358. package/dist/src/adapters/fs/resource-index.js +0 -126
  359. package/dist/src/adapters/fs/self-update-ledger.js +0 -17
  360. package/dist/src/adapters/fs/state-store.js +0 -775
  361. package/dist/src/adapters/git/git-workspace-manager.js +0 -289
  362. package/dist/src/adapters/github/github-artifact-verifier.js +0 -38
  363. package/dist/src/adapters/github/github-auth.js +0 -16
  364. package/dist/src/adapters/github/github-client.js +0 -249
  365. package/dist/src/adapters/github/github-etag-cache.js +0 -57
  366. package/dist/src/adapters/github/github-issues-work-source.js +0 -692
  367. package/dist/src/adapters/github/github-pull-request-activity-source.js +0 -584
  368. package/dist/src/adapters/github/github-pull-request-merge-actor.js +0 -45
  369. package/dist/src/adapters/http/ui-assets.js +0 -1188
  370. package/dist/src/adapters/http/ui-data.js +0 -1107
  371. package/dist/src/adapters/http/ui-server.js +0 -458
  372. package/dist/src/adapters/runner/cli-command.js +0 -73
  373. package/dist/src/adapters/runner/prompt-templates.js +0 -76
  374. package/dist/src/adapters/runner/runner-cli-adapter.js +0 -112
  375. package/dist/src/adapters/runner/runner-registry.js +0 -139
  376. package/dist/src/adapters/runner/runtime-events.js +0 -32
  377. package/dist/src/adapters/runner/stage-prompt.js +0 -268
  378. package/dist/src/adapters/runner/transcripts.js +0 -25
  379. package/dist/src/cli/audit-command.js +0 -36
  380. package/dist/src/cli/correlate-command.js +0 -62
  381. package/dist/src/cli/doctor-command.js +0 -48
  382. package/dist/src/cli/init-command.js +0 -18
  383. package/dist/src/cli/sandbox-command.js +0 -258
  384. package/dist/src/cli/sandbox-entrypoint-command.js +0 -88
  385. package/dist/src/cli/sandbox-exec-logging.js +0 -7
  386. package/dist/src/cli/sandbox-resume.js +0 -79
  387. package/dist/src/cli/sandbox-setup-command.js +0 -31
  388. package/dist/src/cli/scaffold-assets.js +0 -100
  389. package/dist/src/cli/self-update-command.js +0 -198
  390. package/dist/src/cli/startup-preflight.js +0 -134
  391. package/dist/src/cli/stop-command.js +0 -46
  392. package/dist/src/cli/ui-command.js +0 -27
  393. package/dist/src/config/defaults.js +0 -10
  394. package/dist/src/config/discover-config-files.js +0 -11
  395. package/dist/src/config/load-config.js +0 -69
  396. package/dist/src/config/split-config.js +0 -14
  397. package/dist/src/core/active-run-recovery.js +0 -44
  398. package/dist/src/core/approval-intents.js +0 -16
  399. package/dist/src/core/audit-events.js +0 -82
  400. package/dist/src/core/control-plane.js +0 -192
  401. package/dist/src/core/event-builders.js +0 -153
  402. package/dist/src/core/event-resolver.js +0 -249
  403. package/dist/src/core/lifecycle-service.js +0 -8
  404. package/dist/src/core/live-execution.js +0 -81
  405. package/dist/src/core/outbox.js +0 -198
  406. package/dist/src/core/policy-engine.js +0 -389
  407. package/dist/src/core/projection-updater.js +0 -693
  408. package/dist/src/core/quota-backoff.js +0 -69
  409. package/dist/src/core/run-lease.js +0 -38
  410. package/dist/src/core/scheduled-workflow-source.js +0 -199
  411. package/dist/src/core/sink-router.js +0 -107
  412. package/dist/src/core/stale-run-reconciler.js +0 -352
  413. package/dist/src/core/tick-runner.js +0 -2442
  414. package/dist/src/core/workspace-cleanup.js +0 -117
  415. package/dist/src/domain/branch-naming.js +0 -14
  416. package/dist/src/domain/custom-commands.js +0 -58
  417. package/dist/src/domain/event-types.js +0 -158
  418. package/dist/src/domain/manual-labels.js +0 -1
  419. package/dist/src/domain/resource-uri.js +0 -38
  420. package/dist/src/domain/runner-routing.js +0 -127
  421. package/dist/src/domain/runtime-events.js +0 -36
  422. package/dist/src/domain/schema.js +0 -1233
  423. package/dist/src/domain/sources.js +0 -16
  424. package/dist/src/domain/stages.js +0 -36
  425. package/dist/src/domain/work-item-labels.js +0 -48
  426. package/dist/src/domain/work-item-lifecycle.js +0 -10
  427. package/dist/src/domain/work-item-status.js +0 -33
  428. package/dist/src/domain/workflows.js +0 -159
  429. package/dist/src/lib/deep-merge.js +0 -14
  430. package/dist/src/lib/detached-process-logging.js +0 -46
  431. package/dist/src/lib/event-log.js +0 -21
  432. package/dist/src/lib/format.js +0 -41
  433. package/dist/src/lib/json-file.js +0 -23
  434. package/dist/src/lib/lock.js +0 -181
  435. package/dist/src/lib/log-rotation.js +0 -42
  436. package/dist/src/lib/paths.js +0 -52
  437. package/dist/src/lib/process-identity.js +0 -54
  438. package/dist/src/lib/state-health.js +0 -30
  439. package/dist/src/lib/work-id.js +0 -19
  440. package/dist/src/lib/yaml-file.js +0 -20
  441. package/templates/SETUP.md +0 -115
  442. /package/dist/src/{core → activities/pr}/contracts.js +0 -0
  443. /package/dist/src/{domain/types.js → control-plane/contracts/views.js} +0 -0
@@ -1,692 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { autoApprovalLabel, resolveAutoApprovalIntent } from '../../core/approval-intents.js';
3
- import { LABELS_REQUESTED_EVENT } from '../../domain/event-types.js';
4
- import { defaultAgentIdentity } from '../../domain/schema.js';
5
- import { buildResourceUri } from '../../domain/resource-uri.js';
6
- import { wakeStageLabelPrefix } from '../../domain/stages.js';
7
- import { wakeWorkflowLabelPrefix } from '../../domain/workflows.js';
8
- import { FROZEN_WORK_ITEM_LABEL } from '../../domain/work-item-lifecycle.js';
9
- import { createEventEnvelope, createUnkeyedEventEnvelope } from '../../lib/event-log.js';
10
- import { createWakePaths } from '../../lib/paths.js';
11
- import { wakeVersion } from '../../version.js';
12
- import { buildResumeCommandForCli } from '../runner/runner-cli-adapter.js';
13
- import { SCHEDULED_WORKFLOW_LABEL } from '../../domain/work-item-labels.js';
14
- const wakeStatusLabelPrefix = 'wake:status.';
15
- const pollOverlapMs = 60 * 60 * 1000;
16
- // Hidden marker appended to every comment Wake posts. `expectedEcho` normally
17
- // suppresses Wake's own comments from re-entering the projection, but if Wake
18
- // crashes after posting and before the reply-published event is processed,
19
- // expectedEcho is never updated — on restart the comment would otherwise be
20
- // polled back in as a new human comment. The marker is a second, independent
21
- // signal so bot-authored detection doesn't depend on account type or
22
- // expectedEcho bookkeeping surviving a crash (#145).
23
- const wakeCommentMarker = '<!-- wake:agent -->';
24
- const githubSource = 'github';
25
- export function wakeIdempotencyMarker(idempotencyKey) {
26
- return typeof idempotencyKey === 'string'
27
- ? `<!-- wake:idempotency ${idempotencyKey} -->`
28
- : undefined;
29
- }
30
- function normalizeTicketUpsert(input) {
31
- // Names the resource, never the work item — the resolver stamps the
32
- // canonical workItemKey after the poll (spec D1).
33
- return createUnkeyedEventEnvelope({
34
- eventId: `github-issue-${input.repo}-${input.issue.number}-${input.issue.updated_at}`,
35
- streamScope: 'global-intake',
36
- direction: 'inbound',
37
- sourceSystem: 'github',
38
- sourceEventType: 'ticket.upsert',
39
- sourceRefs: {
40
- repo: input.repo,
41
- issueNumber: input.issue.number,
42
- sourceUrl: input.issue.html_url,
43
- resourceUri: buildResourceUri(githubSource, 'issue', `${input.repo}#${input.issue.number}`),
44
- },
45
- occurredAt: input.issue.updated_at,
46
- ingestedAt: input.ingestedAt,
47
- trigger: input.expectedEcho === true ? 'context-only' : 'immediate',
48
- payload: {
49
- ticket: {
50
- repo: input.repo,
51
- number: input.issue.number,
52
- title: input.issue.title,
53
- body: input.issue.body ?? '',
54
- labels: (input.issue.labels ?? [])
55
- .map((label) => (typeof label === 'string' ? label : label.name))
56
- .filter((label) => typeof label === 'string'),
57
- assignees: (input.issue.assignees ?? [])
58
- .map((assignee) => assignee.login)
59
- .filter((login) => typeof login === 'string'),
60
- isPullRequest: input.issue.pull_request !== undefined,
61
- state: input.issue.state === 'closed' ? 'closed' : 'open',
62
- url: input.issue.html_url,
63
- createdAt: input.issue.created_at,
64
- updatedAt: input.issue.updated_at,
65
- },
66
- providerEventType: 'github.issue.upsert',
67
- },
68
- raw: {
69
- github: {
70
- issueUpdatedAt: input.issue.updated_at,
71
- },
72
- },
73
- ...(input.expectedEcho === true ? { derivedHints: { expectedEcho: true } } : {}),
74
- });
75
- }
76
- function normalizeTicketCommentEvent(input) {
77
- const isUpdate = input.existingUpdatedAt !== undefined && input.existingUpdatedAt !== input.comment.updated_at;
78
- return createUnkeyedEventEnvelope({
79
- eventId: `github-comment-${input.repo}-${input.issueNumber}-${input.comment.id}-${input.comment.updated_at}`,
80
- streamScope: 'work-item',
81
- direction: 'inbound',
82
- sourceSystem: 'github',
83
- sourceEventType: isUpdate ? 'ticket.comment.updated' : 'ticket.comment.created',
84
- sourceRefs: {
85
- repo: input.repo,
86
- issueNumber: input.issueNumber,
87
- commentId: String(input.comment.id),
88
- sourceUrl: input.comment.html_url,
89
- // The comment belongs to the issue's work item; the issue is the
90
- // resource the resolver knows it by.
91
- resourceUri: buildResourceUri(githubSource, 'issue', `${input.repo}#${input.issueNumber}`),
92
- },
93
- occurredAt: input.comment.updated_at,
94
- ingestedAt: input.ingestedAt,
95
- trigger: 'context-only',
96
- payload: {
97
- comment: {
98
- id: String(input.comment.id),
99
- body: input.comment.body ?? '',
100
- author: {
101
- login: input.comment.user?.login ?? 'unknown',
102
- },
103
- createdAt: input.comment.created_at,
104
- updatedAt: input.comment.updated_at,
105
- },
106
- providerEventType: isUpdate ? 'github.issue.comment.updated' : 'github.issue.comment.created',
107
- },
108
- derivedHints: {
109
- // Third-party bots/integrations (CI, Dependabot, Renovate, etc.) must
110
- // not be able to unblock a blocked issue; only an actual human reply should.
111
- // The marker check catches Wake's own comments even when expectedEcho
112
- // missed them (crash-recovery gap) or the agent account type is 'User'.
113
- // The selfLogin check catches a comment posted by direct API/CLI call
114
- // (e.g. a `revise` run replying via `gh api`) that never carries the
115
- // marker at all — without it, Wake's own reply looks human and
116
- // re-triggers another run against itself (#258 follow-up incident: 99
117
- // duplicate replies from exactly this gap).
118
- botAuthoredComment: input.comment.user?.type === 'Bot' ||
119
- (input.comment.body ?? '').includes(wakeCommentMarker) ||
120
- (input.selfLogin !== undefined && input.comment.user?.login === input.selfLogin),
121
- },
122
- });
123
- }
124
- function normalizeLabels(labels) {
125
- return [...labels].sort();
126
- }
127
- function labelsMatch(left, right) {
128
- const normalizedLeft = normalizeLabels(left);
129
- const normalizedRight = normalizeLabels(right);
130
- return (normalizedLeft.length === normalizedRight.length &&
131
- normalizedLeft.every((label, index) => label === normalizedRight[index]));
132
- }
133
- function issueLabels(issue) {
134
- return (issue.labels ?? [])
135
- .map((label) => (typeof label === 'string' ? label : label.name))
136
- .filter((label) => typeof label === 'string');
137
- }
138
- function issueAssignees(issue) {
139
- return (issue.assignees ?? [])
140
- .map((assignee) => assignee.login)
141
- .filter((login) => typeof login === 'string');
142
- }
143
- function issueWithAutoApprovalLabel(issue) {
144
- return {
145
- ...issue,
146
- labels: [...issueLabels(issue), autoApprovalLabel],
147
- };
148
- }
149
- async function ensureAutoApprovalLabel(input) {
150
- if (issueLabels(input.issue).includes(autoApprovalLabel)) {
151
- return false;
152
- }
153
- await input.setLabels(input.owner, input.repoName, input.issueNumber, [
154
- ...issueLabels(input.issue),
155
- autoApprovalLabel,
156
- ]);
157
- return true;
158
- }
159
- function isExpectedLabelEcho(issue, local) {
160
- if (local === null || local.wake.expectedEcho.labels.length === 0) {
161
- return false;
162
- }
163
- return (issue.title === local.issue.title &&
164
- (issue.body ?? '') === local.issue.body &&
165
- (issue.state === 'closed' ? 'closed' : 'open') === local.issue.state &&
166
- issue.html_url === local.issue.url &&
167
- labelsMatch(issueAssignees(issue), local.issue.assignees) &&
168
- labelsMatch(issueLabels(issue), local.wake.expectedEcho.labels));
169
- }
170
- function extractCreatedCommentId(response) {
171
- if (response === null || typeof response !== 'object') {
172
- return undefined;
173
- }
174
- const directId = response.id;
175
- if (typeof directId === 'number' || typeof directId === 'string') {
176
- return String(directId);
177
- }
178
- const data = response.data;
179
- if (data !== null && typeof data === 'object') {
180
- const dataId = data.id;
181
- if (typeof dataId === 'number' || typeof dataId === 'string') {
182
- return String(dataId);
183
- }
184
- }
185
- return undefined;
186
- }
187
- function formatControlPlaneLink(url) {
188
- try {
189
- const parsed = new URL(url);
190
- if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
191
- return null;
192
- }
193
- return parsed.toString();
194
- }
195
- catch {
196
- return null;
197
- }
198
- }
199
- export async function readControlPlaneUiUrl(wakeRoot) {
200
- try {
201
- const raw = await readFile(createWakePaths(wakeRoot).controlPlaneUiUrlFile, 'utf8');
202
- return formatControlPlaneLink(raw.trim()) ?? undefined;
203
- }
204
- catch {
205
- return undefined;
206
- }
207
- }
208
- function isGitHubNotFound(error) {
209
- return error instanceof Error && error.status === 404;
210
- }
211
- export function formatGitHubError(error) {
212
- if (error instanceof Error) {
213
- const octokit = error;
214
- if (octokit.status !== undefined) {
215
- const headers = octokit.response?.headers ?? {};
216
- const parts = [`status=${octokit.status}`];
217
- if (headers['x-ratelimit-remaining'] !== undefined)
218
- parts.push(`ratelimit-remaining=${headers['x-ratelimit-remaining']}`);
219
- if (headers['retry-after'] !== undefined)
220
- parts.push(`retry-after=${headers['retry-after']}`);
221
- return parts.join(' ');
222
- }
223
- return error.message.slice(0, 300);
224
- }
225
- return String(error).slice(0, 300);
226
- }
227
- export function formatWakeComment(payload, controlPlaneUrl) {
228
- const body = typeof payload.body === 'string' ? payload.body : '';
229
- const kind = typeof payload.kind === 'string' ? payload.kind : undefined;
230
- const action = typeof payload.action === 'string' ? payload.action : undefined;
231
- const runId = typeof payload.runId === 'string' ? payload.runId : undefined;
232
- const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
233
- const model = typeof payload.model === 'string' ? payload.model : undefined;
234
- const cli = typeof payload.cli === 'string' ? payload.cli : undefined;
235
- const runnerName = typeof payload.runnerName === 'string' ? payload.runnerName : undefined;
236
- const runnerTier = typeof payload.runnerTier === 'string' ? payload.runnerTier : undefined;
237
- const duration = typeof payload.duration === 'string' ? payload.duration : undefined;
238
- const tokens = typeof payload.tokens === 'string' ? payload.tokens : undefined;
239
- const cost = typeof payload.cost === 'string' ? payload.cost : undefined;
240
- const workspacePath = typeof payload.workspacePath === 'string' ? payload.workspacePath : undefined;
241
- const idempotencyMarker = wakeIdempotencyMarker(payload.idempotencyKey);
242
- const outcomeLine = formatOutcomeLine(payload);
243
- const details = [
244
- action === undefined ? undefined : `stage \`${action}\``,
245
- runnerName === undefined ? undefined : `runner \`${runnerName}\``,
246
- runnerTier === undefined ? undefined : `tier \`${runnerTier}\``,
247
- cli === undefined ? undefined : `cli ${cli}`,
248
- model === undefined ? undefined : `model \`${model}\``,
249
- duration === undefined ? undefined : `duration ${duration}`,
250
- tokens === undefined ? undefined : `tokens ${tokens}`,
251
- cost === undefined ? undefined : `cost ${cost}`,
252
- runId === undefined ? undefined : `run \`${runId}\``,
253
- ].filter((part) => part !== undefined);
254
- const name = controlPlaneUrl === undefined
255
- ? defaultAgentIdentity
256
- : `[${defaultAgentIdentity}](${controlPlaneUrl})`;
257
- const header = `**${name}** _(Wake ${wakeVersion}${details.length > 0 ? ` · ${details.join(' · ')}` : ''})_`;
258
- const sections = [wakeCommentMarker, idempotencyMarker, header, outcomeLine, body].filter((section) => section !== undefined);
259
- if (kind === 'approval-request') {
260
- sections.push('_To approve this work, reply with `/approved`. To request changes, reply with `/changes` followed by your feedback. To ask a question without requesting changes, reply with `/ask` followed by your question._');
261
- }
262
- if (kind === 'question') {
263
- sections.push('_Reply on this thread to continue — any reply will resume Wake with your response. To request changes to the overall approach instead, reply with `/changes` followed by your feedback._');
264
- }
265
- if (sessionId !== undefined) {
266
- const resumeCommandArgs = cli === undefined
267
- ? null
268
- : buildResumeCommandForCli({
269
- cli,
270
- sessionId,
271
- });
272
- const resumeCommandText = cli === undefined
273
- ? `<resume command unavailable: missing runner identity for session ${sessionId}>`
274
- : resumeCommandArgs === null
275
- ? `<resume command unavailable: unsupported runner identity for session ${sessionId}>`
276
- : resumeCommandArgs.join(' ');
277
- const resumeCommand = workspacePath === undefined
278
- ? resumeCommandText
279
- : `cd "${workspacePath}"\n${resumeCommandText}`;
280
- sections.push([
281
- '---',
282
- `_Next steps: reply on this thread to continue, or resume this exact ${defaultAgentIdentity} session locally:_`,
283
- '```',
284
- resumeCommand,
285
- '```',
286
- ].join('\n'));
287
- }
288
- return sections.join('\n\n');
289
- }
290
- function formatOutcomeLine(payload) {
291
- const sentinel = typeof payload.sentinel === 'string' ? payload.sentinel : undefined;
292
- const kind = typeof payload.kind === 'string' ? payload.kind : undefined;
293
- if (kind === 'approval-request') {
294
- return '**Outcome:** 🟡 Awaiting Approval';
295
- }
296
- if (sentinel === 'DONE') {
297
- return '**Outcome:** ✅ Done';
298
- }
299
- if (sentinel === 'REJECTED') {
300
- return '**Outcome:** 🔴 Changes Requested';
301
- }
302
- if (sentinel === 'BLOCKED' || kind === 'question') {
303
- return '**Outcome:** 🟠 Blocked';
304
- }
305
- if (sentinel === 'FAILED' || kind === 'failure') {
306
- return '**Outcome:** ❌ Failed';
307
- }
308
- return undefined;
309
- }
310
- function createIssueCommentPublishedEvent(input) {
311
- return createEventEnvelope({
312
- eventId: `${input.event.eventId}-published`,
313
- workItemKey: input.event.workItemKey,
314
- streamScope: 'work-item',
315
- direction: 'outbound',
316
- sourceSystem: 'github',
317
- sourceEventType: 'ticket.reply.published',
318
- sourceRefs: {
319
- repo: input.repo,
320
- issueNumber: input.issueNumber,
321
- ...(input.commentId === undefined ? {} : { commentId: input.commentId }),
322
- },
323
- occurredAt: input.publishedAt,
324
- ingestedAt: input.publishedAt,
325
- trigger: 'context-only',
326
- payload: {
327
- intentEventId: input.event.eventId,
328
- idempotencyKey: input.event.payload.idempotencyKey,
329
- deliveryState: 'CONFIRMED',
330
- kind: input.event.payload.kind,
331
- body: input.event.payload.body,
332
- providerEventType: 'github.issue.comment.published',
333
- ...(input.commentId === undefined ? {} : { providerId: input.commentId }),
334
- },
335
- });
336
- }
337
- export function createGitHubIssuesWorkSource(deps) {
338
- /** O(1): one shard read, then a direct projection read by work id. */
339
- async function readProjectionForIssue(repo, issueNumber) {
340
- const uri = buildResourceUri(githubSource, 'issue', `${repo}#${issueNumber}`);
341
- const workItemKey = await deps.resourceIndex.resolve(uri);
342
- if (workItemKey === undefined) {
343
- return null;
344
- }
345
- return deps.stateStore.readIssueState(workItemKey);
346
- }
347
- return {
348
- async refreshForDispatch(input) {
349
- const repoRef = input.projection.issue.repo;
350
- if (deps.client.getIssue === undefined) {
351
- return null;
352
- }
353
- if (!deps.config.sources.github.repos.includes(repoRef)) {
354
- return null;
355
- }
356
- const [owner, repo] = repoRef.split('/');
357
- if (owner === undefined || repo === undefined) {
358
- return null;
359
- }
360
- const ingestedAt = deps.now().toISOString();
361
- let issue;
362
- try {
363
- issue = await deps.client.getIssue(owner, repo, input.projection.issue.number);
364
- }
365
- catch (error) {
366
- if (isGitHubNotFound(error)) {
367
- return {
368
- events: [],
369
- sourceRevision: `github:issue:${repoRef}#${input.projection.issue.number}@missing`,
370
- sourceExists: false,
371
- };
372
- }
373
- throw error;
374
- }
375
- const events = [];
376
- if (input.projection.issue.updatedAt !== issue.updated_at) {
377
- events.push(normalizeTicketUpsert({
378
- repo: repoRef,
379
- issue,
380
- ingestedAt,
381
- expectedEcho: isExpectedLabelEcho(issue, input.projection),
382
- }));
383
- }
384
- const comments = await deps.client.listComments(owner, repo, input.projection.issue.number, deps.config.sources.github.polling.commentPageSize);
385
- let latestCommentRevision = '';
386
- let autoApprovalLabelAdded = false;
387
- for (const comment of comments) {
388
- if (comment.updated_at > latestCommentRevision) {
389
- latestCommentRevision = comment.updated_at;
390
- }
391
- const known = input.projection.comments.find((entry) => entry.id === String(comment.id));
392
- if (known?.updatedAt === comment.updated_at) {
393
- continue;
394
- }
395
- if (input.projection.wake.expectedEcho.commentIds.includes(String(comment.id))) {
396
- continue;
397
- }
398
- if (!autoApprovalLabelAdded &&
399
- resolveAutoApprovalIntent(comment.body) !== null &&
400
- comment.user?.type !== 'Bot' &&
401
- !(comment.body ?? '').includes(wakeCommentMarker) &&
402
- (deps.selfLogin === undefined || comment.user?.login !== deps.selfLogin)) {
403
- autoApprovalLabelAdded = await ensureAutoApprovalLabel({
404
- owner,
405
- repoName: repo,
406
- issueNumber: input.projection.issue.number,
407
- issue,
408
- setLabels: deps.client.setLabels,
409
- });
410
- }
411
- events.push(normalizeTicketCommentEvent({
412
- repo: repoRef,
413
- issueNumber: input.projection.issue.number,
414
- comment,
415
- ingestedAt,
416
- ...(deps.selfLogin === undefined ? {} : { selfLogin: deps.selfLogin }),
417
- ...(known?.updatedAt === undefined ? {} : { existingUpdatedAt: known.updatedAt }),
418
- }));
419
- }
420
- if (autoApprovalLabelAdded) {
421
- events.push(normalizeTicketUpsert({
422
- repo: repoRef,
423
- issue: issueWithAutoApprovalLabel(issue),
424
- ingestedAt,
425
- }));
426
- }
427
- return {
428
- events,
429
- sourceRevision: latestCommentRevision.length > 0
430
- ? `github:issue:${repoRef}#${issue.number}@${issue.updated_at};comments@${latestCommentRevision}`
431
- : `github:issue:${repoRef}#${issue.number}@${issue.updated_at}`,
432
- };
433
- },
434
- async pollEvents(_input) {
435
- const ingestedAt = deps.now().toISOString();
436
- const events = [];
437
- for (const repoRef of deps.config.sources.github.repos) {
438
- const [owner, repo] = repoRef.split('/');
439
- if (owner === undefined || repo === undefined) {
440
- continue;
441
- }
442
- // One repo's failure (deleted repo, revoked access, transient API
443
- // error) must not stop polling for every other configured repo (E3).
444
- // Skipping `writeSourceState` below on failure means the `since`
445
- // cursor doesn't advance, so the next tick retries this repo from the
446
- // same point instead of silently losing the gap.
447
- try {
448
- const previousPoll = await deps.stateStore.readSourceState('github', repoRef);
449
- const since = previousPoll === null
450
- ? undefined
451
- : new Date(Date.parse(previousPoll.lastSuccessfulPollAt) - pollOverlapMs).toISOString();
452
- const issues = since === undefined
453
- ? await deps.client.listIssues(owner, repo, deps.config.sources.github.polling.maxIssuesPerRepo)
454
- : await deps.client.listIssues(owner, repo, deps.config.sources.github.polling.maxIssuesPerRepo, since);
455
- for (const issue of issues) {
456
- if (issue.pull_request !== undefined) {
457
- continue;
458
- }
459
- // Poll dedup + echo suppression only, never identity: the uri is
460
- // constructed (never parsed) and resolved through the index, which
461
- // keeps a poll flat in the number of work items rather than
462
- // O(issues x projections) (spec D2).
463
- const local = await readProjectionForIssue(repoRef, issue.number);
464
- if (local?.issue.updatedAt !== issue.updated_at) {
465
- events.push(normalizeTicketUpsert({
466
- repo: repoRef,
467
- issue,
468
- ingestedAt,
469
- expectedEcho: isExpectedLabelEcho(issue, local ?? null),
470
- }));
471
- }
472
- const comments = await deps.client.listComments(owner, repo, issue.number, deps.config.sources.github.polling.commentPageSize);
473
- let autoApprovalLabelAdded = false;
474
- for (const comment of comments) {
475
- const known = local?.comments.find((entry) => entry.id === String(comment.id));
476
- if (known?.updatedAt === comment.updated_at) {
477
- continue;
478
- }
479
- if (local?.wake.expectedEcho.commentIds.includes(String(comment.id)) === true) {
480
- continue;
481
- }
482
- if (!autoApprovalLabelAdded &&
483
- resolveAutoApprovalIntent(comment.body) !== null &&
484
- comment.user?.type !== 'Bot' &&
485
- !(comment.body ?? '').includes(wakeCommentMarker) &&
486
- (deps.selfLogin === undefined || comment.user?.login !== deps.selfLogin)) {
487
- autoApprovalLabelAdded = await ensureAutoApprovalLabel({
488
- owner,
489
- repoName: repo,
490
- issueNumber: issue.number,
491
- issue,
492
- setLabels: deps.client.setLabels,
493
- });
494
- }
495
- events.push(normalizeTicketCommentEvent({
496
- repo: repoRef,
497
- issueNumber: issue.number,
498
- comment,
499
- ingestedAt,
500
- ...(deps.selfLogin === undefined ? {} : { selfLogin: deps.selfLogin }),
501
- ...(known?.updatedAt === undefined ? {} : { existingUpdatedAt: known.updatedAt }),
502
- }));
503
- }
504
- if (autoApprovalLabelAdded) {
505
- events.push(normalizeTicketUpsert({
506
- repo: repoRef,
507
- issue: issueWithAutoApprovalLabel(issue),
508
- ingestedAt,
509
- }));
510
- }
511
- }
512
- await deps.stateStore.writeSourceState({
513
- schemaVersion: 1,
514
- source: 'github',
515
- key: repoRef,
516
- lastSuccessfulPollAt: ingestedAt,
517
- });
518
- }
519
- catch (error) {
520
- console.error(`[github-work-source] poll failed for ${repoRef}, skipping this tick: ${formatGitHubError(error)}`);
521
- }
522
- }
523
- return events;
524
- },
525
- async deliverIntent(input) {
526
- const repo = input.event.sourceRefs.repo;
527
- const issueNumber = input.event.sourceRefs.issueNumber;
528
- if (repo === undefined || issueNumber === undefined) {
529
- throw new Error(`cannot deliver intent ${input.event.eventId}: missing sourceRefs.repo/issueNumber`);
530
- }
531
- const [owner, repoName] = repo.split('/');
532
- if (owner === undefined || repoName === undefined) {
533
- throw new Error(`cannot deliver intent ${input.event.eventId}: malformed repo "${repo}"`);
534
- }
535
- const publishedAt = deps.now().toISOString();
536
- if (input.event.sourceEventType === LABELS_REQUESTED_EVENT) {
537
- // Outbound intents are keyed envelopes Wake itself minted, so the work
538
- // item is already named on the event — a direct read, not a lookup.
539
- const projection = await deps.stateStore.readIssueState(input.event.workItemKey);
540
- const currentLabels = projection?.issue.labels ?? [];
541
- const nextStatusLabel = typeof input.event.payload.statusLabel === 'string'
542
- ? input.event.payload.statusLabel
543
- : undefined;
544
- const nextStageLabel = typeof input.event.payload.stageLabel === 'string'
545
- ? input.event.payload.stageLabel
546
- : undefined;
547
- const nextWorkflowLabel = typeof input.event.payload.workflowLabel === 'string'
548
- ? input.event.payload.workflowLabel
549
- : undefined;
550
- // Unlike the three label families above, frozen/scheduled are single
551
- // toggle labels: every call site now computes the full authoritative
552
- // desired state via labelsForWorkItem, so their absence here means
553
- // "should not be present" (not "leave unspecified as before").
554
- const nextFrozenLabel = typeof input.event.payload.frozenLabel === 'string'
555
- ? input.event.payload.frozenLabel
556
- : undefined;
557
- const nextScheduledLabel = typeof input.event.payload.scheduledLabel === 'string'
558
- ? input.event.payload.scheduledLabel
559
- : undefined;
560
- const nextLabels = [
561
- ...currentLabels.filter((label) => !label.startsWith(wakeStatusLabelPrefix) &&
562
- !label.startsWith(wakeStageLabelPrefix) &&
563
- !label.startsWith(wakeWorkflowLabelPrefix) &&
564
- label !== FROZEN_WORK_ITEM_LABEL &&
565
- label !== SCHEDULED_WORKFLOW_LABEL),
566
- ...(nextStatusLabel !== undefined
567
- ? [nextStatusLabel]
568
- : currentLabels.filter((label) => label.startsWith(wakeStatusLabelPrefix))),
569
- ...(nextStageLabel !== undefined
570
- ? [nextStageLabel]
571
- : currentLabels.filter((label) => label.startsWith(wakeStageLabelPrefix))),
572
- ...(nextWorkflowLabel !== undefined
573
- ? [nextWorkflowLabel]
574
- : currentLabels.filter((label) => label.startsWith(wakeWorkflowLabelPrefix))),
575
- ...(nextFrozenLabel !== undefined ? [nextFrozenLabel] : []),
576
- ...(nextScheduledLabel !== undefined ? [nextScheduledLabel] : []),
577
- ];
578
- const labelsChanged = nextLabels.length !== currentLabels.length ||
579
- !nextLabels.every((label, index) => label === currentLabels[index]);
580
- if (labelsChanged) {
581
- await deps.client.setLabels(owner, repoName, issueNumber, nextLabels);
582
- return [
583
- createEventEnvelope({
584
- eventId: `${input.event.eventId}-labels-updated`,
585
- workItemKey: input.event.workItemKey,
586
- streamScope: 'work-item',
587
- direction: 'outbound',
588
- sourceSystem: 'github',
589
- sourceEventType: 'ticket.labels.updated',
590
- sourceRefs: {
591
- repo,
592
- issueNumber,
593
- },
594
- occurredAt: publishedAt,
595
- ingestedAt: publishedAt,
596
- trigger: 'context-only',
597
- payload: {
598
- intentEventId: input.event.eventId,
599
- idempotencyKey: input.event.payload.idempotencyKey,
600
- deliveryState: 'CONFIRMED',
601
- ...(nextStatusLabel !== undefined ? { statusLabel: nextStatusLabel } : {}),
602
- ...(nextStageLabel !== undefined ? { stageLabel: nextStageLabel } : {}),
603
- ...(nextWorkflowLabel !== undefined ? { workflowLabel: nextWorkflowLabel } : {}),
604
- ...(nextFrozenLabel !== undefined ? { frozenLabel: nextFrozenLabel } : {}),
605
- ...(nextScheduledLabel !== undefined ? { scheduledLabel: nextScheduledLabel } : {}),
606
- labels: nextLabels,
607
- providerEventType: 'github.issue.labels.updated',
608
- },
609
- }),
610
- ];
611
- }
612
- return [];
613
- }
614
- const response = await deps.client.createComment(owner, repoName, issueNumber, formatWakeComment(input.event.payload, await readControlPlaneUiUrl(deps.config.paths.wakeRoot)));
615
- const commentId = extractCreatedCommentId(response);
616
- return [
617
- createIssueCommentPublishedEvent({
618
- event: input.event,
619
- repo,
620
- issueNumber,
621
- publishedAt,
622
- ...(commentId === undefined ? {} : { commentId }),
623
- }),
624
- ];
625
- },
626
- async reconcileIntent(input) {
627
- const repo = input.event.sourceRefs.repo;
628
- const issueNumber = input.event.sourceRefs.issueNumber;
629
- if (repo === undefined || issueNumber === undefined) {
630
- return [];
631
- }
632
- const [owner, repoName] = repo.split('/');
633
- if (owner === undefined || repoName === undefined) {
634
- return [];
635
- }
636
- const publishedAt = deps.now().toISOString();
637
- if (input.event.sourceEventType === LABELS_REQUESTED_EVENT) {
638
- const projection = await deps.stateStore.readIssueState(input.event.workItemKey);
639
- const currentLabels = projection?.issue.labels ?? [];
640
- const expected = [
641
- input.event.payload.statusLabel,
642
- input.event.payload.stageLabel,
643
- input.event.payload.workflowLabel,
644
- input.event.payload.frozenLabel,
645
- input.event.payload.scheduledLabel,
646
- ].filter((label) => typeof label === 'string');
647
- if (expected.every((label) => currentLabels.includes(label))) {
648
- return [
649
- createEventEnvelope({
650
- eventId: `${input.event.eventId}-labels-updated`,
651
- workItemKey: input.event.workItemKey,
652
- streamScope: 'work-item',
653
- direction: 'outbound',
654
- sourceSystem: 'github',
655
- sourceEventType: 'ticket.labels.updated',
656
- sourceRefs: { repo, issueNumber },
657
- occurredAt: publishedAt,
658
- ingestedAt: publishedAt,
659
- trigger: 'context-only',
660
- payload: {
661
- intentEventId: input.event.eventId,
662
- idempotencyKey: input.event.payload.idempotencyKey,
663
- deliveryState: 'CONFIRMED',
664
- labels: currentLabels,
665
- providerEventType: 'github.issue.labels.updated',
666
- },
667
- }),
668
- ];
669
- }
670
- return [];
671
- }
672
- const marker = wakeIdempotencyMarker(input.event.payload.idempotencyKey);
673
- if (marker === undefined) {
674
- return [];
675
- }
676
- const comments = await deps.client.listComments(owner, repoName, issueNumber, deps.config.sources.github.polling.commentPageSize);
677
- const existing = comments.find((comment) => (comment.body ?? '').includes(marker));
678
- if (existing === undefined) {
679
- return [];
680
- }
681
- return [
682
- createIssueCommentPublishedEvent({
683
- event: input.event,
684
- repo,
685
- issueNumber,
686
- publishedAt,
687
- commentId: String(existing.id),
688
- }),
689
- ];
690
- },
691
- };
692
- }