@cat-factory/server 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agents/CompositeAgentExecutor.d.ts +39 -0
  3. package/dist/agents/CompositeAgentExecutor.d.ts.map +1 -0
  4. package/dist/agents/CompositeAgentExecutor.js +169 -0
  5. package/dist/agents/CompositeAgentExecutor.js.map +1 -0
  6. package/dist/agents/ContainerAgentExecutor.d.ts +235 -0
  7. package/dist/agents/ContainerAgentExecutor.d.ts.map +1 -0
  8. package/dist/agents/ContainerAgentExecutor.js +825 -0
  9. package/dist/agents/ContainerAgentExecutor.js.map +1 -0
  10. package/dist/agents/ContainerRepoBootstrapper.d.ts +78 -0
  11. package/dist/agents/ContainerRepoBootstrapper.d.ts.map +1 -0
  12. package/dist/agents/ContainerRepoBootstrapper.js +279 -0
  13. package/dist/agents/ContainerRepoBootstrapper.js.map +1 -0
  14. package/dist/agents/ModelRouter.d.ts +69 -0
  15. package/dist/agents/ModelRouter.d.ts.map +1 -0
  16. package/dist/agents/ModelRouter.js +84 -0
  17. package/dist/agents/ModelRouter.js.map +1 -0
  18. package/dist/agents/RunnerJobClient.d.ts +41 -0
  19. package/dist/agents/RunnerJobClient.d.ts.map +1 -0
  20. package/dist/agents/RunnerJobClient.js +43 -0
  21. package/dist/agents/RunnerJobClient.js.map +1 -0
  22. package/dist/agents/modelProviderResolver.d.ts +33 -0
  23. package/dist/agents/modelProviderResolver.d.ts.map +1 -0
  24. package/dist/agents/modelProviderResolver.js +48 -0
  25. package/dist/agents/modelProviderResolver.js.map +1 -0
  26. package/dist/agents/providerCapabilities.d.ts +22 -0
  27. package/dist/agents/providerCapabilities.d.ts.map +1 -0
  28. package/dist/agents/providerCapabilities.js +43 -0
  29. package/dist/agents/providerCapabilities.js.map +1 -0
  30. package/dist/agents/resolveRepoTarget.d.ts +33 -0
  31. package/dist/agents/resolveRepoTarget.d.ts.map +1 -0
  32. package/dist/agents/resolveRepoTarget.js +81 -0
  33. package/dist/agents/resolveRepoTarget.js.map +1 -0
  34. package/dist/app.d.ts +12 -0
  35. package/dist/app.d.ts.map +1 -0
  36. package/dist/app.js +102 -0
  37. package/dist/app.js.map +1 -0
  38. package/dist/auth/GitHubOAuth.d.ts +39 -0
  39. package/dist/auth/GitHubOAuth.d.ts.map +1 -0
  40. package/dist/auth/GitHubOAuth.js +90 -0
  41. package/dist/auth/GitHubOAuth.js.map +1 -0
  42. package/dist/auth/GoogleOAuth.d.ts +35 -0
  43. package/dist/auth/GoogleOAuth.d.ts.map +1 -0
  44. package/dist/auth/GoogleOAuth.js +66 -0
  45. package/dist/auth/GoogleOAuth.js.map +1 -0
  46. package/dist/auth/middleware.d.ts +15 -0
  47. package/dist/auth/middleware.d.ts.map +1 -0
  48. package/dist/auth/middleware.js +63 -0
  49. package/dist/auth/middleware.js.map +1 -0
  50. package/dist/auth/signing.d.ts +50 -0
  51. package/dist/auth/signing.d.ts.map +1 -0
  52. package/dist/auth/signing.js +96 -0
  53. package/dist/auth/signing.js.map +1 -0
  54. package/dist/auth/wsTicket.d.ts +34 -0
  55. package/dist/auth/wsTicket.d.ts.map +1 -0
  56. package/dist/auth/wsTicket.js +50 -0
  57. package/dist/auth/wsTicket.js.map +1 -0
  58. package/dist/config/types.d.ts +294 -0
  59. package/dist/config/types.d.ts.map +1 -0
  60. package/dist/config/types.js +2 -0
  61. package/dist/config/types.js.map +1 -0
  62. package/dist/config/url-safety.d.ts +8 -0
  63. package/dist/config/url-safety.d.ts.map +1 -0
  64. package/dist/config/url-safety.js +11 -0
  65. package/dist/config/url-safety.js.map +1 -0
  66. package/dist/containers/ContainerSessionService.d.ts +67 -0
  67. package/dist/containers/ContainerSessionService.d.ts.map +1 -0
  68. package/dist/containers/ContainerSessionService.js +44 -0
  69. package/dist/containers/ContainerSessionService.js.map +1 -0
  70. package/dist/crypto/WebCryptoPasswordHasher.d.ts +9 -0
  71. package/dist/crypto/WebCryptoPasswordHasher.d.ts.map +1 -0
  72. package/dist/crypto/WebCryptoPasswordHasher.js +67 -0
  73. package/dist/crypto/WebCryptoPasswordHasher.js.map +1 -0
  74. package/dist/crypto/WebCryptoPersonalSecretCipher.d.ts +6 -0
  75. package/dist/crypto/WebCryptoPersonalSecretCipher.d.ts.map +1 -0
  76. package/dist/crypto/WebCryptoPersonalSecretCipher.js +57 -0
  77. package/dist/crypto/WebCryptoPersonalSecretCipher.js.map +1 -0
  78. package/dist/crypto/WebCryptoSecretCipher.d.ts +23 -0
  79. package/dist/crypto/WebCryptoSecretCipher.d.ts.map +1 -0
  80. package/dist/crypto/WebCryptoSecretCipher.js +60 -0
  81. package/dist/crypto/WebCryptoSecretCipher.js.map +1 -0
  82. package/dist/crypto/encoding.d.ts +14 -0
  83. package/dist/crypto/encoding.d.ts.map +1 -0
  84. package/dist/crypto/encoding.js +58 -0
  85. package/dist/crypto/encoding.js.map +1 -0
  86. package/dist/events/FanOutEventPublisher.d.ts +32 -0
  87. package/dist/events/FanOutEventPublisher.d.ts.map +1 -0
  88. package/dist/events/FanOutEventPublisher.js +76 -0
  89. package/dist/events/FanOutEventPublisher.js.map +1 -0
  90. package/dist/events/InAppNotificationChannel.d.ts +20 -0
  91. package/dist/events/InAppNotificationChannel.d.ts.map +1 -0
  92. package/dist/events/InAppNotificationChannel.js +23 -0
  93. package/dist/events/InAppNotificationChannel.js.map +1 -0
  94. package/dist/github/FetchGitHubClient.d.ts +72 -0
  95. package/dist/github/FetchGitHubClient.d.ts.map +1 -0
  96. package/dist/github/FetchGitHubClient.js +485 -0
  97. package/dist/github/FetchGitHubClient.js.map +1 -0
  98. package/dist/github/FetchGitHubProvisioningClient.d.ts +13 -0
  99. package/dist/github/FetchGitHubProvisioningClient.d.ts.map +1 -0
  100. package/dist/github/FetchGitHubProvisioningClient.js +59 -0
  101. package/dist/github/FetchGitHubProvisioningClient.js.map +1 -0
  102. package/dist/github/GitHubAppAuth.d.ts +30 -0
  103. package/dist/github/GitHubAppAuth.d.ts.map +1 -0
  104. package/dist/github/GitHubAppAuth.js +95 -0
  105. package/dist/github/GitHubAppAuth.js.map +1 -0
  106. package/dist/github/GitHubAppRegistry.d.ts +57 -0
  107. package/dist/github/GitHubAppRegistry.d.ts.map +1 -0
  108. package/dist/github/GitHubAppRegistry.js +51 -0
  109. package/dist/github/GitHubAppRegistry.js.map +1 -0
  110. package/dist/github/GitHubCiStatusProvider.d.ts +21 -0
  111. package/dist/github/GitHubCiStatusProvider.d.ts.map +1 -0
  112. package/dist/github/GitHubCiStatusProvider.js +39 -0
  113. package/dist/github/GitHubCiStatusProvider.js.map +1 -0
  114. package/dist/github/GitHubMergeabilityProvider.d.ts +26 -0
  115. package/dist/github/GitHubMergeabilityProvider.d.ts.map +1 -0
  116. package/dist/github/GitHubMergeabilityProvider.js +38 -0
  117. package/dist/github/GitHubMergeabilityProvider.js.map +1 -0
  118. package/dist/github/GitHubPullRequestMerger.d.ts +23 -0
  119. package/dist/github/GitHubPullRequestMerger.d.ts.map +1 -0
  120. package/dist/github/GitHubPullRequestMerger.js +38 -0
  121. package/dist/github/GitHubPullRequestMerger.js.map +1 -0
  122. package/dist/github/WebCryptoWebhookVerifier.d.ts +9 -0
  123. package/dist/github/WebCryptoWebhookVerifier.d.ts.map +1 -0
  124. package/dist/github/WebCryptoWebhookVerifier.js +40 -0
  125. package/dist/github/WebCryptoWebhookVerifier.js.map +1 -0
  126. package/dist/github/ensureWorkBranch.d.ts +26 -0
  127. package/dist/github/ensureWorkBranch.d.ts.map +1 -0
  128. package/dist/github/ensureWorkBranch.js +97 -0
  129. package/dist/github/ensureWorkBranch.js.map +1 -0
  130. package/dist/github/state.d.ts +19 -0
  131. package/dist/github/state.d.ts.map +1 -0
  132. package/dist/github/state.js +55 -0
  133. package/dist/github/state.js.map +1 -0
  134. package/dist/http/authGate.d.ts +21 -0
  135. package/dist/http/authGate.d.ts.map +1 -0
  136. package/dist/http/authGate.js +77 -0
  137. package/dist/http/authGate.js.map +1 -0
  138. package/dist/http/cors.d.ts +13 -0
  139. package/dist/http/cors.d.ts.map +1 -0
  140. package/dist/http/cors.js +30 -0
  141. package/dist/http/cors.js.map +1 -0
  142. package/dist/http/env.d.ts +68 -0
  143. package/dist/http/env.d.ts.map +1 -0
  144. package/dist/http/env.js +2 -0
  145. package/dist/http/env.js.map +1 -0
  146. package/dist/http/errorHandler.d.ts +4 -0
  147. package/dist/http/errorHandler.d.ts.map +1 -0
  148. package/dist/http/errorHandler.js +33 -0
  149. package/dist/http/errorHandler.js.map +1 -0
  150. package/dist/http/params.d.ts +8 -0
  151. package/dist/http/params.d.ts.map +1 -0
  152. package/dist/http/params.js +13 -0
  153. package/dist/http/params.js.map +1 -0
  154. package/dist/http/validation.d.ts +12 -0
  155. package/dist/http/validation.d.ts.map +1 -0
  156. package/dist/http/validation.js +21 -0
  157. package/dist/http/validation.js.map +1 -0
  158. package/dist/index.d.ts +46 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +63 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/modules/accounts/AccountController.d.ts +10 -0
  163. package/dist/modules/accounts/AccountController.d.ts.map +1 -0
  164. package/dist/modules/accounts/AccountController.js +197 -0
  165. package/dist/modules/accounts/AccountController.js.map +1 -0
  166. package/dist/modules/agentRuns/AgentRunController.d.ts +10 -0
  167. package/dist/modules/agentRuns/AgentRunController.d.ts.map +1 -0
  168. package/dist/modules/agentRuns/AgentRunController.js +65 -0
  169. package/dist/modules/agentRuns/AgentRunController.js.map +1 -0
  170. package/dist/modules/auth/AuthController.d.ts +12 -0
  171. package/dist/modules/auth/AuthController.d.ts.map +1 -0
  172. package/dist/modules/auth/AuthController.js +457 -0
  173. package/dist/modules/auth/AuthController.js.map +1 -0
  174. package/dist/modules/board/BoardController.d.ts +8 -0
  175. package/dist/modules/board/BoardController.d.ts.map +1 -0
  176. package/dist/modules/board/BoardController.js +89 -0
  177. package/dist/modules/board/BoardController.js.map +1 -0
  178. package/dist/modules/boardScan/BoardScanController.d.ts +10 -0
  179. package/dist/modules/boardScan/BoardScanController.d.ts.map +1 -0
  180. package/dist/modules/boardScan/BoardScanController.js +53 -0
  181. package/dist/modules/boardScan/BoardScanController.js.map +1 -0
  182. package/dist/modules/bootstrap/BootstrapController.d.ts +10 -0
  183. package/dist/modules/bootstrap/BootstrapController.d.ts.map +1 -0
  184. package/dist/modules/bootstrap/BootstrapController.js +75 -0
  185. package/dist/modules/bootstrap/BootstrapController.js.map +1 -0
  186. package/dist/modules/clarity/ClarityReviewController.d.ts +11 -0
  187. package/dist/modules/clarity/ClarityReviewController.d.ts.map +1 -0
  188. package/dist/modules/clarity/ClarityReviewController.js +97 -0
  189. package/dist/modules/clarity/ClarityReviewController.js.map +1 -0
  190. package/dist/modules/consensus/ConsensusController.d.ts +12 -0
  191. package/dist/modules/consensus/ConsensusController.d.ts.map +1 -0
  192. package/dist/modules/consensus/ConsensusController.js +23 -0
  193. package/dist/modules/consensus/ConsensusController.js.map +1 -0
  194. package/dist/modules/documents/DocumentSourceController.d.ts +10 -0
  195. package/dist/modules/documents/DocumentSourceController.d.ts.map +1 -0
  196. package/dist/modules/documents/DocumentSourceController.js +116 -0
  197. package/dist/modules/documents/DocumentSourceController.js.map +1 -0
  198. package/dist/modules/environments/EnvironmentController.d.ts +10 -0
  199. package/dist/modules/environments/EnvironmentController.d.ts.map +1 -0
  200. package/dist/modules/environments/EnvironmentController.js +95 -0
  201. package/dist/modules/environments/EnvironmentController.js.map +1 -0
  202. package/dist/modules/events/EventsController.d.ts +26 -0
  203. package/dist/modules/events/EventsController.d.ts.map +1 -0
  204. package/dist/modules/events/EventsController.js +56 -0
  205. package/dist/modules/events/EventsController.js.map +1 -0
  206. package/dist/modules/execution/ExecutionController.d.ts +10 -0
  207. package/dist/modules/execution/ExecutionController.d.ts.map +1 -0
  208. package/dist/modules/execution/ExecutionController.js +156 -0
  209. package/dist/modules/execution/ExecutionController.js.map +1 -0
  210. package/dist/modules/fragmentLibrary/FragmentLibraryController.d.ts +14 -0
  211. package/dist/modules/fragmentLibrary/FragmentLibraryController.d.ts.map +1 -0
  212. package/dist/modules/fragmentLibrary/FragmentLibraryController.js +128 -0
  213. package/dist/modules/fragmentLibrary/FragmentLibraryController.js.map +1 -0
  214. package/dist/modules/github/GitHubController.d.ts +12 -0
  215. package/dist/modules/github/GitHubController.d.ts.map +1 -0
  216. package/dist/modules/github/GitHubController.js +234 -0
  217. package/dist/modules/github/GitHubController.js.map +1 -0
  218. package/dist/modules/github/GitHubWebhookController.d.ts +13 -0
  219. package/dist/modules/github/GitHubWebhookController.d.ts.map +1 -0
  220. package/dist/modules/github/GitHubWebhookController.js +74 -0
  221. package/dist/modules/github/GitHubWebhookController.js.map +1 -0
  222. package/dist/modules/llmProxy/LlmProxyController.d.ts +18 -0
  223. package/dist/modules/llmProxy/LlmProxyController.d.ts.map +1 -0
  224. package/dist/modules/llmProxy/LlmProxyController.js +567 -0
  225. package/dist/modules/llmProxy/LlmProxyController.js.map +1 -0
  226. package/dist/modules/localModels/LocalModelEndpointController.d.ts +4 -0
  227. package/dist/modules/localModels/LocalModelEndpointController.d.ts.map +1 -0
  228. package/dist/modules/localModels/LocalModelEndpointController.js +58 -0
  229. package/dist/modules/localModels/LocalModelEndpointController.js.map +1 -0
  230. package/dist/modules/merge/MergePresetController.d.ts +9 -0
  231. package/dist/modules/merge/MergePresetController.d.ts.map +1 -0
  232. package/dist/modules/merge/MergePresetController.js +46 -0
  233. package/dist/modules/merge/MergePresetController.js.map +1 -0
  234. package/dist/modules/modelDefaults/ModelDefaultsController.d.ts +9 -0
  235. package/dist/modules/modelDefaults/ModelDefaultsController.d.ts.map +1 -0
  236. package/dist/modules/modelDefaults/ModelDefaultsController.js +32 -0
  237. package/dist/modules/modelDefaults/ModelDefaultsController.js.map +1 -0
  238. package/dist/modules/models/ModelController.d.ts +11 -0
  239. package/dist/modules/models/ModelController.d.ts.map +1 -0
  240. package/dist/modules/models/ModelController.js +38 -0
  241. package/dist/modules/models/ModelController.js.map +1 -0
  242. package/dist/modules/notifications/NotificationController.d.ts +13 -0
  243. package/dist/modules/notifications/NotificationController.d.ts.map +1 -0
  244. package/dist/modules/notifications/NotificationController.js +67 -0
  245. package/dist/modules/notifications/NotificationController.js.map +1 -0
  246. package/dist/modules/pipelines/PipelineController.d.ts +5 -0
  247. package/dist/modules/pipelines/PipelineController.d.ts.map +1 -0
  248. package/dist/modules/pipelines/PipelineController.js +46 -0
  249. package/dist/modules/pipelines/PipelineController.js.map +1 -0
  250. package/dist/modules/promptFragments/PromptFragmentController.d.ts +11 -0
  251. package/dist/modules/promptFragments/PromptFragmentController.d.ts.map +1 -0
  252. package/dist/modules/promptFragments/PromptFragmentController.js +18 -0
  253. package/dist/modules/promptFragments/PromptFragmentController.js.map +1 -0
  254. package/dist/modules/providers/ApiKeyController.d.ts +13 -0
  255. package/dist/modules/providers/ApiKeyController.d.ts.map +1 -0
  256. package/dist/modules/providers/ApiKeyController.js +98 -0
  257. package/dist/modules/providers/ApiKeyController.js.map +1 -0
  258. package/dist/modules/providers/PersonalSubscriptionController.d.ts +4 -0
  259. package/dist/modules/providers/PersonalSubscriptionController.d.ts.map +1 -0
  260. package/dist/modules/providers/PersonalSubscriptionController.js +48 -0
  261. package/dist/modules/providers/PersonalSubscriptionController.js.map +1 -0
  262. package/dist/modules/providers/VendorCredentialController.d.ts +4 -0
  263. package/dist/modules/providers/VendorCredentialController.d.ts.map +1 -0
  264. package/dist/modules/providers/VendorCredentialController.js +55 -0
  265. package/dist/modules/providers/VendorCredentialController.js.map +1 -0
  266. package/dist/modules/providers/personalCredentialGate.d.ts +34 -0
  267. package/dist/modules/providers/personalCredentialGate.d.ts.map +1 -0
  268. package/dist/modules/providers/personalCredentialGate.js +106 -0
  269. package/dist/modules/providers/personalCredentialGate.js.map +1 -0
  270. package/dist/modules/recurring/RecurringPipelineController.d.ts +8 -0
  271. package/dist/modules/recurring/RecurringPipelineController.d.ts.map +1 -0
  272. package/dist/modules/recurring/RecurringPipelineController.js +58 -0
  273. package/dist/modules/recurring/RecurringPipelineController.js.map +1 -0
  274. package/dist/modules/recurring/TrackerSettingsController.d.ts +8 -0
  275. package/dist/modules/recurring/TrackerSettingsController.d.ts.map +1 -0
  276. package/dist/modules/recurring/TrackerSettingsController.js +30 -0
  277. package/dist/modules/recurring/TrackerSettingsController.js.map +1 -0
  278. package/dist/modules/releaseHealth/ReleaseHealthController.d.ts +9 -0
  279. package/dist/modules/releaseHealth/ReleaseHealthController.d.ts.map +1 -0
  280. package/dist/modules/releaseHealth/ReleaseHealthController.js +58 -0
  281. package/dist/modules/releaseHealth/ReleaseHealthController.js.map +1 -0
  282. package/dist/modules/requirements/RequirementReviewController.d.ts +12 -0
  283. package/dist/modules/requirements/RequirementReviewController.d.ts.map +1 -0
  284. package/dist/modules/requirements/RequirementReviewController.js +107 -0
  285. package/dist/modules/requirements/RequirementReviewController.js.map +1 -0
  286. package/dist/modules/runners/RunnerPoolController.d.ts +10 -0
  287. package/dist/modules/runners/RunnerPoolController.d.ts.map +1 -0
  288. package/dist/modules/runners/RunnerPoolController.js +52 -0
  289. package/dist/modules/runners/RunnerPoolController.js.map +1 -0
  290. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsController.d.ts +9 -0
  291. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsController.d.ts.map +1 -0
  292. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsController.js +32 -0
  293. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsController.js.map +1 -0
  294. package/dist/modules/services/ServiceMountController.d.ts +11 -0
  295. package/dist/modules/services/ServiceMountController.d.ts.map +1 -0
  296. package/dist/modules/services/ServiceMountController.js +64 -0
  297. package/dist/modules/services/ServiceMountController.js.map +1 -0
  298. package/dist/modules/settings/WorkspaceSettingsController.d.ts +9 -0
  299. package/dist/modules/settings/WorkspaceSettingsController.d.ts.map +1 -0
  300. package/dist/modules/settings/WorkspaceSettingsController.js +32 -0
  301. package/dist/modules/settings/WorkspaceSettingsController.js.map +1 -0
  302. package/dist/modules/slack/SlackController.d.ts +17 -0
  303. package/dist/modules/slack/SlackController.d.ts.map +1 -0
  304. package/dist/modules/slack/SlackController.js +135 -0
  305. package/dist/modules/slack/SlackController.js.map +1 -0
  306. package/dist/modules/tasks/TaskSourceController.d.ts +9 -0
  307. package/dist/modules/tasks/TaskSourceController.d.ts.map +1 -0
  308. package/dist/modules/tasks/TaskSourceController.js +103 -0
  309. package/dist/modules/tasks/TaskSourceController.js.map +1 -0
  310. package/dist/modules/webSearch/WebSearchProxyController.d.ts +4 -0
  311. package/dist/modules/webSearch/WebSearchProxyController.d.ts.map +1 -0
  312. package/dist/modules/webSearch/WebSearchProxyController.js +78 -0
  313. package/dist/modules/webSearch/WebSearchProxyController.js.map +1 -0
  314. package/dist/modules/webSearch/upstreams.d.ts +50 -0
  315. package/dist/modules/webSearch/upstreams.d.ts.map +1 -0
  316. package/dist/modules/webSearch/upstreams.js +107 -0
  317. package/dist/modules/webSearch/upstreams.js.map +1 -0
  318. package/dist/modules/workspaces/WorkspaceController.d.ts +5 -0
  319. package/dist/modules/workspaces/WorkspaceController.d.ts.map +1 -0
  320. package/dist/modules/workspaces/WorkspaceController.js +167 -0
  321. package/dist/modules/workspaces/WorkspaceController.js.map +1 -0
  322. package/dist/observability/logger.d.ts +9 -0
  323. package/dist/observability/logger.d.ts.map +1 -0
  324. package/dist/observability/logger.js +39 -0
  325. package/dist/observability/logger.js.map +1 -0
  326. package/dist/persistence/mappers.d.ts +101 -0
  327. package/dist/persistence/mappers.d.ts.map +1 -0
  328. package/dist/persistence/mappers.js +260 -0
  329. package/dist/persistence/mappers.js.map +1 -0
  330. package/dist/runtime/escalateNotifications.d.ts +12 -0
  331. package/dist/runtime/escalateNotifications.d.ts.map +1 -0
  332. package/dist/runtime/escalateNotifications.js +25 -0
  333. package/dist/runtime/escalateNotifications.js.map +1 -0
  334. package/dist/runtime/gateways.d.ts +159 -0
  335. package/dist/runtime/gateways.d.ts.map +1 -0
  336. package/dist/runtime/gateways.js +2 -0
  337. package/dist/runtime/gateways.js.map +1 -0
  338. package/package.json +44 -0
@@ -0,0 +1,825 @@
1
+ import {} from '@cat-factory/kernel';
2
+ import { CredentialRequiredError, SUBSCRIPTION_VENDORS, isIndividualVendor, } from '@cat-factory/kernel';
3
+ import { isLocalRunner, resolveInstanceTypeId } from '@cat-factory/contracts';
4
+ import { composeBlockSystemPrompt, FINAL_ANSWER_IN_REPLY, isReadOnlyAgentKind, systemPromptFor, userPromptFor, webResearchGuidanceFor, } from '@cat-factory/agents';
5
+ import { ModelRouter } from './ModelRouter.js';
6
+ import { CI_FIXER_AGENT_KIND, CONFLICT_RESOLVER_AGENT_KIND, FIXER_AGENT_KIND, MERGER_AGENT_KIND, ON_CALL_AGENT_KIND, SPEC_WRITER_AGENT_KIND, TESTER_AGENT_KIND, } from '@cat-factory/orchestration';
7
+ import { RunnerJobClient } from './RunnerJobClient.js';
8
+ /**
9
+ * The repo spec every container job body carries: clone coordinates plus, for a
10
+ * monorepo service, the subdirectory the harness should run the agent within. Built
11
+ * here once so the (six) agent-kind job bodies can't drift on which repo fields they
12
+ * forward.
13
+ */
14
+ /**
15
+ * The harness job id for one pipeline step: the run (execution) id plus the agent
16
+ * kind. A run executes a sequence of steps that all share the one per-run container,
17
+ * so each needs an id that is UNIQUE WITHIN THE RUN — the harness keys its per-kind
18
+ * job registries by it, and two steps sharing an id alias there (the bug where an
19
+ * `architect` /explore poll read back the `spec-writer`'s /spec result). The run is
20
+ * addressed separately by the execution id (the {@link RunnerJobRef.runId}).
21
+ */
22
+ function stepJobId(executionId, agentKind) {
23
+ return `${executionId}-${agentKind}`;
24
+ }
25
+ /**
26
+ * The {@link RunnerJobRef} a job handle addresses: the run (for the per-run container)
27
+ * plus the per-step job id. Falls back to the job id as the run id for a handle minted
28
+ * before run ids were carried (or a single-job flow where the two coincide).
29
+ */
30
+ function refForHandle(handle) {
31
+ return { runId: handle.runId ?? handle.jobId, jobId: handle.jobId };
32
+ }
33
+ function buildRepoSpec(repo) {
34
+ return {
35
+ owner: repo.owner,
36
+ name: repo.name,
37
+ baseBranch: repo.baseBranch,
38
+ cloneUrl: `https://github.com/${repo.owner}/${repo.name}.git`,
39
+ ...(repo.serviceDirectory ? { serviceDirectory: repo.serviceDirectory } : {}),
40
+ };
41
+ }
42
+ /** Poll cadence for the non-durable `run()` fallback (the durable driver sleeps between polls itself). */
43
+ const RUN_POLL_INTERVAL_MS = 5_000;
44
+ /** Role prompt the Blueprinter step's agent runs under (returns the tree as JSON). */
45
+ const BLUEPRINT_SYSTEM_PROMPT = 'You are a Domain-Driven Design architect mapping this repository. Decompose it ' +
46
+ 'into ONE top-level service and the modules inside it, where each module is a ' +
47
+ 'DOMAIN — a cohesive area of the BUSINESS, in the language of the problem space ' +
48
+ '(a DDD bounded context / aggregate / subdomain). Name modules after business ' +
49
+ 'concepts, not technical layers. ' +
50
+ 'A module MUST represent a business capability or domain model (e.g. Billing, ' +
51
+ 'Catalog, Ordering, Identity), NOT a technical layer or shape: "api", "routes", ' +
52
+ '"controllers", "utils", "helpers", "lib", "common", "config", "types", "models", ' +
53
+ '"db" and the like are NOT domains and MUST NOT be modules. ' +
54
+ 'Group the genuinely non-business, technical/cross-cutting plumbing (persistence ' +
55
+ 'wiring, HTTP/transport, logging, configuration, auth middleware, build/deploy, ' +
56
+ 'shared utilities) into a SINGLE module named "infrastructure" rather than ' +
57
+ 'scattering it into many technical modules. ' +
58
+ 'Prefer organising code by domain (the ubiquitous language) over organising by ' +
59
+ 'file type. Anchor every node to the codebase with explicit repo-relative ' +
60
+ 'file/directory references. Keep names short and descriptive. ' +
61
+ 'Respond with ONLY a JSON object of shape {"type","name","summary","references":[],' +
62
+ '"modules":[{"name","summary","references":[]}]} — no prose, no code fences. ' +
63
+ FINAL_ANSWER_IN_REPLY;
64
+ /** Role prompt the spec-writer step runs under (returns the spec doc as JSON). */
65
+ const SPEC_WRITER_SYSTEM_PROMPT = 'You maintain the PRESCRIPTIVE specification for a service. You are given the ' +
66
+ 'specification already committed to the repository (the baseline) and the ' +
67
+ 'requirements of ONE task. Apply that task as an INCREMENT onto the baseline: add ' +
68
+ 'requirements for what the task introduces, and adjust existing requirements ONLY ' +
69
+ 'where the task changes their expected behaviour. Leave every other part of the ' +
70
+ 'baseline spec untouched. Translate ONLY what the task requirements state — do NOT ' +
71
+ 'invent requirements, fill gaps, or design beyond them (missing requirements are the ' +
72
+ 'requirements step’s job, not yours). Requirements are grouped by capability, each ' +
73
+ 'phrased as "The system SHALL …" with a MoSCoW priority (must/should/could) and ' +
74
+ 'structured Given/When/Then acceptance criteria, plus cross-cutting domain rules / ' +
75
+ 'invariants. Acceptance-scenario coverage is a FIRST-CLASS deliverable: every ' +
76
+ 'requirement the task adds or changes MUST carry complete acceptance criteria — the ' +
77
+ 'happy path AND the invalid-input / error / edge / boundary cases the requirements ' +
78
+ 'imply — since the Gherkin `.feature` files and the runnable tests are derived ' +
79
+ 'mechanically from them. Preserve the baseline’s existing `sourceBlockIds`; tag the ' +
80
+ 'requirements this task adds or changes with this task’s block id. Return the ' +
81
+ 'COMPLETE updated specification (baseline plus this increment), not a diff. You have ' +
82
+ 'NO repository write access and MUST NOT write, edit, or commit any file: the platform ' +
83
+ 'persists the specification you return, so returning it IS the whole job. Respond ' +
84
+ 'with ONLY a JSON object of ' +
85
+ 'shape {"service","summary","groups":[{"name","summary","requirements":[{"id",' +
86
+ '"title","statement","kind","priority","sourceBlockIds":[],"acceptance":[{"id",' +
87
+ '"given","when","outcome"}]}]}],"rules":[{"id","rule","rationale","sourceBlockIds":[]}]} ' +
88
+ '(each acceptance criterion is a Given/When/Then, with the Then clause in `outcome`) — ' +
89
+ 'no prose, no code fences. ' +
90
+ FINAL_ANSWER_IN_REPLY;
91
+ /** Role prompt the `merger` step runs under (scores the PR; returns JSON only). */
92
+ const MERGER_SYSTEM_PROMPT = 'You are a release manager assessing a pull request before merge. Inspect the ' +
93
+ 'diff between the PR head branch and the base branch and judge three axes, each ' +
94
+ 'as a number from 0 (trivial/safe) to 1 (severe): complexity (how intricate the ' +
95
+ 'change is), risk (how likely it is to break something), and impact (blast radius ' +
96
+ 'if it does). Be conservative. Respond with ONLY a JSON object of shape ' +
97
+ '{"complexity":0.0,"risk":0.0,"impact":0.0,"rationale":"…"} — no prose, no code fences. ' +
98
+ FINAL_ANSWER_IN_REPLY;
99
+ const ON_CALL_SYSTEM_PROMPT = 'You are an on-call engineer investigating a possible post-release regression. A ' +
100
+ 'recently merged pull request shipped, and the evidence below (alerting Datadog ' +
101
+ 'monitors/SLOs and recent error logs) suggests the service regressed afterward. Read ' +
102
+ 'the PR diff on the head branch and weigh whether THIS change is the likely cause — ' +
103
+ 'beware correlation vs causation; a coincident deploy is not proof. You may read and ' +
104
+ 'inspect any file, but you MUST NOT modify, commit or revert anything; a human decides ' +
105
+ 'whether to revert. Respond with ONLY a JSON object of shape ' +
106
+ '{"culpritConfidence":0.0,"recommendation":"revert"|"hold"|"monitor","rationale":"…",' +
107
+ '"evidence":["…"]} — no prose, no code fences. ' +
108
+ FINAL_ANSWER_IN_REPLY;
109
+ /**
110
+ * An {@link AgentExecutor} that performs implementation work in a real sandbox:
111
+ * it dispatches a per-run container running the Pi coding agent (a per-run
112
+ * Cloudflare Container, or an org's self-hosted runner pool), feeds it the block's
113
+ * composed prompt fragments as context, and has it clone the repo, implement the
114
+ * block, push a branch and open a PR.
115
+ *
116
+ * Secrets never reach the container image. Provider keys stay in the backend; the
117
+ * container reaches models only through the facade's LLM proxy using a
118
+ * short-lived, model-locked session token, and clones/pushes with a short-lived
119
+ * GitHub installation token — both handed over per job. Token usage is metered
120
+ * by the proxy (the single metering point), so this executor reports no `usage`
121
+ * to avoid double-counting in the execution engine.
122
+ */
123
+ export class ContainerAgentExecutor {
124
+ deps;
125
+ /** Shared backend-polymorphic dispatch/poll/release plumbing (see RunnerJobClient). */
126
+ jobs;
127
+ /**
128
+ * Job ids whose subscription usage has already been folded into the leased token.
129
+ * `recordSubscriptionUsage` is additive, and the durable driver polls a finished
130
+ * job inside a retriable step — so a poll that records usage and then throws (or
131
+ * whose surrounding upsert/emit throws) would replay and double-count, unfairly
132
+ * penalising the token in the usage-aware rotation. Recording once per job id
133
+ * guards that. Best-effort + bounded: cleared wholesale past a cap, and it cannot
134
+ * survive a cold isolate replay — a re-record there is the documented, benign
135
+ * worst case (one extra job's tokens on one row), never silent over-counting.
136
+ */
137
+ recordedUsageJobs = new Set();
138
+ /** Resolves which model + subscription path a step runs on (routing policy). */
139
+ modelRouter;
140
+ constructor(deps) {
141
+ this.deps = deps;
142
+ this.jobs = new RunnerJobClient(deps.resolveTransport);
143
+ this.modelRouter = new ModelRouter({
144
+ agentRouting: deps.agentRouting,
145
+ resolveBlockModel: deps.resolveBlockModel,
146
+ resolveWorkspaceModelDefault: deps.resolveWorkspaceModelDefault,
147
+ hasSubscriptionToken: deps.hasSubscriptionToken,
148
+ hasPersonalSubscription: deps.hasPersonalSubscription,
149
+ });
150
+ }
151
+ /** Repo-operating steps always run as polled async jobs (the coding can be long). */
152
+ runsAsync(_context) {
153
+ return true;
154
+ }
155
+ /**
156
+ * Dispatch the implementation job to this run's container and return a handle.
157
+ * Returns as soon as the job is accepted — the work continues in the container,
158
+ * polled via {@link pollJob}. Idempotent: the harness re-attaches to a job
159
+ * already running for `executionId`, so a replayed dispatch never duplicates work.
160
+ */
161
+ async startJob(context) {
162
+ const { workspaceId, executionId } = this.requireIds(context);
163
+ const { body, model, kind, subscriptionTokenId } = await this.buildJobBody(context);
164
+ // The job's id is per-STEP (run id + agent kind), so sibling steps that share this
165
+ // run's container never collide in the harness's per-kind job registries; the run
166
+ // itself is addressed by the execution id, so its container is reclaimed as a unit.
167
+ const jobId = body.jobId;
168
+ const ref = { runId: executionId, jobId };
169
+ await this.jobs.dispatch(workspaceId, ref, body, kind, this.dispatchOptions(context));
170
+ // Carry the run id + workspace on the handle so the poll/stop site can re-address
171
+ // the same per-run container (Cloudflare vs. self-hosted pool) given only the
172
+ // handle; carry the leased subscription token id so a finished subscription job
173
+ // can attribute its usage back to the right pool row.
174
+ return {
175
+ jobId,
176
+ runId: executionId,
177
+ model,
178
+ workspaceId,
179
+ agentKind: context.agentKind,
180
+ ...(subscriptionTokenId ? { subscriptionTokenId } : {}),
181
+ };
182
+ }
183
+ /** Poll a dispatched job for its state, mapping the runner view into an update. */
184
+ async pollJob(handle) {
185
+ const view = await this.jobs.poll(handle.workspaceId, refForHandle(handle));
186
+ // Forward any tool spans the harness drained on this poll to the trace sink, as
187
+ // child spans under the RUN's trace (the run id is the trace id the LLM proxy's
188
+ // generations also use, so per-step jobs share one trace). Isolated + best-effort:
189
+ // never affects the lifecycle.
190
+ if (this.deps.llmTraceSink?.recordToolSpans && view.spans && view.spans.length > 0) {
191
+ try {
192
+ await this.deps.llmTraceSink.recordToolSpans({
193
+ workspaceId: handle.workspaceId ?? null,
194
+ executionId: handle.runId ?? handle.jobId,
195
+ agentKind: handle.agentKind ?? 'agent',
196
+ }, view.spans);
197
+ }
198
+ catch {
199
+ // Swallowed: the sink logs its own errors; observability never breaks a run.
200
+ }
201
+ }
202
+ if (view.state === 'running') {
203
+ // Forward the latest subtask counts (if any) so the engine can surface
204
+ // live "N/M done" progress on the step; the shapes match field-for-field.
205
+ return view.progress ? { state: 'running', subtasks: view.progress } : { state: 'running' };
206
+ }
207
+ if (view.state === 'failed') {
208
+ return { state: 'failed', error: view.error ?? 'Implementation job failed' };
209
+ }
210
+ // Completed: a structured `error` (e.g. "no file changes") is still a failure.
211
+ const result = view.result ?? {};
212
+ if (result.error)
213
+ return { state: 'failed', error: `Implementation failed: ${result.error}` };
214
+ // Attribute a subscription harness's reported usage to its leased pool token
215
+ // (usage-aware rotation) and the telemetry sink. Best-effort: a missing usage
216
+ // signal or unconfigured recorder is a no-op; recorded at most once per job id
217
+ // so a retried/replayed poll can't double-count (see `recordedUsageJobs`).
218
+ if (handle.subscriptionTokenId &&
219
+ handle.workspaceId &&
220
+ result.usage &&
221
+ this.deps.recordSubscriptionUsage &&
222
+ !this.recordedUsageJobs.has(handle.jobId)) {
223
+ await this.deps.recordSubscriptionUsage(handle.workspaceId, handle.subscriptionTokenId, result.usage);
224
+ // Mark only AFTER a successful write: a failed record is left to retry rather
225
+ // than silently dropped. Bound the set so a long-lived process can't grow it
226
+ // unboundedly (clearing only risks a benign re-record on a later retry).
227
+ if (this.recordedUsageJobs.size >= 10_000)
228
+ this.recordedUsageJobs.clear();
229
+ this.recordedUsageJobs.add(handle.jobId);
230
+ }
231
+ return { state: 'done', result: toRunResult(result) };
232
+ }
233
+ /**
234
+ * Stop a running job and reclaim its backing runner: resolve the same transport
235
+ * the job dispatched to (by workspace) and `release` it — for the Cloudflare
236
+ * backend this SIGKILLs the per-run container instead of letting it idle out.
237
+ * Best-effort/idempotent: a transport without `release`, or an already-gone job,
238
+ * is a no-op.
239
+ */
240
+ async stopJob(handle) {
241
+ await this.jobs.release(handle.workspaceId, refForHandle(handle));
242
+ }
243
+ /**
244
+ * Synchronous convenience for non-durable callers (and tests): dispatch then
245
+ * poll inline until the job finishes. The durable driver does not use this — it
246
+ * calls {@link startJob}/{@link pollJob} so it can sleep durably between polls.
247
+ */
248
+ async run(context) {
249
+ const handle = await this.startJob(context);
250
+ for (;;) {
251
+ const update = await this.pollJob(handle);
252
+ if (update.state === 'done') {
253
+ // The poll site can't resolve the model ref, so fold in the label the
254
+ // dispatch captured (matches what the durable path records on the step).
255
+ return { ...update.result, ...(handle.model ? { model: handle.model } : {}) };
256
+ }
257
+ if (update.state === 'failed')
258
+ throw new Error(update.error);
259
+ await new Promise((resolve) => setTimeout(resolve, RUN_POLL_INTERVAL_MS));
260
+ }
261
+ }
262
+ /**
263
+ * Preview the model this job will run, without dispatching the container. The
264
+ * proxyable-provider guard is deliberately left to `buildJobBody` (the dispatch
265
+ * path) so an unservable model still fails loudly there; this only names it.
266
+ */
267
+ async resolveModel(context) {
268
+ const ref = await this.modelRouter.resolveRef(context);
269
+ return `${ref.provider}:${ref.model}`;
270
+ }
271
+ /**
272
+ * Whether this step will run on a flat-rate subscription (quota) model — it
273
+ * resolves to a Claude Code / Codex harness (a subscription-only model, or a
274
+ * dual-mode model auto-routed to its subscription flavour because the workspace has
275
+ * a token). The engine's spend gate consults this so a quota run is not paused by
276
+ * an exhausted monetary budget it never contributes to. Best-effort: without a
277
+ * workspace id it reports false.
278
+ */
279
+ async isQuotaBased(context) {
280
+ if (!context.workspaceId)
281
+ return false;
282
+ const { ref } = await this.modelRouter.resolveEffectiveRef(context, context.workspaceId);
283
+ return ref.harness === 'claude-code' || ref.harness === 'codex';
284
+ }
285
+ /**
286
+ * Per-service provisioning hints for the dispatch: the cloud provider the service
287
+ * runs on and the abstract instance size resolved to the target's concrete
288
+ * instance-type id. Cloudflare maps the id to a Container instance type; a
289
+ * self-hosted pool forwards it (with the provider) and provisions itself. Undefined
290
+ * when the service pins no provider/size (the transport keeps its default).
291
+ */
292
+ dispatchOptions(context) {
293
+ const provider = context.service?.cloudProvider;
294
+ const size = context.service?.instanceSize;
295
+ if (!provider && !size)
296
+ return undefined;
297
+ return {
298
+ instanceTypeId: resolveInstanceTypeId(provider, size),
299
+ ...(provider ? { provider } : {}),
300
+ // Forward the abstract size too, so the local Docker/Podman backend can size
301
+ // the per-job container (`--memory`/`--cpus`) without decoding the cloud id.
302
+ ...(size ? { instanceSize: size } : {}),
303
+ };
304
+ }
305
+ /** Validate the ids every container job needs, narrowing them to non-empty strings. */
306
+ requireIds(context) {
307
+ const { workspaceId, executionId } = context;
308
+ const blockId = context.block.id;
309
+ if (!workspaceId || !executionId || !blockId) {
310
+ throw new Error('ContainerAgentExecutor requires workspaceId, executionId and block.id');
311
+ }
312
+ return { workspaceId, executionId, blockId };
313
+ }
314
+ /** Resolve tokens/prompts/target and assemble the harness job body for `context`. */
315
+ async buildJobBody(context) {
316
+ const { workspaceId, executionId, blockId } = this.requireIds(context);
317
+ // Per-STEP harness job id: unique within the run so this step's job never aliases
318
+ // a sibling step's in the (shared) per-run container's job registries.
319
+ const jobId = stepJobId(executionId, context.agentKind);
320
+ // "Subscriptions always win": a subscription-only model carries its harness; a
321
+ // dual-mode GLM/Kimi step pinned to its Cloudflare base is auto-routed to Claude
322
+ // Code when the workspace has a pooled token for the vendor. Shared with
323
+ // isQuotaBased so the dispatch and the spend gate agree on what the step runs.
324
+ const { ref, subscriptionVendor } = await this.modelRouter.resolveEffectiveRef(context, workspaceId);
325
+ const harness = ref.harness ?? 'pi';
326
+ // The Pi harness reaches models through the LLM proxy, so its model must be a
327
+ // provider the proxy can serve; locking it here stops the container choosing
328
+ // another. The subscription harnesses (Claude Code / Codex) talk direct to the
329
+ // vendor with a pooled token, so the proxyable guard does not apply to them.
330
+ if (harness === 'pi' && !isProxyableProvider(ref.provider)) {
331
+ throw new Error(`Container implementation needs a model the LLM proxy can serve ` +
332
+ `(Workers AI, a direct OpenAI-compatible provider, or a local runner); ` +
333
+ `'${ref.provider}' is not supported. Pick a Workers AI model, configure a ` +
334
+ `provider key (QWEN_API_KEY / DEEPSEEK_API_KEY / MOONSHOT_API_KEY), or add a local ` +
335
+ `runner (Ollama / LM Studio / …) and pick that model.`);
336
+ }
337
+ const repo = await this.deps.resolveRepoTarget(workspaceId, blockId);
338
+ if (!repo) {
339
+ throw new Error(`No connected GitHub repository found for workspace '${workspaceId}'`);
340
+ }
341
+ const ghToken = await this.deps.mintInstallationToken(repo.installationId);
342
+ // The shared per-task work branch every agent in this pipeline operates on. Its name
343
+ // is deterministic from the block id (so a retry/replay/sweeper re-drive always targets
344
+ // the SAME branch with no extra persistence), and once a PR is open it IS this branch.
345
+ // Ensure it up front (mechanical, idempotent) so even the read-only design agents clone
346
+ // the branch the earlier writers committed to — e.g. the spec-writer's in-repo `spec/`.
347
+ // Writers create it from base when absent; read-only agents only probe (a missing
348
+ // branch ⇒ nothing to read yet ⇒ fall back to base), so a code-less pipeline never
349
+ // orphans an empty ref. Once this block already has a PR, the branch IS that PR's
350
+ // branch, so we skip the round-trip entirely.
351
+ const workBranch = `cat-factory/${blockId}`;
352
+ const workBranchReady = context.block.pullRequest?.branch === workBranch
353
+ ? true
354
+ : this.deps.ensureWorkBranch
355
+ ? await this.deps.ensureWorkBranch(repo, workBranch, {
356
+ create: !isReadOnlyAgentKind(context.agentKind),
357
+ })
358
+ : false;
359
+ // Resolve the per-job auth the harness carries: the proxy session token for Pi,
360
+ // or a leased subscription token for Claude Code / Codex. `auth` is spread into
361
+ // every job body so the per-kind bodies can't drift on which auth they forward.
362
+ const { auth, subscriptionTokenId } = await this.resolveAuth(context, {
363
+ harness,
364
+ ref,
365
+ subscriptionVendor,
366
+ workspaceId,
367
+ executionId,
368
+ });
369
+ // The fields EVERY harness job body carries, built once so the per-kind bodies
370
+ // can't drift on which jobId/model/auth/repo/proxy fields they forward.
371
+ const common = {
372
+ jobId,
373
+ model: ref.model,
374
+ ...auth,
375
+ ghToken,
376
+ repo: buildRepoSpec(repo),
377
+ ...(this.deps.githubApiBase ? { githubApiBase: this.deps.githubApiBase } : {}),
378
+ };
379
+ // The proxy-backed web-tools nudge + switch, shared by the kinds that allow web
380
+ // access (coder/mocker/ci-fixer/fixer/tester/read-only). The harness surfaces the
381
+ // tools only when web search is configured in the container env. Per-kind hint
382
+ // (coder/mocker/analysis/… and any custom container kind resolves its own).
383
+ const webTools = {
384
+ webToolsGuidance: webResearchGuidanceFor(context.agentKind, { fetch: true }),
385
+ ...(this.deps.webSearchProxyEnabled ? { webSearch: true } : {}),
386
+ };
387
+ const { body, kind } = this.buildKindBody(context, {
388
+ common,
389
+ webTools,
390
+ repo,
391
+ workBranch,
392
+ workBranchReady,
393
+ });
394
+ return { subscriptionTokenId, body, model: `${ref.provider}:${ref.model}`, kind };
395
+ }
396
+ /**
397
+ * Resolve the per-job auth the harness carries: the proxy session token for Pi, or a
398
+ * leased subscription token for Claude Code / Codex. Spread into every job body
399
+ * (`common`) so the per-kind bodies can't drift on which auth they forward.
400
+ */
401
+ async resolveAuth(context, args) {
402
+ const { harness, ref, subscriptionVendor, workspaceId, executionId } = args;
403
+ if (harness === 'pi') {
404
+ const accountId = this.deps.resolveAccountId
405
+ ? await this.deps.resolveAccountId(workspaceId)
406
+ : undefined;
407
+ const sessionToken = await this.deps.sessionService.mint({
408
+ workspaceId,
409
+ accountId: accountId ?? undefined,
410
+ userId: context.initiatedByUserId,
411
+ executionId,
412
+ agentKind: context.agentKind,
413
+ provider: ref.provider,
414
+ model: ref.model,
415
+ });
416
+ return { auth: { harness, proxyBaseUrl: this.deps.proxyBaseUrl, sessionToken } };
417
+ }
418
+ if (!subscriptionVendor) {
419
+ throw new Error(`The ${harness} harness is not configured on this deployment; connect a ` +
420
+ `subscription token or pick a different model.`);
421
+ }
422
+ // Individual-usage vendors (Claude) are NOT pooled: lease the run-initiator's OWN
423
+ // activated personal credential. Pooled vendors (GLM/Kimi/DeepSeek/Codex) lease
424
+ // from the workspace pool. Either path hands the RAW credential to the resolved
425
+ // runner transport (see the trust note below).
426
+ let secret;
427
+ let subscriptionTokenId;
428
+ if (isIndividualVendor(subscriptionVendor)) {
429
+ if (!this.deps.leasePersonalSubscriptionToken) {
430
+ throw new Error(`Personal ${subscriptionVendor} subscriptions are not configured on this ` +
431
+ `deployment (no ENCRYPTION_KEY); pick a different model.`);
432
+ }
433
+ if (!context.initiatedByUserId) {
434
+ // No identified initiator (auth-disabled/local dev): an individual-usage
435
+ // credential is owned by a specific user and can't be resolved without one.
436
+ throw new CredentialRequiredError(`Running a ${subscriptionVendor} model requires a signed-in user with a personal subscription.`, { vendor: subscriptionVendor, reason: 'no_subscription' });
437
+ }
438
+ // Throws CredentialRequiredError(password_required) when the run has no live
439
+ // activation — the dispatch path surfaces it as a clear, retriable failure.
440
+ const leased = await this.deps.leasePersonalSubscriptionToken(executionId, context.initiatedByUserId, subscriptionVendor);
441
+ secret = leased.secret;
442
+ }
443
+ else {
444
+ if (!this.deps.leaseSubscriptionToken) {
445
+ throw new Error(`The ${harness} harness is not configured on this deployment; connect a ` +
446
+ `subscription token or pick a different model.`);
447
+ }
448
+ const leased = await this.deps.leaseSubscriptionToken(workspaceId, subscriptionVendor);
449
+ subscriptionTokenId = leased.tokenId;
450
+ secret = leased.secret;
451
+ }
452
+ // SECURITY/TRUST: unlike the Pi harness (short-lived, model-locked proxy session
453
+ // token) this hands the RAW, long-lived subscription credential — a Claude OAuth
454
+ // token or a full ChatGPT auth.json — to the resolved runner transport. For the
455
+ // Cloudflare backend that is an ephemeral, managed per-run container. For a
456
+ // self-hosted runner pool it is the WORKSPACE'S OWN BYO infra (it connected the
457
+ // pool), so the credential stays within the workspace's trust domain — but a
458
+ // workspace should only point its subscription-harness steps at a runner pool it
459
+ // operates, since the credential leaves the backend to reach it.
460
+ // Non-Anthropic Claude-Code vendors (GLM/Kimi/DeepSeek) need their Anthropic-
461
+ // compatible base URL; Anthropic itself uses the OAuth token against api.anthropic.com.
462
+ const baseUrl = SUBSCRIPTION_VENDORS[subscriptionVendor].baseUrl;
463
+ return {
464
+ auth: {
465
+ harness,
466
+ subscriptionToken: secret,
467
+ ...(baseUrl ? { subscriptionBaseUrl: baseUrl } : {}),
468
+ },
469
+ ...(subscriptionTokenId ? { subscriptionTokenId } : {}),
470
+ };
471
+ }
472
+ /**
473
+ * Build the per-kind harness job body: the shared `common` fields plus ONLY the delta
474
+ * specific to this kind's harness endpoint (its prompts, the branch it runs on, and
475
+ * any per-kind extras), and the matching dispatch `kind`. The web-search fields live
476
+ * in `webTools` (shared by the kinds that allow web access). The dispatch precedence
477
+ * matches the original if-ladder exactly: the specific kinds first, then any read-only
478
+ * kind, then the default coder body.
479
+ */
480
+ buildKindBody(context, parts) {
481
+ const { common, webTools, repo, workBranch, workBranchReady } = parts;
482
+ const prBranch = context.block.pullRequest?.branch;
483
+ const roleSystemPrompt = composeBlockSystemPrompt(systemPromptFor(context.agentKind), context.block);
484
+ switch (context.agentKind) {
485
+ // The Blueprinter step commits the regenerated `blueprints/` folder onto an
486
+ // existing branch (the earlier `coder` step's PR branch when present, else the
487
+ // repo's default branch) — never a fresh branch / new PR. Its body targets the
488
+ // harness `/blueprint` endpoint and returns the decomposition tree.
489
+ case 'blueprints':
490
+ return {
491
+ kind: 'blueprint',
492
+ body: {
493
+ ...common,
494
+ systemPrompt: BLUEPRINT_SYSTEM_PROMPT,
495
+ instructions: 'Map (or update) this repository into the canonical service → modules ' +
496
+ 'blueprint, anchored to real file/directory references.',
497
+ branch: prBranch ?? repo.baseBranch,
498
+ mode: prBranch ? 'update' : 'create',
499
+ },
500
+ };
501
+ // The spec-writer commits the regenerated `spec/` folder onto the implementation
502
+ // branch — the earlier `coder` step's PR branch when present, else the
503
+ // deterministic `cat-factory/<blockId>` the coder WILL resume (created from base if
504
+ // absent). It NEVER targets the base branch: the spec is a prescriptive document
505
+ // for not-yet-landed work, so — like the feature-time blueprint — it must merge
506
+ // together WITH the feature, never reach `main` ahead of it. Its body carries ONLY
507
+ // this task's requirements (the block description already IS the task's reworked /
508
+ // incorporated requirements): the writer reads the baseline spec committed on the
509
+ // branch and applies this task as an increment, so an unmerged sibling task's work
510
+ // never bleeds in. Targets the harness `/spec` endpoint.
511
+ case SPEC_WRITER_AGENT_KIND:
512
+ return {
513
+ kind: 'spec',
514
+ body: {
515
+ ...common,
516
+ systemPrompt: SPEC_WRITER_SYSTEM_PROMPT,
517
+ instructions: 'Apply this task as an increment onto the specification already committed to ' +
518
+ 'the repository: add requirements for what it introduces, adjust existing ' +
519
+ 'ones only where it changes their behaviour, and leave the rest untouched. ' +
520
+ 'Every requirement you add or change must carry COMPLETE acceptance-scenario ' +
521
+ 'coverage. Return the complete updated specification.',
522
+ branch: workBranch,
523
+ task: {
524
+ id: context.block.id,
525
+ title: context.block.title,
526
+ description: context.block.description,
527
+ },
528
+ },
529
+ };
530
+ // The CI-fixer clones the PR head branch, runs the failing build/tests, fixes
531
+ // them and pushes back to the SAME branch (no new branch / PR) so CI re-runs.
532
+ case CI_FIXER_AGENT_KIND:
533
+ if (!prBranch) {
534
+ throw new Error('CI-fixer needs the implementation PR branch to push fixes to');
535
+ }
536
+ return {
537
+ kind: 'ci-fix',
538
+ body: {
539
+ ...common,
540
+ systemPrompt: roleSystemPrompt,
541
+ userPrompt: userPromptFor(context),
542
+ branch: prBranch,
543
+ ...webTools,
544
+ },
545
+ };
546
+ // The conflict-resolver clones the PR head branch, merges the base in, resolves
547
+ // the conflicts and pushes back to the SAME branch (no new branch / PR) so the
548
+ // PR becomes mergeable and CI re-runs.
549
+ //
550
+ // Unlike the CI-fixer it is deliberately NOT given `userPromptFor(context)`: that
551
+ // renders the full task brief + every prior agent's output (the spec-writer's whole
552
+ // spec, etc.), which buries the one-line "resolve a conflict" role and drifts the
553
+ // model onto re-implementing the feature (observed in prod: a resolver that returned
554
+ // a "test report is ready" answer and never touched the markers). The harness
555
+ // discovers the conflicted files in the container and leads the prompt with the
556
+ // actual hunks; the backend supplies only a compact task reference for intent.
557
+ case CONFLICT_RESOLVER_AGENT_KIND: {
558
+ if (!prBranch) {
559
+ throw new Error('Conflict-resolver needs the implementation PR branch to resolve conflicts on');
560
+ }
561
+ const description = context.block.description?.trim();
562
+ return {
563
+ kind: 'resolve-conflicts',
564
+ body: {
565
+ ...common,
566
+ systemPrompt: roleSystemPrompt,
567
+ userPrompt: `Task: ${context.block.title}${description ? `\n\n${description}` : ''}`,
568
+ branch: prBranch,
569
+ },
570
+ };
571
+ }
572
+ // The merger clones the PR head branch to assess the diff vs base; it makes no
573
+ // commits (the engine performs the real merge through the GitHub API on its
574
+ // verdict). Returns ONLY a JSON assessment, mapped to `mergeAssessment`.
575
+ case MERGER_AGENT_KIND:
576
+ return {
577
+ kind: 'merge',
578
+ body: {
579
+ ...common,
580
+ systemPrompt: MERGER_SYSTEM_PROMPT,
581
+ instructions: 'Assess the pull request on the head branch against the base branch and ' +
582
+ 'return the complexity / risk / impact scores + rationale as JSON.',
583
+ branch: prBranch ?? repo.baseBranch,
584
+ ...(context.block.pullRequest?.number !== undefined
585
+ ? { prNumber: context.block.pullRequest.number }
586
+ : {}),
587
+ },
588
+ };
589
+ // The on-call agent investigates a post-release regression: it correlates the
590
+ // RELEASED change with the Datadog regression evidence (handed in via priorOutputs)
591
+ // and returns ONLY a JSON assessment — it makes NO commits and reverts nothing (the
592
+ // engine raises a notification for a human). The gate only escalates AFTER the merger
593
+ // step, which merges the PR and DELETES the work branch, so the head branch is gone by
594
+ // now — clone the BASE branch (which always exists and contains the merged change) and
595
+ // hand the agent the PR number + the now-historical head branch name so it can locate
596
+ // the merged commit in history. Targets the harness `/on-call` endpoint.
597
+ case ON_CALL_AGENT_KIND:
598
+ return {
599
+ kind: 'on-call',
600
+ body: {
601
+ ...common,
602
+ systemPrompt: ON_CALL_SYSTEM_PROMPT,
603
+ userPrompt: userPromptFor(context),
604
+ branch: repo.baseBranch,
605
+ ...(context.block.pullRequest?.branch
606
+ ? { headBranch: context.block.pullRequest.branch }
607
+ : {}),
608
+ ...(context.block.pullRequest?.number !== undefined
609
+ ? { prNumber: context.block.pullRequest.number }
610
+ : {}),
611
+ },
612
+ };
613
+ // The tester clones the PR head branch, stands up its dependencies (locally via
614
+ // the service's docker-compose, or against the provisioned ephemeral env — the
615
+ // task's `tester.environment` config picks which), runs the suite and returns a
616
+ // structured report. It makes NO commits (the engine loops the `fixer` on a
617
+ // withheld greenlight). Targets the harness `/test` endpoint; mapped to `testReport`.
618
+ case TESTER_AGENT_KIND: {
619
+ const env = context.block.agentConfig?.['tester.environment'] === 'local' ? 'local' : 'ephemeral';
620
+ const service = context.service;
621
+ return {
622
+ kind: 'test',
623
+ body: {
624
+ ...common,
625
+ systemPrompt: roleSystemPrompt,
626
+ userPrompt: userPromptFor(context),
627
+ branch: prBranch ?? repo.baseBranch,
628
+ // How the Tester stands up its dependencies for this run.
629
+ test: {
630
+ environment: env,
631
+ ...(env === 'local'
632
+ ? {
633
+ noInfraDependencies: service?.noInfraDependencies === true,
634
+ ...(service?.testComposePath ? { composePath: service.testComposePath } : {}),
635
+ }
636
+ : {}),
637
+ ...(env === 'ephemeral' && context.environment?.url
638
+ ? { environmentUrl: context.environment.url }
639
+ : {}),
640
+ },
641
+ ...webTools,
642
+ },
643
+ };
644
+ }
645
+ // The fixer clones the PR head branch, applies fixes for the concerns in the
646
+ // Tester's report (folded into the user prompt via the prior `tester` output) and
647
+ // pushes back to the SAME branch (no new branch / PR) so the Tester can re-run.
648
+ // Mirrors the CI-fixer's body; targets the harness `/fix-tests` endpoint.
649
+ case FIXER_AGENT_KIND:
650
+ if (!prBranch) {
651
+ throw new Error('Fixer needs the implementation PR branch to push fixes to');
652
+ }
653
+ return {
654
+ kind: 'fix-tests',
655
+ body: {
656
+ ...common,
657
+ systemPrompt: roleSystemPrompt,
658
+ userPrompt: userPromptFor(context),
659
+ branch: prBranch,
660
+ ...webTools,
661
+ },
662
+ };
663
+ }
664
+ // Read-only agents (architect, analysis) explore a real checkout but never edit
665
+ // it: they clone a branch, produce a prose report/proposal and return it as
666
+ // `output`. They target the harness `/explore` endpoint — which opens no branch,
667
+ // makes no commit, opens no PR, and (unlike `/run`) does NOT treat an edit-free
668
+ // run as a failure. One shared body for every read-only kind. They explore the
669
+ // shared work branch when it exists (so e.g. the architect reads the spec-writer's
670
+ // committed `spec/` and any in-progress implementation), falling back to base when
671
+ // it could not be ensured (no GitHub wired) and no PR branch exists yet.
672
+ if (isReadOnlyAgentKind(context.agentKind)) {
673
+ return {
674
+ kind: 'explore',
675
+ body: {
676
+ ...common,
677
+ // The harness explore job's temp-dir/log label. Named `label`, not `kind`:
678
+ // `kind` is the dispatch discriminator the transport stamps onto the body.
679
+ label: context.agentKind,
680
+ systemPrompt: roleSystemPrompt,
681
+ userPrompt: userPromptFor(context),
682
+ branch: workBranchReady ? workBranch : (prBranch ?? repo.baseBranch),
683
+ ...webTools,
684
+ },
685
+ };
686
+ }
687
+ // The default coder (and any other write-and-PR kind): the build-phase role plus
688
+ // the block's selected best-practice fragments, exactly as the inline executor
689
+ // composes (engine-resolved tenant catalog when present, else the manual ids).
690
+ // `headBranch` is deterministic per task (block), NOT per dispatch: a retry mints a
691
+ // fresh executionId but keeps the blockId, and a sweeper re-drive keeps both — so a
692
+ // stable name means every re-dispatch of this task targets the SAME branch. The
693
+ // harness checkpoints commits to it during the run and RESUMES on it if it already
694
+ // exists, so an evicted/failed run's work survives and a retry continues on top of
695
+ // it rather than starting over.
696
+ return {
697
+ kind: 'run',
698
+ body: {
699
+ ...common,
700
+ systemPrompt: roleSystemPrompt,
701
+ userPrompt: userPromptFor(context),
702
+ headBranch: workBranch,
703
+ pr: {
704
+ title: `${context.block.title} (${context.pipelineName})`,
705
+ body: prBody(context),
706
+ },
707
+ ...webTools,
708
+ },
709
+ };
710
+ }
711
+ }
712
+ /** Map a finished runner {@link RunnerJobResult} into the engine's {@link AgentRunResult}. */
713
+ function toRunResult(result) {
714
+ // A Blueprinter job carries a decomposition tree instead of a PR; surface it so
715
+ // the engine can strictly validate + reconcile it onto the board.
716
+ if (result.service !== undefined) {
717
+ return {
718
+ output: result.summary?.trim() || 'Service blueprint updated.',
719
+ blueprintService: result.service,
720
+ };
721
+ }
722
+ // A spec-writer job carries a prescriptive specification doc instead of a PR;
723
+ // surface it so the engine can strictly validate + persist/surface it.
724
+ if (result.spec !== undefined) {
725
+ return {
726
+ output: result.summary?.trim() || 'Service specification updated.',
727
+ spec: result.spec,
728
+ };
729
+ }
730
+ // A `merger` job carries a PR assessment instead of a PR; surface it so the
731
+ // engine can compare it to the task's thresholds and merge-or-notify.
732
+ if (result.assessment !== undefined) {
733
+ return {
734
+ output: result.summary?.trim() || 'Pull request assessed.',
735
+ mergeAssessment: result.assessment,
736
+ };
737
+ }
738
+ // An `on-call` job carries a release-regression assessment; surface it so the engine
739
+ // can raise the `release_regression` notification + enrich any open incident.
740
+ if (result.onCallAssessment !== undefined) {
741
+ return {
742
+ output: result.summary?.trim() || 'Release regression investigated.',
743
+ onCallAssessment: result.onCallAssessment,
744
+ };
745
+ }
746
+ // A `tester` job carries a structured test report instead of a PR; surface it so
747
+ // the engine can greenlight-or-loop the fixer.
748
+ if (result.report !== undefined) {
749
+ return {
750
+ output: result.summary?.trim() || 'Testing complete.',
751
+ testReport: result.report,
752
+ };
753
+ }
754
+ // A `ci-fixer` job reports whether it pushed a fix. The engine's CI gate ignores
755
+ // this result (it just re-polls CI), but map it to a sensible output regardless.
756
+ if (result.pushed !== undefined) {
757
+ return {
758
+ output: result.summary?.trim() ||
759
+ (result.pushed ? 'Pushed a CI fix to the PR branch.' : 'No CI fix was produced.'),
760
+ };
761
+ }
762
+ // A `conflict-resolver` job reports whether the branch is now mergeable. The
763
+ // engine's conflicts gate re-checks mergeability regardless; map to an output.
764
+ if (result.resolved !== undefined) {
765
+ return {
766
+ output: result.summary?.trim() ||
767
+ (result.resolved
768
+ ? 'Resolved merge conflicts and pushed to the PR branch.'
769
+ : 'Could not fully resolve the merge conflicts.'),
770
+ };
771
+ }
772
+ const summary = result.summary?.trim() || 'Implementation complete.';
773
+ const output = result.prUrl ? `${summary}\n\nPR: ${result.prUrl}` : summary;
774
+ // Surface the opened PR structurally (not just in the output text) so the
775
+ // engine can record it on the block and the board can link straight to it.
776
+ const pullRequest = result.prUrl
777
+ ? {
778
+ url: result.prUrl,
779
+ ...(prNumberFromUrl(result.prUrl) !== undefined
780
+ ? { number: prNumberFromUrl(result.prUrl) }
781
+ : {}),
782
+ ...(result.branch ? { branch: result.branch } : {}),
783
+ }
784
+ : undefined;
785
+ // No `model` here: the proxy meters tokens and the async path doesn't carry the
786
+ // provider ref to the poll site. `usage` is likewise omitted (metered by the proxy).
787
+ return {
788
+ output,
789
+ ...(pullRequest ? { pullRequest } : {}),
790
+ };
791
+ }
792
+ /** Extract the PR number from a GitHub pull-request URL (`.../pull/42`). */
793
+ function prNumberFromUrl(url) {
794
+ const match = /\/pull\/(\d+)/.exec(url);
795
+ if (!match)
796
+ return undefined;
797
+ const n = Number(match[1]);
798
+ return Number.isFinite(n) ? n : undefined;
799
+ }
800
+ /**
801
+ * Providers the LLM proxy can serve: the direct OpenAI Chat Completions-compatible
802
+ * upstreams it forwards to, plus `workers-ai`, which it runs in-Worker through the
803
+ * AI binding (no provider key required), plus the local runners (Ollama / LM Studio /
804
+ * llama.cpp / vLLM / custom), which the proxy forwards to the run initiator's own
805
+ * OpenAI-compatible endpoint (no key lease).
806
+ */
807
+ function isProxyableProvider(provider) {
808
+ return (provider === 'workers-ai' ||
809
+ provider === 'qwen' ||
810
+ provider === 'deepseek' ||
811
+ provider === 'moonshot' ||
812
+ provider === 'openai' ||
813
+ isLocalRunner(provider));
814
+ }
815
+ function prBody(context) {
816
+ const lines = [
817
+ `Automated implementation for block **${context.block.title}** (${context.block.type}).`,
818
+ '',
819
+ context.block.description || '(no description)',
820
+ '',
821
+ `Pipeline: ${context.pipelineName}`,
822
+ ];
823
+ return lines.join('\n');
824
+ }
825
+ //# sourceMappingURL=ContainerAgentExecutor.js.map