@intrect/openswarm 0.2.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 (437) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +544 -0
  3. package/config.example.yaml +107 -0
  4. package/dist/adapters/base.d.ts +8 -0
  5. package/dist/adapters/base.d.ts.map +1 -0
  6. package/dist/adapters/base.js +104 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/claude.d.ts +13 -0
  9. package/dist/adapters/claude.d.ts.map +1 -0
  10. package/dist/adapters/claude.js +318 -0
  11. package/dist/adapters/claude.js.map +1 -0
  12. package/dist/adapters/codex.d.ts +14 -0
  13. package/dist/adapters/codex.d.ts.map +1 -0
  14. package/dist/adapters/codex.js +366 -0
  15. package/dist/adapters/codex.js.map +1 -0
  16. package/dist/adapters/cryptoQuantAdapter.d.ts +85 -0
  17. package/dist/adapters/cryptoQuantAdapter.d.ts.map +1 -0
  18. package/dist/adapters/cryptoQuantAdapter.js +238 -0
  19. package/dist/adapters/cryptoQuantAdapter.js.map +1 -0
  20. package/dist/adapters/index.d.ts +17 -0
  21. package/dist/adapters/index.d.ts.map +1 -0
  22. package/dist/adapters/index.js +47 -0
  23. package/dist/adapters/index.js.map +1 -0
  24. package/dist/adapters/processRegistry.d.ts +38 -0
  25. package/dist/adapters/processRegistry.d.ts.map +1 -0
  26. package/dist/adapters/processRegistry.js +147 -0
  27. package/dist/adapters/processRegistry.js.map +1 -0
  28. package/dist/adapters/streamBuffer.d.ts +59 -0
  29. package/dist/adapters/streamBuffer.d.ts.map +1 -0
  30. package/dist/adapters/streamBuffer.js +123 -0
  31. package/dist/adapters/streamBuffer.js.map +1 -0
  32. package/dist/adapters/types.d.ts +65 -0
  33. package/dist/adapters/types.d.ts.map +1 -0
  34. package/dist/adapters/types.js +6 -0
  35. package/dist/adapters/types.js.map +1 -0
  36. package/dist/agents/agentBus.d.ts +160 -0
  37. package/dist/agents/agentBus.d.ts.map +1 -0
  38. package/dist/agents/agentBus.js +350 -0
  39. package/dist/agents/agentBus.js.map +1 -0
  40. package/dist/agents/agentPair.d.ts +210 -0
  41. package/dist/agents/agentPair.d.ts.map +1 -0
  42. package/dist/agents/agentPair.js +420 -0
  43. package/dist/agents/agentPair.js.map +1 -0
  44. package/dist/agents/auditor.d.ts +27 -0
  45. package/dist/agents/auditor.d.ts.map +1 -0
  46. package/dist/agents/auditor.js +237 -0
  47. package/dist/agents/auditor.js.map +1 -0
  48. package/dist/agents/cliStreamParser.d.ts +18 -0
  49. package/dist/agents/cliStreamParser.d.ts.map +1 -0
  50. package/dist/agents/cliStreamParser.js +156 -0
  51. package/dist/agents/cliStreamParser.js.map +1 -0
  52. package/dist/agents/documenter.d.ts +31 -0
  53. package/dist/agents/documenter.d.ts.map +1 -0
  54. package/dist/agents/documenter.js +285 -0
  55. package/dist/agents/documenter.js.map +1 -0
  56. package/dist/agents/index.d.ts +10 -0
  57. package/dist/agents/index.d.ts.map +1 -0
  58. package/dist/agents/index.js +10 -0
  59. package/dist/agents/index.js.map +1 -0
  60. package/dist/agents/pairMetrics.d.ts +63 -0
  61. package/dist/agents/pairMetrics.d.ts.map +1 -0
  62. package/dist/agents/pairMetrics.js +231 -0
  63. package/dist/agents/pairMetrics.js.map +1 -0
  64. package/dist/agents/pairPipeline.d.ts +155 -0
  65. package/dist/agents/pairPipeline.d.ts.map +1 -0
  66. package/dist/agents/pairPipeline.js +793 -0
  67. package/dist/agents/pairPipeline.js.map +1 -0
  68. package/dist/agents/pairWebhook.d.ts +59 -0
  69. package/dist/agents/pairWebhook.d.ts.map +1 -0
  70. package/dist/agents/pairWebhook.js +242 -0
  71. package/dist/agents/pairWebhook.js.map +1 -0
  72. package/dist/agents/pipelineFormat.d.ts +11 -0
  73. package/dist/agents/pipelineFormat.d.ts.map +1 -0
  74. package/dist/agents/pipelineFormat.js +164 -0
  75. package/dist/agents/pipelineFormat.js.map +1 -0
  76. package/dist/agents/pipelineGuards.d.ts +23 -0
  77. package/dist/agents/pipelineGuards.d.ts.map +1 -0
  78. package/dist/agents/pipelineGuards.js +175 -0
  79. package/dist/agents/pipelineGuards.js.map +1 -0
  80. package/dist/agents/reviewer.d.ts +37 -0
  81. package/dist/agents/reviewer.d.ts.map +1 -0
  82. package/dist/agents/reviewer.js +213 -0
  83. package/dist/agents/reviewer.js.map +1 -0
  84. package/dist/agents/skillDocumenter.d.ts +23 -0
  85. package/dist/agents/skillDocumenter.d.ts.map +1 -0
  86. package/dist/agents/skillDocumenter.js +218 -0
  87. package/dist/agents/skillDocumenter.js.map +1 -0
  88. package/dist/agents/tester.d.ts +37 -0
  89. package/dist/agents/tester.d.ts.map +1 -0
  90. package/dist/agents/tester.js +308 -0
  91. package/dist/agents/tester.js.map +1 -0
  92. package/dist/agents/worker.d.ts +30 -0
  93. package/dist/agents/worker.d.ts.map +1 -0
  94. package/dist/agents/worker.js +128 -0
  95. package/dist/agents/worker.js.map +1 -0
  96. package/dist/automation/autonomousRunner.d.ts +123 -0
  97. package/dist/automation/autonomousRunner.d.ts.map +1 -0
  98. package/dist/automation/autonomousRunner.js +1082 -0
  99. package/dist/automation/autonomousRunner.js.map +1 -0
  100. package/dist/automation/ciWorker.d.ts +51 -0
  101. package/dist/automation/ciWorker.d.ts.map +1 -0
  102. package/dist/automation/ciWorker.js +282 -0
  103. package/dist/automation/ciWorker.js.map +1 -0
  104. package/dist/automation/conflictResolver.d.ts +29 -0
  105. package/dist/automation/conflictResolver.d.ts.map +1 -0
  106. package/dist/automation/conflictResolver.js +261 -0
  107. package/dist/automation/conflictResolver.js.map +1 -0
  108. package/dist/automation/dailyReporter.d.ts +26 -0
  109. package/dist/automation/dailyReporter.d.ts.map +1 -0
  110. package/dist/automation/dailyReporter.js +132 -0
  111. package/dist/automation/dailyReporter.js.map +1 -0
  112. package/dist/automation/index.d.ts +7 -0
  113. package/dist/automation/index.d.ts.map +1 -0
  114. package/dist/automation/index.js +7 -0
  115. package/dist/automation/index.js.map +1 -0
  116. package/dist/automation/longRunningMonitor.d.ts +26 -0
  117. package/dist/automation/longRunningMonitor.d.ts.map +1 -0
  118. package/dist/automation/longRunningMonitor.js +231 -0
  119. package/dist/automation/longRunningMonitor.js.map +1 -0
  120. package/dist/automation/prOwnership.d.ts +18 -0
  121. package/dist/automation/prOwnership.d.ts.map +1 -0
  122. package/dist/automation/prOwnership.js +61 -0
  123. package/dist/automation/prOwnership.js.map +1 -0
  124. package/dist/automation/prProcessor.d.ts +62 -0
  125. package/dist/automation/prProcessor.d.ts.map +1 -0
  126. package/dist/automation/prProcessor.js +700 -0
  127. package/dist/automation/prProcessor.js.map +1 -0
  128. package/dist/automation/runnerExecution.d.ts +49 -0
  129. package/dist/automation/runnerExecution.d.ts.map +1 -0
  130. package/dist/automation/runnerExecution.js +663 -0
  131. package/dist/automation/runnerExecution.js.map +1 -0
  132. package/dist/automation/runnerState.d.ts +170 -0
  133. package/dist/automation/runnerState.d.ts.map +1 -0
  134. package/dist/automation/runnerState.js +495 -0
  135. package/dist/automation/runnerState.js.map +1 -0
  136. package/dist/automation/runnerTypes.d.ts +46 -0
  137. package/dist/automation/runnerTypes.d.ts.map +1 -0
  138. package/dist/automation/runnerTypes.js +5 -0
  139. package/dist/automation/runnerTypes.js.map +1 -0
  140. package/dist/automation/scheduler.d.ts +75 -0
  141. package/dist/automation/scheduler.d.ts.map +1 -0
  142. package/dist/automation/scheduler.js +394 -0
  143. package/dist/automation/scheduler.js.map +1 -0
  144. package/dist/cli/promptHandler.d.ts +13 -0
  145. package/dist/cli/promptHandler.d.ts.map +1 -0
  146. package/dist/cli/promptHandler.js +189 -0
  147. package/dist/cli/promptHandler.js.map +1 -0
  148. package/dist/cli.d.ts +3 -0
  149. package/dist/cli.d.ts.map +1 -0
  150. package/dist/cli.js +138 -0
  151. package/dist/cli.js.map +1 -0
  152. package/dist/core/config.d.ts +308 -0
  153. package/dist/core/config.d.ts.map +1 -0
  154. package/dist/core/config.js +529 -0
  155. package/dist/core/config.js.map +1 -0
  156. package/dist/core/eventHub.d.ts +194 -0
  157. package/dist/core/eventHub.d.ts.map +1 -0
  158. package/dist/core/eventHub.js +136 -0
  159. package/dist/core/eventHub.js.map +1 -0
  160. package/dist/core/index.d.ts +6 -0
  161. package/dist/core/index.d.ts.map +1 -0
  162. package/dist/core/index.js +6 -0
  163. package/dist/core/index.js.map +1 -0
  164. package/dist/core/service.d.ts +27 -0
  165. package/dist/core/service.d.ts.map +1 -0
  166. package/dist/core/service.js +438 -0
  167. package/dist/core/service.js.map +1 -0
  168. package/dist/core/traceCollector.d.ts +105 -0
  169. package/dist/core/traceCollector.d.ts.map +1 -0
  170. package/dist/core/traceCollector.js +141 -0
  171. package/dist/core/traceCollector.js.map +1 -0
  172. package/dist/core/types.d.ts +413 -0
  173. package/dist/core/types.d.ts.map +1 -0
  174. package/dist/core/types.js +5 -0
  175. package/dist/core/types.js.map +1 -0
  176. package/dist/discord/discordCore.d.ts +104 -0
  177. package/dist/discord/discordCore.d.ts.map +1 -0
  178. package/dist/discord/discordCore.js +698 -0
  179. package/dist/discord/discordCore.js.map +1 -0
  180. package/dist/discord/discordHandlers.d.ts +86 -0
  181. package/dist/discord/discordHandlers.d.ts.map +1 -0
  182. package/dist/discord/discordHandlers.js +849 -0
  183. package/dist/discord/discordHandlers.js.map +1 -0
  184. package/dist/discord/discordPair.d.ts +6 -0
  185. package/dist/discord/discordPair.d.ts.map +1 -0
  186. package/dist/discord/discordPair.js +567 -0
  187. package/dist/discord/discordPair.js.map +1 -0
  188. package/dist/discord/index.d.ts +4 -0
  189. package/dist/discord/index.d.ts.map +1 -0
  190. package/dist/discord/index.js +11 -0
  191. package/dist/discord/index.js.map +1 -0
  192. package/dist/github/github.d.ts +236 -0
  193. package/dist/github/github.d.ts.map +1 -0
  194. package/dist/github/github.js +535 -0
  195. package/dist/github/github.js.map +1 -0
  196. package/dist/github/index.d.ts +2 -0
  197. package/dist/github/index.d.ts.map +1 -0
  198. package/dist/github/index.js +2 -0
  199. package/dist/github/index.js.map +1 -0
  200. package/dist/index.d.ts +3 -0
  201. package/dist/index.d.ts.map +1 -0
  202. package/dist/index.js +60 -0
  203. package/dist/index.js.map +1 -0
  204. package/dist/knowledge/analyzer.d.ts +36 -0
  205. package/dist/knowledge/analyzer.d.ts.map +1 -0
  206. package/dist/knowledge/analyzer.js +170 -0
  207. package/dist/knowledge/analyzer.js.map +1 -0
  208. package/dist/knowledge/gitInfo.d.ts +10 -0
  209. package/dist/knowledge/gitInfo.d.ts.map +1 -0
  210. package/dist/knowledge/gitInfo.js +134 -0
  211. package/dist/knowledge/gitInfo.js.map +1 -0
  212. package/dist/knowledge/graph.d.ts +45 -0
  213. package/dist/knowledge/graph.d.ts.map +1 -0
  214. package/dist/knowledge/graph.js +262 -0
  215. package/dist/knowledge/graph.js.map +1 -0
  216. package/dist/knowledge/graphqlExporter.d.ts +64 -0
  217. package/dist/knowledge/graphqlExporter.d.ts.map +1 -0
  218. package/dist/knowledge/graphqlExporter.js +333 -0
  219. package/dist/knowledge/graphqlExporter.js.map +1 -0
  220. package/dist/knowledge/index.d.ts +58 -0
  221. package/dist/knowledge/index.d.ts.map +1 -0
  222. package/dist/knowledge/index.js +212 -0
  223. package/dist/knowledge/index.js.map +1 -0
  224. package/dist/knowledge/repository.d.ts +64 -0
  225. package/dist/knowledge/repository.d.ts.map +1 -0
  226. package/dist/knowledge/repository.js +250 -0
  227. package/dist/knowledge/repository.js.map +1 -0
  228. package/dist/knowledge/riskOnAnalyzer.d.ts +79 -0
  229. package/dist/knowledge/riskOnAnalyzer.d.ts.map +1 -0
  230. package/dist/knowledge/riskOnAnalyzer.js +243 -0
  231. package/dist/knowledge/riskOnAnalyzer.js.map +1 -0
  232. package/dist/knowledge/scanner.d.ts +14 -0
  233. package/dist/knowledge/scanner.d.ts.map +1 -0
  234. package/dist/knowledge/scanner.js +350 -0
  235. package/dist/knowledge/scanner.js.map +1 -0
  236. package/dist/knowledge/store.d.ts +23 -0
  237. package/dist/knowledge/store.d.ts.map +1 -0
  238. package/dist/knowledge/store.js +86 -0
  239. package/dist/knowledge/store.js.map +1 -0
  240. package/dist/knowledge/types.d.ts +288 -0
  241. package/dist/knowledge/types.d.ts.map +1 -0
  242. package/dist/knowledge/types.js +111 -0
  243. package/dist/knowledge/types.js.map +1 -0
  244. package/dist/linear/index.d.ts +3 -0
  245. package/dist/linear/index.d.ts.map +1 -0
  246. package/dist/linear/index.js +3 -0
  247. package/dist/linear/index.js.map +1 -0
  248. package/dist/linear/linear.d.ts +160 -0
  249. package/dist/linear/linear.d.ts.map +1 -0
  250. package/dist/linear/linear.js +983 -0
  251. package/dist/linear/linear.js.map +1 -0
  252. package/dist/linear/projectUpdater.d.ts +23 -0
  253. package/dist/linear/projectUpdater.d.ts.map +1 -0
  254. package/dist/linear/projectUpdater.js +347 -0
  255. package/dist/linear/projectUpdater.js.map +1 -0
  256. package/dist/locale/en.d.ts +3 -0
  257. package/dist/locale/en.d.ts.map +1 -0
  258. package/dist/locale/en.js +436 -0
  259. package/dist/locale/en.js.map +1 -0
  260. package/dist/locale/index.d.ts +28 -0
  261. package/dist/locale/index.d.ts.map +1 -0
  262. package/dist/locale/index.js +89 -0
  263. package/dist/locale/index.js.map +1 -0
  264. package/dist/locale/ko.d.ts +3 -0
  265. package/dist/locale/ko.d.ts.map +1 -0
  266. package/dist/locale/ko.js +436 -0
  267. package/dist/locale/ko.js.map +1 -0
  268. package/dist/locale/prompts/en.d.ts +3 -0
  269. package/dist/locale/prompts/en.d.ts.map +1 -0
  270. package/dist/locale/prompts/en.js +278 -0
  271. package/dist/locale/prompts/en.js.map +1 -0
  272. package/dist/locale/prompts/ko.d.ts +3 -0
  273. package/dist/locale/prompts/ko.d.ts.map +1 -0
  274. package/dist/locale/prompts/ko.js +279 -0
  275. package/dist/locale/prompts/ko.js.map +1 -0
  276. package/dist/locale/types.d.ts +407 -0
  277. package/dist/locale/types.d.ts.map +1 -0
  278. package/dist/locale/types.js +5 -0
  279. package/dist/locale/types.js.map +1 -0
  280. package/dist/memory/codex.d.ts +93 -0
  281. package/dist/memory/codex.d.ts.map +1 -0
  282. package/dist/memory/codex.js +366 -0
  283. package/dist/memory/codex.js.map +1 -0
  284. package/dist/memory/compaction.d.ts +21 -0
  285. package/dist/memory/compaction.d.ts.map +1 -0
  286. package/dist/memory/compaction.js +205 -0
  287. package/dist/memory/compaction.js.map +1 -0
  288. package/dist/memory/index.d.ts +13 -0
  289. package/dist/memory/index.d.ts.map +1 -0
  290. package/dist/memory/index.js +13 -0
  291. package/dist/memory/index.js.map +1 -0
  292. package/dist/memory/memoryCore.d.ts +178 -0
  293. package/dist/memory/memoryCore.d.ts.map +1 -0
  294. package/dist/memory/memoryCore.js +623 -0
  295. package/dist/memory/memoryCore.js.map +1 -0
  296. package/dist/memory/memoryOps.d.ts +90 -0
  297. package/dist/memory/memoryOps.d.ts.map +1 -0
  298. package/dist/memory/memoryOps.js +606 -0
  299. package/dist/memory/memoryOps.js.map +1 -0
  300. package/dist/orchestration/conflictDetector.d.ts +15 -0
  301. package/dist/orchestration/conflictDetector.d.ts.map +1 -0
  302. package/dist/orchestration/conflictDetector.js +130 -0
  303. package/dist/orchestration/conflictDetector.js.map +1 -0
  304. package/dist/orchestration/decisionEngine.d.ts +177 -0
  305. package/dist/orchestration/decisionEngine.d.ts.map +1 -0
  306. package/dist/orchestration/decisionEngine.js +496 -0
  307. package/dist/orchestration/decisionEngine.js.map +1 -0
  308. package/dist/orchestration/index.d.ts +5 -0
  309. package/dist/orchestration/index.d.ts.map +1 -0
  310. package/dist/orchestration/index.js +5 -0
  311. package/dist/orchestration/index.js.map +1 -0
  312. package/dist/orchestration/taskParser.d.ts +67 -0
  313. package/dist/orchestration/taskParser.d.ts.map +1 -0
  314. package/dist/orchestration/taskParser.js +542 -0
  315. package/dist/orchestration/taskParser.js.map +1 -0
  316. package/dist/orchestration/taskScheduler.d.ts +141 -0
  317. package/dist/orchestration/taskScheduler.d.ts.map +1 -0
  318. package/dist/orchestration/taskScheduler.js +317 -0
  319. package/dist/orchestration/taskScheduler.js.map +1 -0
  320. package/dist/orchestration/workflow.d.ts +145 -0
  321. package/dist/orchestration/workflow.d.ts.map +1 -0
  322. package/dist/orchestration/workflow.js +301 -0
  323. package/dist/orchestration/workflow.js.map +1 -0
  324. package/dist/runners/cliRunner.d.ts +11 -0
  325. package/dist/runners/cliRunner.d.ts.map +1 -0
  326. package/dist/runners/cliRunner.js +194 -0
  327. package/dist/runners/cliRunner.js.map +1 -0
  328. package/dist/support/apiCache.d.ts +85 -0
  329. package/dist/support/apiCache.d.ts.map +1 -0
  330. package/dist/support/apiCache.js +163 -0
  331. package/dist/support/apiCache.js.map +1 -0
  332. package/dist/support/chat.d.ts +3 -0
  333. package/dist/support/chat.d.ts.map +1 -0
  334. package/dist/support/chat.js +304 -0
  335. package/dist/support/chat.js.map +1 -0
  336. package/dist/support/chatBackend.d.ts +25 -0
  337. package/dist/support/chatBackend.d.ts.map +1 -0
  338. package/dist/support/chatBackend.js +235 -0
  339. package/dist/support/chatBackend.js.map +1 -0
  340. package/dist/support/chatMemory.d.ts +71 -0
  341. package/dist/support/chatMemory.d.ts.map +1 -0
  342. package/dist/support/chatMemory.js +119 -0
  343. package/dist/support/chatMemory.js.map +1 -0
  344. package/dist/support/chatTui.d.ts +3 -0
  345. package/dist/support/chatTui.d.ts.map +1 -0
  346. package/dist/support/chatTui.js +998 -0
  347. package/dist/support/chatTui.js.map +1 -0
  348. package/dist/support/costTracker.d.ts +29 -0
  349. package/dist/support/costTracker.d.ts.map +1 -0
  350. package/dist/support/costTracker.js +113 -0
  351. package/dist/support/costTracker.js.map +1 -0
  352. package/dist/support/dashboardHtml.d.ts +3 -0
  353. package/dist/support/dashboardHtml.d.ts.map +1 -0
  354. package/dist/support/dashboardHtml.js +2070 -0
  355. package/dist/support/dashboardHtml.js.map +1 -0
  356. package/dist/support/delete-beliefs.d.ts +2 -0
  357. package/dist/support/delete-beliefs.d.ts.map +1 -0
  358. package/dist/support/delete-beliefs.js +34 -0
  359. package/dist/support/delete-beliefs.js.map +1 -0
  360. package/dist/support/dev.d.ts +55 -0
  361. package/dist/support/dev.d.ts.map +1 -0
  362. package/dist/support/dev.js +298 -0
  363. package/dist/support/dev.js.map +1 -0
  364. package/dist/support/editParser.d.ts +37 -0
  365. package/dist/support/editParser.d.ts.map +1 -0
  366. package/dist/support/editParser.js +365 -0
  367. package/dist/support/editParser.js.map +1 -0
  368. package/dist/support/gitStatus.d.ts +21 -0
  369. package/dist/support/gitStatus.d.ts.map +1 -0
  370. package/dist/support/gitStatus.js +108 -0
  371. package/dist/support/gitStatus.js.map +1 -0
  372. package/dist/support/gitTracker.d.ts +30 -0
  373. package/dist/support/gitTracker.d.ts.map +1 -0
  374. package/dist/support/gitTracker.js +143 -0
  375. package/dist/support/gitTracker.js.map +1 -0
  376. package/dist/support/index.d.ts +13 -0
  377. package/dist/support/index.d.ts.map +1 -0
  378. package/dist/support/index.js +13 -0
  379. package/dist/support/index.js.map +1 -0
  380. package/dist/support/planner.d.ts +58 -0
  381. package/dist/support/planner.d.ts.map +1 -0
  382. package/dist/support/planner.js +395 -0
  383. package/dist/support/planner.js.map +1 -0
  384. package/dist/support/projectMapper.d.ts +46 -0
  385. package/dist/support/projectMapper.d.ts.map +1 -0
  386. package/dist/support/projectMapper.js +259 -0
  387. package/dist/support/projectMapper.js.map +1 -0
  388. package/dist/support/quotaTracker.d.ts +29 -0
  389. package/dist/support/quotaTracker.d.ts.map +1 -0
  390. package/dist/support/quotaTracker.js +89 -0
  391. package/dist/support/quotaTracker.js.map +1 -0
  392. package/dist/support/rateLimiter.d.ts +101 -0
  393. package/dist/support/rateLimiter.d.ts.map +1 -0
  394. package/dist/support/rateLimiter.js +219 -0
  395. package/dist/support/rateLimiter.js.map +1 -0
  396. package/dist/support/rollback.d.ts +61 -0
  397. package/dist/support/rollback.d.ts.map +1 -0
  398. package/dist/support/rollback.js +328 -0
  399. package/dist/support/rollback.js.map +1 -0
  400. package/dist/support/stuckDetector.d.ts +68 -0
  401. package/dist/support/stuckDetector.d.ts.map +1 -0
  402. package/dist/support/stuckDetector.js +174 -0
  403. package/dist/support/stuckDetector.js.map +1 -0
  404. package/dist/support/timeWindow.d.ts +60 -0
  405. package/dist/support/timeWindow.d.ts.map +1 -0
  406. package/dist/support/timeWindow.js +236 -0
  407. package/dist/support/timeWindow.js.map +1 -0
  408. package/dist/support/web.d.ts +11 -0
  409. package/dist/support/web.d.ts.map +1 -0
  410. package/dist/support/web.js +938 -0
  411. package/dist/support/web.js.map +1 -0
  412. package/dist/support/workflowLinear.d.ts +99 -0
  413. package/dist/support/workflowLinear.d.ts.map +1 -0
  414. package/dist/support/workflowLinear.js +257 -0
  415. package/dist/support/workflowLinear.js.map +1 -0
  416. package/dist/support/worktreeManager.d.ts +20 -0
  417. package/dist/support/worktreeManager.d.ts.map +1 -0
  418. package/dist/support/worktreeManager.js +144 -0
  419. package/dist/support/worktreeManager.js.map +1 -0
  420. package/dist/taskState/store.d.ts +101 -0
  421. package/dist/taskState/store.d.ts.map +1 -0
  422. package/dist/taskState/store.js +346 -0
  423. package/dist/taskState/store.js.map +1 -0
  424. package/package.json +70 -0
  425. package/templates/AGENTS.md +432 -0
  426. package/templates/BOOT.md +25 -0
  427. package/templates/BOOTSTRAP.md +50 -0
  428. package/templates/CHANGELOG_AUDIT.md +74 -0
  429. package/templates/HEARTBEAT.md +86 -0
  430. package/templates/IDENTITY.md +27 -0
  431. package/templates/ISSUE_ANALYSIS.md +31 -0
  432. package/templates/PR_LAND.md +75 -0
  433. package/templates/PR_REVIEW.md +97 -0
  434. package/templates/SOUL.dev.md +52 -0
  435. package/templates/SOUL.md +81 -0
  436. package/templates/TOOLS.md +52 -0
  437. package/templates/USER.md +22 -0
@@ -0,0 +1,1082 @@
1
+ // OpenSwarm - Autonomous Runner
2
+ // Heartbeat → Decision → Execution → Report
3
+ import { Cron } from 'croner';
4
+ import { loadTaskState, saveTaskState, buildProjectsInfo, appendPipelineHistory, getPipelineHistory, incrementRejection, clearRejection, isRejectionLimitReached, canRetryNow, setRetryTime, clearRetryTime, formatRetryTime, getDailyCompletedCount, getDailyPaceInfo, recordProjectCompletion, canProjectAcceptTask, getProjectWindowCount, } from './runnerState.js';
5
+ import { getDecisionEngine, } from '../orchestration/decisionEngine.js';
6
+ // ExecutorResult used via execution.reportExecutionResult
7
+ import { checkWorkAllowed } from '../support/timeWindow.js';
8
+ import { saveCognitiveMemory } from '../memory/index.js';
9
+ import * as linear from '../linear/index.js';
10
+ import { updateProjectAfterTask } from '../linear/projectUpdater.js';
11
+ import { initScheduler } from '../orchestration/taskScheduler.js';
12
+ import { formatPipelineResultEmbed, } from '../agents/pairPipeline.js';
13
+ import * as planner from '../support/planner.js';
14
+ import * as execution from './runnerExecution.js';
15
+ import { reportToDiscord, fetchLinearTasks } from './runnerExecution.js';
16
+ import { t } from '../locale/index.js';
17
+ import { broadcastEvent } from '../core/eventHub.js';
18
+ import { pruneWorktrees } from '../support/worktreeManager.js';
19
+ import { refreshGraph, toProjectSlug } from '../knowledge/index.js';
20
+ import { checkAllMonitors, getActiveMonitors } from './longRunningMonitor.js';
21
+ import { detectFileConflicts } from '../orchestration/conflictDetector.js';
22
+ import { checkQuotaAllowance } from '../support/quotaTracker.js';
23
+ // Re-export types and integration setters (used by service.ts)
24
+ export { setDiscordReporter, setLinearFetcher } from './runnerExecution.js';
25
+ let runnerInstance = null;
26
+ export class AutonomousRunner {
27
+ config;
28
+ engine;
29
+ scheduler;
30
+ cronJob = null;
31
+ state = {
32
+ isRunning: false,
33
+ lastHeartbeat: 0,
34
+ consecutiveErrors: 0,
35
+ };
36
+ // Heartbeat concurrency guard
37
+ _heartbeatRunning = false;
38
+ // Explicitly enabled project paths (allow-list; empty = nothing runs)
39
+ enabledProjects = new Set();
40
+ /** Check if a resolved path is under any enabled project */
41
+ isProjectEnabled(resolvedPath) {
42
+ if (this.enabledProjects.size === 0)
43
+ return false;
44
+ if (this.enabledProjects.has(resolvedPath))
45
+ return true;
46
+ // Check if resolvedPath is a subdirectory of any enabled project
47
+ for (const enabled of this.enabledProjects) {
48
+ if (resolvedPath.startsWith(enabled + '/'))
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ // Last fetched Linear tasks (for dashboard display)
54
+ lastFetchedTasks = [];
55
+ // Cache: linearProjectName → resolvedLocalPath (populated during task execution)
56
+ projectPathCache = new Map();
57
+ // Turbo mode: faster heartbeat, higher daily cap, no stage skipping
58
+ turboMode = false;
59
+ turboExpiresAt = null;
60
+ static TURBO_DURATION_MS = 4 * 60 * 60 * 1000; // 4 hours auto-expire
61
+ // Track completed/failed task IDs to prevent re-selection (persisted to disk)
62
+ completedTaskIds = new Set();
63
+ failedTaskCounts = new Map();
64
+ failedTaskRetryTimes = new Map(); // issueId → next retry timestamp (ms)
65
+ static MAX_RETRY_COUNT = 4; // Increased from 2 to allow more retries with backoff
66
+ get taskStateRef() {
67
+ return {
68
+ completedTaskIds: this.completedTaskIds,
69
+ failedTaskCounts: this.failedTaskCounts,
70
+ failedTaskRetryTimes: this.failedTaskRetryTimes,
71
+ };
72
+ }
73
+ loadTaskState() {
74
+ loadTaskState(this.taskStateRef);
75
+ }
76
+ saveTaskState() {
77
+ saveTaskState(this.taskStateRef);
78
+ }
79
+ formatTaskContext(task) {
80
+ const parts = [];
81
+ if (task.linearProject?.name)
82
+ parts.push(`[${task.linearProject.name}]`);
83
+ if (task.issueIdentifier)
84
+ parts.push(task.issueIdentifier);
85
+ else if (task.issueId)
86
+ parts.push(task.issueId.slice(0, 8));
87
+ return parts.length > 0 ? parts.join(' ') : '';
88
+ }
89
+ constructor(config) {
90
+ this.config = config;
91
+ this.loadTaskState(); // Restore completed/failed task IDs from disk
92
+ this.engine = getDecisionEngine({
93
+ allowedProjects: config.allowedProjects,
94
+ linearTeamId: config.linearTeamId,
95
+ autoExecute: config.autoExecute,
96
+ maxConsecutiveTasks: config.maxConsecutiveTasks,
97
+ cooldownSeconds: config.cooldownSeconds,
98
+ dryRun: config.dryRun,
99
+ });
100
+ // Initialize TaskScheduler
101
+ this.scheduler = initScheduler({
102
+ maxConcurrent: config.maxConcurrentTasks ?? 1,
103
+ allowSameProjectConcurrent: false,
104
+ worktreeMode: config.worktreeMode ?? false,
105
+ });
106
+ // Set up scheduler event handling
107
+ this.setupSchedulerEvents();
108
+ }
109
+ setupSchedulerEvents() {
110
+ this.scheduler.on('started', async (running) => {
111
+ const taskCtx = this.formatTaskContext(running.task);
112
+ console.log(`[Scheduler] Task started: ${taskCtx} ${running.task.title}`);
113
+ broadcastEvent({ type: 'task:started', data: { taskId: running.task.id, title: running.task.title, issueIdentifier: running.task.issueIdentifier } });
114
+ });
115
+ this.scheduler.on('completed', async ({ task, result }) => {
116
+ const taskCtx = this.formatTaskContext(task);
117
+ console.log(`[Scheduler] Task completed: ${taskCtx} ${task.title}`);
118
+ broadcastEvent({ type: 'task:completed', data: { taskId: task.id, success: result.success, duration: result.totalDuration } });
119
+ this.recordPipelineHistory(task, result);
120
+ await reportToDiscord(formatPipelineResultEmbed(result));
121
+ // Track as completed ONLY on success to prevent re-selection (persist to disk)
122
+ if (task.issueId && result.success) {
123
+ this.completedTaskIds.add(task.issueId);
124
+ clearRejection(task.issueId); // Clear rejection count on success
125
+ clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry backoff time
126
+ this.saveTaskState();
127
+ // Track project-level pace (5h rolling window)
128
+ const projectName = task.linearProject?.name ?? 'unknown';
129
+ recordProjectCompletion(projectName, result.totalCost?.costUsd);
130
+ }
131
+ // Skip completion handling for decomposed tasks. Child issues represent the runnable work.
132
+ if (result.finalStatus === 'decomposed') {
133
+ console.log(`[Scheduler] Task decomposed into sub-issues, skipping Done state`);
134
+ this.scheduleNextHeartbeat();
135
+ return;
136
+ }
137
+ // On success, update Linear issue to Done
138
+ if (result.success && task.issueId) {
139
+ try {
140
+ await execution.syncSuccessState(task);
141
+ await linear.logPairComplete(task.issueId, result.sessionId, {
142
+ attempts: result.iterations,
143
+ duration: Math.floor(result.totalDuration / 1000),
144
+ filesChanged: result.workerResult?.filesChanged || [],
145
+ workerSummary: result.workerResult?.summary,
146
+ workerCommands: result.workerResult?.commands,
147
+ reviewerFeedback: result.reviewResult?.feedback,
148
+ reviewerDecision: result.reviewResult?.decision,
149
+ testResults: result.testerResult ? {
150
+ passed: result.testerResult.testsPassed,
151
+ failed: result.testerResult.testsFailed,
152
+ coverage: result.testerResult.coverage,
153
+ failedTests: result.testerResult.failedTests,
154
+ } : undefined,
155
+ });
156
+ await execution.reconcileCompletionState(task);
157
+ console.log(`[Scheduler] Issue ${task.issueId} marked as Done`);
158
+ }
159
+ catch (err) {
160
+ console.error(`[Scheduler] Failed to update issue state:`, err);
161
+ }
162
+ try {
163
+ await saveCognitiveMemory('strategy', `Pipeline execution succeeded: "${task.title}"`, { confidence: 0.9, derivedFrom: task.issueId });
164
+ }
165
+ catch (memErr) {
166
+ console.warn(`[Scheduler] Memory save failed (non-critical):`, memErr);
167
+ }
168
+ }
169
+ // Linear project Status Update + Overview refresh (non-blocking)
170
+ if (task.linearProject) {
171
+ updateProjectAfterTask(task.linearProject.id, task.linearProject.name, {
172
+ title: task.title,
173
+ success: result.success,
174
+ duration: result.totalDuration,
175
+ issueIdentifier: task.issueIdentifier,
176
+ cost: result.totalCost?.costUsd,
177
+ projectPath: result.taskContext?.projectPath,
178
+ }).catch(e => console.warn('[Scheduler] Project update failed:', e));
179
+ }
180
+ this.scheduleNextHeartbeat();
181
+ });
182
+ this.scheduler.on('failed', async ({ task, result }) => {
183
+ const taskCtx = this.formatTaskContext(task);
184
+ console.log(`[Scheduler] Task failed: ${taskCtx} ${task.title}`);
185
+ broadcastEvent({ type: 'task:completed', data: { taskId: task.id, success: false, duration: result.totalDuration } });
186
+ this.recordPipelineHistory(task, result);
187
+ await reportToDiscord(formatPipelineResultEmbed(result));
188
+ // If rejected, track rejection count and block after max attempts
189
+ if (task.issueId && result.finalStatus === 'rejected') {
190
+ const feedback = result.reviewResult?.feedback || 'No feedback provided';
191
+ const rejectionCount = incrementRejection(task.issueId, feedback);
192
+ console.log(`[Scheduler] Task rejected (${rejectionCount}/3): ${taskCtx} ${task.title}`);
193
+ console.log(`[Scheduler] Rejection reason: ${feedback}`);
194
+ if (isRejectionLimitReached(task.issueId)) {
195
+ // Max rejections reached - permanently block
196
+ this.completedTaskIds.add(task.issueId); // Prevent re-selection
197
+ clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry time
198
+ this.saveTaskState();
199
+ try {
200
+ await execution.syncFailureState(task, `Max rejection limit reached (${rejectionCount} attempts): ${feedback}`);
201
+ await linear.logBlocked(task.issueId, 'autonomous-runner', `⚠️ **Max rejection limit reached (${rejectionCount} attempts)**\n\n` +
202
+ `This task has been rejected ${rejectionCount} times by the reviewer and requires manual intervention.\n\n` +
203
+ `**Latest rejection reason:**\n${feedback}\n\n` +
204
+ `**Action required:** Please review the task requirements and code manually, or adjust the task scope.`);
205
+ console.log(`[Scheduler] Issue ${task.issueId} permanently blocked (max rejections reached)`);
206
+ }
207
+ catch (err) {
208
+ console.error(`[Scheduler] Failed to update issue state:`, err);
209
+ }
210
+ return;
211
+ }
212
+ else {
213
+ // Not max yet - schedule retry with exponential backoff
214
+ const nextRetryTime = setRetryTime(task.issueId, rejectionCount, this.failedTaskRetryTimes);
215
+ const retryIn = formatRetryTime(nextRetryTime);
216
+ this.saveTaskState();
217
+ try {
218
+ await execution.syncFailureState(task, `Review rejected (${rejectionCount}/3): ${feedback}`);
219
+ await linear.logBlocked(task.issueId, 'autonomous-runner', t('runner.reviewRejected', { feedback }) +
220
+ `\n\n**Rejection count:** ${rejectionCount}/3 - Will retry automatically ${retryIn}.`);
221
+ console.log(`[Scheduler] Issue ${task.issueId} marked as Todo (blocked) (rejected ${rejectionCount}/3) — retry ${retryIn}`);
222
+ }
223
+ catch (err) {
224
+ console.error(`[Scheduler] Failed to update issue state:`, err);
225
+ }
226
+ return;
227
+ }
228
+ }
229
+ // Track failure count — block after MAX_RETRY_COUNT failures
230
+ if (task.issueId) {
231
+ const count = (this.failedTaskCounts.get(task.issueId) ?? 0) + 1;
232
+ this.failedTaskCounts.set(task.issueId, count);
233
+ if (count >= AutonomousRunner.MAX_RETRY_COUNT) {
234
+ // Max retries exceeded - permanently block
235
+ this.completedTaskIds.add(task.issueId); // Prevent re-selection
236
+ clearRetryTime(task.issueId, this.failedTaskRetryTimes); // Clear retry time
237
+ this.saveTaskState();
238
+ console.log(`[Scheduler] Task failure count: ${count}/${AutonomousRunner.MAX_RETRY_COUNT} for ${taskCtx} — BLOCKED`);
239
+ try {
240
+ await execution.syncFailureState(task, `Autonomous execution failed ${count} times`);
241
+ await linear.logBlocked(task.issueId, 'autonomous-runner', `Autonomous execution failed ${count} times. Moving to Blocked for manual review.`);
242
+ console.log(`[Scheduler] Issue ${task.issueId} marked as Todo (blocked) (max retries exceeded)`);
243
+ }
244
+ catch (err) {
245
+ console.error(`[Scheduler] Failed to update issue state:`, err);
246
+ }
247
+ }
248
+ else {
249
+ // Schedule retry with exponential backoff
250
+ const nextRetryTime = setRetryTime(task.issueId, count, this.failedTaskRetryTimes);
251
+ const retryIn = formatRetryTime(nextRetryTime);
252
+ console.log(`[Scheduler] Task failure count: ${count}/${AutonomousRunner.MAX_RETRY_COUNT} for ${taskCtx} — retry ${retryIn}`);
253
+ this.saveTaskState();
254
+ }
255
+ }
256
+ // Linear project Status Update + Overview refresh (non-blocking)
257
+ if (task.linearProject) {
258
+ updateProjectAfterTask(task.linearProject.id, task.linearProject.name, {
259
+ title: task.title,
260
+ success: result.success,
261
+ duration: result.totalDuration,
262
+ issueIdentifier: task.issueIdentifier,
263
+ cost: result.totalCost?.costUsd,
264
+ projectPath: result.taskContext?.projectPath,
265
+ }).catch(e => console.warn('[Scheduler] Project update failed:', e));
266
+ }
267
+ this.scheduleNextHeartbeat();
268
+ });
269
+ this.scheduler.on('error', async ({ task, error }) => {
270
+ const taskCtx = this.formatTaskContext(task);
271
+ console.error(`[Scheduler] Task error: ${taskCtx} ${task.title}`, error);
272
+ await reportToDiscord(t('runner.pipelineError', { title: `${taskCtx} ${task.title}`, error: error.message }));
273
+ });
274
+ this.scheduler.on('slotFreed', () => {
275
+ // Auto-execute next task when slot becomes available
276
+ void this.runAvailableTasks();
277
+ });
278
+ }
279
+ filterAlreadyProcessed(tasks) {
280
+ let recovered = 0;
281
+ let backoffSkipped = 0;
282
+ const recoverableStates = new Set(['Todo', 'In Progress', 'In Review']);
283
+ const filtered = tasks.filter(task => {
284
+ const id = task.issueId || task.id;
285
+ // Check rejection limit first
286
+ if (isRejectionLimitReached(id)) {
287
+ return false; // Skip tasks that hit max rejection limit
288
+ }
289
+ // Recover issues in active states from completed/failed list
290
+ // (user or system intentionally moved back to active, so retry)
291
+ if (recoverableStates.has(task.linearState || '') && (this.completedTaskIds.has(id) || (this.failedTaskCounts.get(id) ?? 0) >= AutonomousRunner.MAX_RETRY_COUNT)) {
292
+ this.completedTaskIds.delete(id);
293
+ this.failedTaskCounts.delete(id);
294
+ clearRejection(id); // Clear rejection count on recovery
295
+ clearRetryTime(id, this.failedTaskRetryTimes); // Clear retry backoff time
296
+ recovered++;
297
+ return true;
298
+ }
299
+ if (this.completedTaskIds.has(id))
300
+ return false;
301
+ if ((this.failedTaskCounts.get(id) ?? 0) >= AutonomousRunner.MAX_RETRY_COUNT)
302
+ return false;
303
+ // Check if task is in exponential backoff period
304
+ if (!canRetryNow(id, this.failedTaskRetryTimes)) {
305
+ backoffSkipped++;
306
+ return false; // Skip tasks still in backoff period
307
+ }
308
+ return true;
309
+ });
310
+ if (recovered > 0) {
311
+ this.saveTaskState();
312
+ this.syslog(`♻ Recovered ${recovered} Todo issues from completed/failed/rejected list`);
313
+ }
314
+ if (backoffSkipped > 0) {
315
+ this.syslog(`⏰ Skipped ${backoffSkipped} tasks in exponential backoff period`);
316
+ }
317
+ return filtered;
318
+ }
319
+ /** Schedule next heartbeat with pace-aware cooldown */
320
+ _nextHeartbeatTimer = null;
321
+ scheduleNextHeartbeat() {
322
+ if (this._nextHeartbeatTimer)
323
+ return; // already scheduled
324
+ const isTurbo = this.getTurboMode();
325
+ // Turbo: 5min flat, no progressive slowdown
326
+ if (isTurbo) {
327
+ const turboCooldown = 5 * 60_000; // 5min
328
+ console.log(`[AutonomousRunner] TURBO: next heartbeat in 5min`);
329
+ this._nextHeartbeatTimer = setTimeout(() => {
330
+ this._nextHeartbeatTimer = null;
331
+ void this.heartbeat();
332
+ }, turboCooldown);
333
+ return;
334
+ }
335
+ // Normal: progressive slowdown based on 5h window usage
336
+ const perProjectCap = this.config.dailyTaskCap ?? 6;
337
+ const globalCap = Math.max(this.enabledProjects.size, 3) * perProjectCap;
338
+ const baseCooldown = this.config.interTaskCooldownMs ?? 1_800_000; // 30min default
339
+ const totalInWindow = getDailyCompletedCount();
340
+ // Progressive slowdown: ratio² × 3 multiplier
341
+ const ratio = totalInWindow / globalCap;
342
+ const multiplier = 1 + (ratio * ratio * 3);
343
+ const adjustedCooldown = Math.round(baseCooldown * multiplier);
344
+ const cooldownMin = Math.round(adjustedCooldown / 60_000);
345
+ console.log(`[AutonomousRunner] Scheduling next heartbeat in ${cooldownMin}min (5h window: ${totalInWindow}/${globalCap}, multiplier: ${multiplier.toFixed(2)}x)`);
346
+ this._nextHeartbeatTimer = setTimeout(() => {
347
+ this._nextHeartbeatTimer = null;
348
+ void this.heartbeat();
349
+ }, adjustedCooldown);
350
+ }
351
+ async runAvailableTasks() {
352
+ if (!this.config.pairMode || !this.config.maxConcurrentTasks) {
353
+ return; // Parallel processing disabled
354
+ }
355
+ await this.scheduler.runAvailable(async (task, projectPath) => {
356
+ return this.executePipeline(task, projectPath);
357
+ });
358
+ }
359
+ getRolesForProject(projectPath) {
360
+ // Find per-project configuration
361
+ const projectConfig = this.config.projectAgents?.find(pa => projectPath.includes(pa.projectPath.replace('~', '')));
362
+ if (!projectConfig?.roles && !this.config.defaultRoles) {
363
+ // Convert from legacy configuration
364
+ return {
365
+ worker: {
366
+ enabled: true,
367
+ model: this.config.workerModel || 'claude-sonnet-4-5-20250929',
368
+ timeoutMs: this.config.workerTimeoutMs ?? 0,
369
+ },
370
+ reviewer: {
371
+ enabled: true,
372
+ model: this.config.reviewerModel || 'claude-haiku-4-5-20251001',
373
+ timeoutMs: this.config.reviewerTimeoutMs ?? 0,
374
+ },
375
+ };
376
+ }
377
+ // Apply per-project overrides
378
+ const base = this.config.defaultRoles || {
379
+ worker: { enabled: true, model: 'claude-sonnet-4-5-20250929', timeoutMs: 0 },
380
+ reviewer: { enabled: true, model: 'claude-haiku-4-5-20251001', timeoutMs: 0 },
381
+ };
382
+ if (!projectConfig?.roles) {
383
+ return base;
384
+ }
385
+ // Merge overrides
386
+ return {
387
+ worker: { ...base.worker, ...projectConfig.roles.worker },
388
+ reviewer: { ...base.reviewer, ...projectConfig.roles.reviewer },
389
+ tester: projectConfig.roles.tester
390
+ ? { ...base.tester, ...projectConfig.roles.tester }
391
+ : base.tester,
392
+ documenter: projectConfig.roles.documenter
393
+ ? { ...base.documenter, ...projectConfig.roles.documenter }
394
+ : base.documenter,
395
+ };
396
+ }
397
+ async start() {
398
+ if (this.state.isRunning) {
399
+ console.log('[AutonomousRunner] Already running');
400
+ return;
401
+ }
402
+ await this.engine.init();
403
+ // worktree mode: clean up dangling worktrees at startup
404
+ if (this.config.worktreeMode) {
405
+ for (const projectPath of this.config.allowedProjects) {
406
+ const resolvedPath = projectPath.replace('~', process.env.HOME || '');
407
+ pruneWorktrees(resolvedPath).catch((e) => console.error(`[AutonomousRunner] Worktree prune failed for ${resolvedPath}:`, e));
408
+ }
409
+ }
410
+ // Set up cron job
411
+ this.cronJob = new Cron(this.config.heartbeatSchedule, async () => {
412
+ await this.heartbeat();
413
+ });
414
+ this.state.isRunning = true;
415
+ this.state.startedAt = Date.now();
416
+ console.log(`[AutonomousRunner] Started with schedule: ${this.config.heartbeatSchedule}`);
417
+ await reportToDiscord(`🤖 ${t('runner.modeStarted')}\n` +
418
+ `Schedule: \`${this.config.heartbeatSchedule}\`\n` +
419
+ `Auto-execute: ${this.config.autoExecute ? '✅' : '❌'}\n` +
420
+ `Projects: ${this.config.allowedProjects.join(', ')}`);
421
+ // Immediate execution option
422
+ if (this.config.triggerNow) {
423
+ console.log('[AutonomousRunner] Triggering immediate heartbeat in 10s...');
424
+ setTimeout(() => void this.heartbeat(), 10000); // Run after 10s (wait for Discord/Linear connection)
425
+ }
426
+ }
427
+ stop() {
428
+ if (this.cronJob) {
429
+ this.cronJob.stop();
430
+ this.cronJob = null;
431
+ }
432
+ this.state.isRunning = false;
433
+ console.log('[AutonomousRunner] Stopped');
434
+ }
435
+ buildStats() {
436
+ const stats = this.scheduler.getStats();
437
+ return {
438
+ runningTasks: stats.running,
439
+ queuedTasks: stats.queued,
440
+ completedToday: stats.completed,
441
+ uptime: this.state.startedAt ? Date.now() - this.state.startedAt : 0,
442
+ schedulerPaused: this.scheduler.isPaused(),
443
+ };
444
+ }
445
+ refreshKnowledgeGraphs() {
446
+ for (const projectPath of this.config.allowedProjects) {
447
+ const resolvedPath = projectPath.replace('~', process.env.HOME || '');
448
+ refreshGraph(resolvedPath).then(graph => {
449
+ if (graph) {
450
+ const slug = toProjectSlug(resolvedPath);
451
+ broadcastEvent({
452
+ type: 'knowledge:updated',
453
+ data: { projectSlug: slug, nodeCount: graph.nodeCount, edgeCount: graph.edgeCount },
454
+ });
455
+ }
456
+ }).catch((e) => {
457
+ console.error(`[AutonomousRunner] Knowledge graph refresh failed for ${resolvedPath}:`, e);
458
+ });
459
+ }
460
+ }
461
+ /** Send system message to dashboard LIVE LOG */
462
+ syslog(line) {
463
+ console.log(`[HB] ${line}`);
464
+ broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'heartbeat', line } });
465
+ }
466
+ async heartbeat() {
467
+ if (this._heartbeatRunning) {
468
+ console.log('[AutonomousRunner] Heartbeat already running, skipping');
469
+ return;
470
+ }
471
+ this._heartbeatRunning = true;
472
+ console.log('[AutonomousRunner] Heartbeat triggered');
473
+ this.state.lastHeartbeat = Date.now();
474
+ broadcastEvent({ type: 'stats', data: this.buildStats() });
475
+ broadcastEvent({ type: 'heartbeat' });
476
+ this.syslog('▶ Heartbeat started');
477
+ try {
478
+ // 0. Knowledge graph refresh (async, service continues even on failure)
479
+ this.refreshKnowledgeGraphs();
480
+ // 0.5 Long-running monitor passive check (before time window)
481
+ const active = getActiveMonitors().filter(m => m.state === 'pending' || m.state === 'running');
482
+ if (active.length > 0) {
483
+ const checked = await checkAllMonitors().catch(() => 0);
484
+ this.syslog(`✓ Monitors: ${checked} checked / ${active.length} active`);
485
+ }
486
+ // 1. Check time window
487
+ const timeCheck = checkWorkAllowed();
488
+ if (!timeCheck.allowed) {
489
+ console.log(`[AutonomousRunner] Blocked: ${timeCheck.reason}`);
490
+ this.syslog(`⛔ Time window blocked: ${timeCheck.reason}`);
491
+ return;
492
+ }
493
+ this.syslog('✓ Time window: allowed');
494
+ // 1.5 Quota gate — skip heartbeat if Claude Max quota is too high
495
+ const quotaCheck = await checkQuotaAllowance(80);
496
+ if (!quotaCheck.allowed) {
497
+ console.log(`[AutonomousRunner] Quota gate: SKIP — ${quotaCheck.reason}`);
498
+ broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'quota', line: `⏸ ${quotaCheck.reason}` } });
499
+ return;
500
+ }
501
+ if (quotaCheck.utilization !== undefined && quotaCheck.utilization > 60) {
502
+ console.log(`[AutonomousRunner] Quota warning: ${quotaCheck.utilization.toFixed(0)}% utilization`);
503
+ }
504
+ // 1.6 Pace gate — per-project 5h rolling window
505
+ const isTurbo = this.getTurboMode();
506
+ const perProjectCap = isTurbo ? 20 : (this.config.dailyTaskCap ?? 6);
507
+ const totalInWindow = getDailyCompletedCount();
508
+ // 전역 상한: 프로젝트 수 × per-project cap (안전장치)
509
+ const globalCap = Math.max(this.enabledProjects.size, 3) * perProjectCap;
510
+ if (totalInWindow >= globalCap) {
511
+ console.log(`[AutonomousRunner] Global pace limit: ${totalInWindow}/${globalCap} tasks in 5h window — skipping`);
512
+ this.syslog(`⏸ Global pace: ${totalInWindow}/${globalCap} (5h window)`);
513
+ broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'pace', line: `⏸ Global pace: ${totalInWindow}/${globalCap}` } });
514
+ return;
515
+ }
516
+ const modeLabel = isTurbo ? 'TURBO' : 'Normal';
517
+ this.syslog(`✓ Pace: ${totalInWindow}/${globalCap} global, ${perProjectCap}/project [${modeLabel}]`);
518
+ // 2. Fetch tasks from Linear
519
+ this.syslog('⟳ Fetching tasks from Linear...');
520
+ const fetchResult = await fetchLinearTasks();
521
+ if (fetchResult.error) {
522
+ this.syslog(`✗ Linear fetch error: ${fetchResult.error}`);
523
+ await reportToDiscord(`⚠️ Linear fetch failed: ${fetchResult.error}`);
524
+ return;
525
+ }
526
+ const tasks = fetchResult.tasks;
527
+ if (tasks.length === 0) {
528
+ this.syslog('— No tasks in backlog');
529
+ return;
530
+ }
531
+ this.lastFetchedTasks = tasks;
532
+ this.syslog(`✓ Found ${tasks.length} tasks from Linear`);
533
+ // Filter out completed and over-retried tasks
534
+ const filteredTasks = this.filterAlreadyProcessed(tasks);
535
+ if (filteredTasks.length === 0) {
536
+ this.syslog('— All tasks already completed or max retries exceeded');
537
+ return;
538
+ }
539
+ if (filteredTasks.length !== tasks.length) {
540
+ this.syslog(` Filtered: ${tasks.length} → ${filteredTasks.length} (skipped ${tasks.length - filteredTasks.length} completed/failed)`);
541
+ }
542
+ // Parallel processing mode
543
+ if (this.config.maxConcurrentTasks && this.config.maxConcurrentTasks > 1 && this.config.pairMode) {
544
+ await this.heartbeatParallel(filteredTasks);
545
+ return;
546
+ }
547
+ // 3. Run Decision Engine (single task)
548
+ this.syslog('⟳ Running Decision Engine...');
549
+ const decision = await this.engine.heartbeat(filteredTasks);
550
+ this.syslog(`→ Decision: ${decision.action} — ${decision.reason}`);
551
+ this.state.lastDecision = decision;
552
+ // 4. Handle decision
553
+ if (decision.action === 'execute' && decision.task) {
554
+ await this.executeTaskPairMode(decision.task);
555
+ }
556
+ else if (decision.action === 'defer' && decision.task) {
557
+ this.state.pendingApproval = decision.task;
558
+ await this.requestApproval(decision);
559
+ }
560
+ this.state.consecutiveErrors = 0;
561
+ }
562
+ catch (error) {
563
+ this.state.consecutiveErrors++;
564
+ const msg = error instanceof Error ? error.message : String(error);
565
+ console.error('[AutonomousRunner] Heartbeat error:', msg);
566
+ this.syslog(`✗ Heartbeat error: ${msg}`);
567
+ if (this.state.consecutiveErrors >= 3) {
568
+ await reportToDiscord(t('runner.consecutiveErrors', { count: this.state.consecutiveErrors, error: msg }));
569
+ }
570
+ }
571
+ finally {
572
+ this._heartbeatRunning = false;
573
+ }
574
+ }
575
+ async heartbeatParallel(tasks) {
576
+ const availableSlots = this.scheduler.getAvailableSlots();
577
+ const runningCount = this.scheduler.getStats().running;
578
+ this.syslog(` Parallel mode | slots: ${availableSlots} free / ${this.config.maxConcurrentTasks} max | running: ${runningCount}`);
579
+ if (availableSlots === 0) {
580
+ this.syslog(`⏳ All slots busy (${runningCount} tasks running), waiting...`);
581
+ return;
582
+ }
583
+ // Fill all available slots (worktree mode isolates each task)
584
+ const maxSlots = availableSlots;
585
+ // Pre-filter tasks to enabled projects only (before DecisionEngine selection)
586
+ // This prevents DecisionEngine from wasting its max-slot budget on non-enabled projects.
587
+ // Only execute Todo tasks; Backlog is fetched for dashboard display only
588
+ const executableTasks = tasks.filter(t => t.linearState !== 'Backlog');
589
+ let tasksForEngine = executableTasks;
590
+ if (this.enabledProjects.size > 0) {
591
+ tasksForEngine = executableTasks.filter(task => {
592
+ const projName = task.linearProject?.name;
593
+ if (!projName)
594
+ return false;
595
+ const cachedPath = this.projectPathCache.get(projName)
596
+ ?? this.projectPathCache.get(projName.toLowerCase())
597
+ ?? this.projectPathCache.get(projName.replace(/-/g, ' '));
598
+ if (!cachedPath) {
599
+ this.syslog(` ⚠ No path cache for project "${projName}" — skipping ${task.issueIdentifier}`);
600
+ return false;
601
+ }
602
+ const enabled = this.isProjectEnabled(cachedPath);
603
+ if (!enabled) {
604
+ this.syslog(` ⚠ Project "${projName}" (${cachedPath}) not enabled — skipping ${task.issueIdentifier}`);
605
+ }
606
+ return enabled;
607
+ });
608
+ if (tasksForEngine.length === 0) {
609
+ this.syslog(`⚠ No enabled tasks (${executableTasks.length} executable, ${tasks.length - executableTasks.length} backlog)`);
610
+ this.syslog(` Path cache: [${[...this.projectPathCache.entries()].map(([k, v]) => `${k}→${v}`).join(', ')}]`);
611
+ this.syslog(` Enabled: [${[...this.enabledProjects].join(', ')}]`);
612
+ return;
613
+ }
614
+ this.syslog(` Tasks: ${tasksForEngine.length} enabled-or-uncached / ${executableTasks.length} executable / ${tasks.length} total`);
615
+ }
616
+ // Get validated task list from DecisionEngine
617
+ this.syslog('⟳ Decision Engine evaluating tasks...');
618
+ const decision = await this.engine.heartbeatMultiple(tasksForEngine, maxSlots, [] // No project exclusion — worktree mode isolates each task
619
+ );
620
+ console.log(`[AutonomousRunner] Decision: ${decision.action} — ${decision.reason} (${decision.tasks?.length ?? 0} tasks)`);
621
+ if (decision.action === 'skip' || decision.action === 'defer') {
622
+ this.syslog(`→ Decision: ${decision.action} — ${decision.reason}`);
623
+ return;
624
+ }
625
+ // Add validated tasks to queue (with conflict detection)
626
+ let enqueuedCount = 0;
627
+ // Pre-filter: resolve project paths and skip invalid tasks
628
+ const candidates = [];
629
+ for (const { task } of decision.tasks) {
630
+ if (this.scheduler.isTaskQueued(task.id) || this.scheduler.isTaskRunning(task.id)) {
631
+ this.syslog(` Skip (already queued/running): ${task.issueIdentifier || task.id.slice(0, 8)} ${task.title}`);
632
+ continue;
633
+ }
634
+ const projectPath = await this.resolveProjectPath(task);
635
+ if (!projectPath) {
636
+ this.syslog(`✗ Cannot resolve project path for "${task.linearProject?.name || task.title}" — skipping`);
637
+ continue;
638
+ }
639
+ if (task.linearProject?.name) {
640
+ this.projectPathCache.set(task.linearProject.name, projectPath);
641
+ }
642
+ if (this.scheduler.isProjectBusy(projectPath)) {
643
+ this.syslog(` Project busy: ${projectPath}`);
644
+ continue;
645
+ }
646
+ if (this.enabledProjects.size > 0 && !this.isProjectEnabled(projectPath)) {
647
+ this.syslog(` Project not enabled: ${projectPath}`);
648
+ continue;
649
+ }
650
+ // 프로젝트별 5시간 윈도우 cap 체크
651
+ const projName = task.linearProject?.name ?? 'unknown';
652
+ const perProjectCap = this.config.dailyTaskCap ?? 6;
653
+ if (!canProjectAcceptTask(projName, perProjectCap)) {
654
+ const count = getProjectWindowCount(projName);
655
+ this.syslog(` ⏸ ${projName}: ${count}/${perProjectCap} tasks in 5h window — throttled`);
656
+ continue;
657
+ }
658
+ candidates.push({ task, projectPath });
659
+ }
660
+ // Group candidates by projectPath for conflict detection
661
+ const byProject = new Map();
662
+ for (const c of candidates) {
663
+ const group = byProject.get(c.projectPath) || [];
664
+ group.push(c);
665
+ byProject.set(c.projectPath, group);
666
+ }
667
+ // Detect file conflicts per project using Knowledge Graph
668
+ const safeTasks = new Set(); // task IDs safe to enqueue
669
+ for (const [projPath, group] of byProject) {
670
+ if (group.length <= 1) {
671
+ // 단일 태스크 → 충돌 없음
672
+ for (const c of group)
673
+ safeTasks.add(c.task.id);
674
+ continue;
675
+ }
676
+ try {
677
+ const result = await detectFileConflicts(group.map(c => c.task), projPath);
678
+ for (const t of result.safe) {
679
+ safeTasks.add(t.id);
680
+ }
681
+ for (const cg of result.conflictGroups) {
682
+ const ids = cg.tasks.map(t => t.issueIdentifier || t.id.slice(0, 8)).join(', ');
683
+ this.syslog(`Conflict group: [${ids}] shared: ${cg.sharedModules.join(', ')}`);
684
+ // 충돌 그룹의 연기된 태스크 로그
685
+ for (const t of cg.tasks) {
686
+ if (!safeTasks.has(t.id)) {
687
+ this.syslog(`Conflict detected — deferring: ${t.issueIdentifier || t.id.slice(0, 8)} ${t.title}`);
688
+ }
689
+ }
690
+ }
691
+ }
692
+ catch (err) {
693
+ // KG 분석 실패 시 모든 태스크를 safe로 처리 (graceful degradation)
694
+ console.warn(`[AutonomousRunner] Conflict detection failed for ${projPath}:`, err);
695
+ for (const c of group)
696
+ safeTasks.add(c.task.id);
697
+ }
698
+ }
699
+ // Enqueue safe tasks only
700
+ for (const { task, projectPath } of candidates) {
701
+ if (!safeTasks.has(task.id))
702
+ continue;
703
+ this.scheduler.enqueue(task, projectPath);
704
+ broadcastEvent({ type: 'task:queued', data: { taskId: task.id, title: task.title, projectPath, issueIdentifier: task.issueIdentifier } });
705
+ this.syslog(`✓ Queued: ${task.issueIdentifier || ''} ${task.title} → ${projectPath.split('/').slice(-2).join('/')}`);
706
+ enqueuedCount++;
707
+ // Claim the task immediately: set Linear to 'In Progress' so restarts don't re-queue it
708
+ if (task.issueId) {
709
+ linear.updateIssueState(task.issueId, 'In Progress').catch((err) => console.warn(`[AutonomousRunner] Failed to claim issue ${task.issueIdentifier}:`, err));
710
+ }
711
+ }
712
+ if (enqueuedCount === 0 && decision.skippedCount > 0) {
713
+ this.syslog(`— No new tasks queued (skipped: ${decision.skippedCount})`);
714
+ }
715
+ else {
716
+ this.syslog(`✓ Enqueued ${enqueuedCount} task(s) | skipped: ${decision.skippedCount}`);
717
+ }
718
+ // Execute tasks
719
+ await this.runAvailableTasks();
720
+ }
721
+ /** Execute task in pair mode */
722
+ async executeTaskPairMode(task) {
723
+ // Auto-resolve project path
724
+ const projectPath = await this.resolveProjectPath(task);
725
+ // Error if project path mapping failed
726
+ if (!projectPath) {
727
+ const errorMsg = `Failed to resolve project path for "${task.linearProject?.name || task.title}"`;
728
+ console.error(`[AutonomousRunner] ${errorMsg}`);
729
+ await reportToDiscord(t('runner.projectMappingFailed', { title: task.title, project: task.linearProject?.name || 'unknown' }));
730
+ return;
731
+ }
732
+ // Skip if project is not in enabled list (allow-list; empty = nothing runs)
733
+ if (this.enabledProjects.size > 0 && !this.isProjectEnabled(projectPath)) {
734
+ console.log(`[AutonomousRunner] Project not enabled, skipping: ${projectPath}`);
735
+ return;
736
+ }
737
+ // Cache linearProjectName → resolvedPath for dashboard
738
+ if (task.linearProject?.name) {
739
+ this.projectPathCache.set(task.linearProject.name, projectPath);
740
+ }
741
+ console.log(`[AutonomousRunner] projectPath: ${projectPath}`);
742
+ // Check task decomposition (when enableDecomposition is set)
743
+ if (this.config.enableDecomposition) {
744
+ const threshold = this.config.decompositionThresholdMinutes ?? 30;
745
+ const needsDecomp = planner.needsDecomposition(task, threshold);
746
+ if (needsDecomp) {
747
+ console.log(`[AutonomousRunner] Task "${task.title}" needs decomposition (>${threshold}min estimated)`);
748
+ const decomposed = await this.decomposeTask(task, projectPath, threshold);
749
+ if (decomposed) {
750
+ // Decomposition succeeded - sub-tasks added to queue, skip original task
751
+ return;
752
+ }
753
+ // Decomposition failed - execute original task as-is
754
+ console.log('[AutonomousRunner] Decomposition failed, executing original task');
755
+ }
756
+ }
757
+ // Use scheduler for parallel processing mode
758
+ if (this.config.maxConcurrentTasks && this.config.maxConcurrentTasks > 1) {
759
+ this.scheduler.enqueue(task, projectPath);
760
+ broadcastEvent({ type: 'task:queued', data: { taskId: task.id, title: task.title, projectPath, issueIdentifier: task.issueIdentifier } });
761
+ await this.runAvailableTasks();
762
+ return;
763
+ }
764
+ // Single execution (legacy)
765
+ const result = await this.executePipeline(task, projectPath);
766
+ await reportToDiscord(formatPipelineResultEmbed(result));
767
+ // Update Linear issue state
768
+ if (task.issueId) {
769
+ try {
770
+ if (result.success) {
771
+ // On success, move to Done
772
+ await execution.syncSuccessState(task);
773
+ await linear.logPairComplete(task.issueId, result.sessionId, {
774
+ attempts: result.iterations,
775
+ duration: Math.floor(result.totalDuration / 1000),
776
+ filesChanged: result.workerResult?.filesChanged || [],
777
+ workerSummary: result.workerResult?.summary,
778
+ workerCommands: result.workerResult?.commands,
779
+ reviewerFeedback: result.reviewResult?.feedback,
780
+ reviewerDecision: result.reviewResult?.decision,
781
+ testResults: result.testerResult ? {
782
+ passed: result.testerResult.testsPassed,
783
+ failed: result.testerResult.testsFailed,
784
+ coverage: result.testerResult.coverage,
785
+ failedTests: result.testerResult.failedTests,
786
+ } : undefined,
787
+ });
788
+ await execution.reconcileCompletionState(task);
789
+ console.log(`[AutonomousRunner] Issue ${task.issueId} marked as Done`);
790
+ try {
791
+ await saveCognitiveMemory('strategy', `Pair execution succeeded: "${task.title}"`, { confidence: 0.9, derivedFrom: task.issueId });
792
+ }
793
+ catch (memErr) {
794
+ console.warn(`[AutonomousRunner] Memory save failed (non-critical):`, memErr);
795
+ }
796
+ }
797
+ else if (result.finalStatus === 'rejected') {
798
+ // Change to Blocked on review rejection
799
+ await execution.syncFailureState(task, `Review rejected: ${result.reviewResult?.feedback || t('common.fallback.noDescription')}`);
800
+ await linear.logBlocked(task.issueId, 'autonomous-runner', t('runner.reviewRejected', { feedback: result.reviewResult?.feedback || t('common.fallback.noDescription') }));
801
+ console.log(`[AutonomousRunner] Issue ${task.issueId} marked as Todo (blocked) (rejected)`);
802
+ }
803
+ // If failed, keep In Progress (retry on next heartbeat)
804
+ }
805
+ catch (err) {
806
+ console.error(`[AutonomousRunner] Failed to update issue state:`, err);
807
+ }
808
+ }
809
+ }
810
+ getExecCtx() {
811
+ return {
812
+ allowedProjects: this.config.allowedProjects,
813
+ plannerModel: this.config.plannerModel,
814
+ plannerTimeoutMs: this.config.plannerTimeoutMs,
815
+ pairMaxAttempts: this.config.pairMaxAttempts,
816
+ enableDecomposition: this.config.enableDecomposition,
817
+ decompositionThresholdMinutes: this.config.decompositionThresholdMinutes,
818
+ decompositionMaxDepth: this.config.decomposition?.maxDepth ?? 2,
819
+ decompositionMaxChildren: this.config.decomposition?.maxChildrenPerTask ?? 5,
820
+ decompositionDailyLimit: this.config.decomposition?.dailyLimit ?? 20,
821
+ decompositionAutoBacklog: this.config.decomposition?.autoBacklog ?? true,
822
+ jobProfiles: this.config.jobProfiles,
823
+ getRolesForProject: (p) => this.getRolesForProject(p),
824
+ reportToDiscord,
825
+ worktreeMode: this.config.worktreeMode ?? false,
826
+ scheduleNextHeartbeat: () => this.scheduleNextHeartbeat(),
827
+ guards: this.config.guards,
828
+ };
829
+ }
830
+ async resolveProjectPath(task) {
831
+ return execution.resolveProjectPath(this.getExecCtx(), task);
832
+ }
833
+ async decomposeTask(task, projectPath, targetMinutes) {
834
+ return execution.decomposeTask(this.getExecCtx(), task, projectPath, targetMinutes);
835
+ }
836
+ async executePipeline(task, projectPath) {
837
+ return execution.executePipeline(this.getExecCtx(), task, projectPath);
838
+ }
839
+ async requestApproval(decision) {
840
+ return execution.requestApproval(decision, reportToDiscord);
841
+ }
842
+ async approve() {
843
+ if (!this.state.pendingApproval) {
844
+ return false;
845
+ }
846
+ const task = this.state.pendingApproval;
847
+ this.state.pendingApproval = undefined;
848
+ // Get workflow from Decision Engine
849
+ const decision = await this.engine.heartbeat([task]);
850
+ if (decision.workflow && decision.task) {
851
+ await this.executeTaskPairMode(decision.task);
852
+ return true;
853
+ }
854
+ return false;
855
+ }
856
+ reject() {
857
+ if (!this.state.pendingApproval) {
858
+ return false;
859
+ }
860
+ this.state.pendingApproval = undefined;
861
+ return true;
862
+ }
863
+ async runNow() {
864
+ await this.heartbeat();
865
+ }
866
+ getState() {
867
+ return { ...this.state };
868
+ }
869
+ getAllowedProjects() {
870
+ return this.config.allowedProjects ?? [];
871
+ }
872
+ updateAllowedProjects(paths) {
873
+ this.config.allowedProjects = paths;
874
+ this.engine.updateAllowedProjects(paths);
875
+ }
876
+ getStats() {
877
+ return { isRunning: this.state.isRunning, lastHeartbeat: this.state.lastHeartbeat,
878
+ engineStats: this.engine.getStats(), pendingApproval: !!this.state.pendingApproval,
879
+ schedulerStats: this.scheduler.getStats(),
880
+ turboMode: this.turboMode,
881
+ turboExpiresAt: this.turboExpiresAt,
882
+ dailyPace: getDailyPaceInfo(),
883
+ };
884
+ }
885
+ // ============================================
886
+ // Turbo Mode
887
+ // ============================================
888
+ getTurboMode() {
889
+ // Auto-expire turbo
890
+ if (this.turboMode && this.turboExpiresAt && Date.now() >= this.turboExpiresAt) {
891
+ this.setTurboMode(false);
892
+ }
893
+ return this.turboMode;
894
+ }
895
+ setTurboMode(enabled) {
896
+ this.turboMode = enabled;
897
+ if (enabled) {
898
+ this.turboExpiresAt = Date.now() + AutonomousRunner.TURBO_DURATION_MS;
899
+ const expiresIn = Math.round(AutonomousRunner.TURBO_DURATION_MS / 3_600_000);
900
+ console.log(`[AutonomousRunner] TURBO MODE ON (auto-expires in ${expiresIn}h)`);
901
+ broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'turbo', line: `TURBO ON — expires in ${expiresIn}h` } });
902
+ }
903
+ else {
904
+ this.turboExpiresAt = null;
905
+ console.log('[AutonomousRunner] TURBO MODE OFF');
906
+ broadcastEvent({ type: 'log', data: { taskId: 'system', stage: 'turbo', line: 'TURBO OFF — normal pace resumed' } });
907
+ }
908
+ }
909
+ getAdapterSummary() {
910
+ const defaultAdapter = this.config.defaultAdapter ?? 'claude';
911
+ const defaultRoles = this.config.defaultRoles;
912
+ return {
913
+ defaultAdapter,
914
+ worker: {
915
+ adapter: defaultRoles?.worker?.adapter ?? defaultAdapter,
916
+ model: defaultRoles?.worker?.model ?? this.config.workerModel ?? 'claude-sonnet-4-5-20250929',
917
+ enabled: defaultRoles?.worker?.enabled !== false,
918
+ },
919
+ reviewer: {
920
+ adapter: defaultRoles?.reviewer?.adapter ?? defaultAdapter,
921
+ model: defaultRoles?.reviewer?.model ?? this.config.reviewerModel ?? 'claude-haiku-4-5-20251001',
922
+ enabled: defaultRoles?.reviewer?.enabled !== false,
923
+ },
924
+ tester: defaultRoles?.tester ? {
925
+ adapter: defaultRoles.tester.adapter ?? defaultAdapter,
926
+ model: defaultRoles.tester.model,
927
+ enabled: defaultRoles.tester.enabled !== false,
928
+ } : undefined,
929
+ documenter: defaultRoles?.documenter ? {
930
+ adapter: defaultRoles.documenter.adapter ?? defaultAdapter,
931
+ model: defaultRoles.documenter.model,
932
+ enabled: defaultRoles.documenter.enabled !== false,
933
+ } : undefined,
934
+ };
935
+ }
936
+ switchProvider(adapter) {
937
+ const mapModelForProvider = (model, role) => {
938
+ const current = model || '';
939
+ const isClaudeModel = current.startsWith('claude-');
940
+ // ChatGPT 계정 Codex에서는 gpt-5.x / gpt-*-codex 계열만 지원
941
+ // o-series (o3, o4-mini 등)는 사용 불가
942
+ const isCodexCompatible = current.startsWith('gpt-');
943
+ if (adapter === 'codex') {
944
+ if (isCodexCompatible)
945
+ return current;
946
+ // 비호환 모델(o-series 포함) → 모델 플래그 생략 → Codex 기본값 사용
947
+ return '';
948
+ }
949
+ if (isClaudeModel)
950
+ return current;
951
+ if (role === 'reviewer')
952
+ return 'claude-sonnet-4-20250514';
953
+ return 'claude-haiku-4-5-20251001';
954
+ };
955
+ this.config.defaultAdapter = adapter;
956
+ if (this.config.defaultRoles) {
957
+ this.config.defaultRoles.worker = {
958
+ ...this.config.defaultRoles.worker,
959
+ adapter,
960
+ model: mapModelForProvider(this.config.defaultRoles.worker.model, 'worker'),
961
+ };
962
+ this.config.defaultRoles.reviewer = {
963
+ ...this.config.defaultRoles.reviewer,
964
+ adapter,
965
+ model: mapModelForProvider(this.config.defaultRoles.reviewer.model, 'reviewer'),
966
+ };
967
+ if (this.config.defaultRoles.tester) {
968
+ this.config.defaultRoles.tester = {
969
+ ...this.config.defaultRoles.tester,
970
+ adapter,
971
+ model: mapModelForProvider(this.config.defaultRoles.tester.model, 'tester'),
972
+ };
973
+ }
974
+ if (this.config.defaultRoles.documenter) {
975
+ this.config.defaultRoles.documenter = {
976
+ ...this.config.defaultRoles.documenter,
977
+ adapter,
978
+ model: mapModelForProvider(this.config.defaultRoles.documenter.model, 'documenter'),
979
+ };
980
+ }
981
+ if (this.config.defaultRoles.auditor) {
982
+ this.config.defaultRoles.auditor = {
983
+ ...this.config.defaultRoles.auditor,
984
+ adapter,
985
+ model: mapModelForProvider(this.config.defaultRoles.auditor.model, 'auditor'),
986
+ };
987
+ }
988
+ if (this.config.defaultRoles['skill-documenter']) {
989
+ this.config.defaultRoles['skill-documenter'] = {
990
+ ...this.config.defaultRoles['skill-documenter'],
991
+ adapter,
992
+ model: mapModelForProvider(this.config.defaultRoles['skill-documenter'].model, 'skill-documenter'),
993
+ };
994
+ }
995
+ }
996
+ if (this.config.workerModel) {
997
+ this.config.workerModel = mapModelForProvider(this.config.workerModel, 'worker');
998
+ }
999
+ if (this.config.reviewerModel) {
1000
+ this.config.reviewerModel = mapModelForProvider(this.config.reviewerModel, 'reviewer');
1001
+ }
1002
+ console.log(`[AutonomousRunner] Provider switched: ${adapter}`);
1003
+ }
1004
+ pauseScheduler() { this.scheduler.pause(); }
1005
+ resumeScheduler() { this.scheduler.resume(); }
1006
+ getQueuedTasks() { return this.scheduler.getQueuedTasks(); }
1007
+ getRunningTasks() { return this.scheduler.getRunningTasks(); }
1008
+ getPipelineHistory(limit = 50) { return getPipelineHistory(limit); }
1009
+ recordPipelineHistory(task, result) {
1010
+ appendPipelineHistory({
1011
+ sessionId: result.sessionId, issueIdentifier: task.issueIdentifier || task.issueId,
1012
+ issueId: task.issueId, taskTitle: task.title, projectName: task.linearProject?.name,
1013
+ projectPath: result.taskContext?.projectPath, success: result.success,
1014
+ finalStatus: result.finalStatus, iterations: result.iterations,
1015
+ totalDuration: result.totalDuration,
1016
+ stages: result.stages.map(s => ({ stage: s.stage, success: s.success, duration: s.duration })),
1017
+ cost: result.totalCost ? { costUsd: result.totalCost.costUsd,
1018
+ inputTokens: result.totalCost.inputTokens, outputTokens: result.totalCost.outputTokens } : undefined,
1019
+ prUrl: result.prUrl, reviewerFeedback: result.reviewResult?.feedback,
1020
+ completedAt: new Date().toISOString(),
1021
+ });
1022
+ }
1023
+ disableProject(projectPath) {
1024
+ this.enabledProjects.delete(projectPath);
1025
+ console.log(`[AutonomousRunner] Project disabled: ${projectPath}`);
1026
+ }
1027
+ enableProject(projectPath) {
1028
+ this.enabledProjects.add(projectPath);
1029
+ console.log(`[AutonomousRunner] Project enabled: ${projectPath}`);
1030
+ }
1031
+ /** Get all currently enabled project paths */
1032
+ getEnabledProjects() {
1033
+ return Array.from(this.enabledProjects);
1034
+ }
1035
+ /** Pre-register project path in cache (name → path) */
1036
+ registerProjectPath(name, projectPath) {
1037
+ if (!this.projectPathCache.has(name)) {
1038
+ this.projectPathCache.set(name, projectPath);
1039
+ }
1040
+ // Also register capitalized variant to handle "openswarm" ↔ "Openswarm" mismatch
1041
+ const capitalized = name.charAt(0).toUpperCase() + name.slice(1);
1042
+ if (capitalized !== name && !this.projectPathCache.has(capitalized)) {
1043
+ this.projectPathCache.set(capitalized, projectPath);
1044
+ }
1045
+ }
1046
+ getProjectsInfo() {
1047
+ const running = this.scheduler.getRunningTasks();
1048
+ const queued = this.scheduler.getQueuedTasks();
1049
+ // Update path cache from currently running tasks
1050
+ for (const r of running) {
1051
+ if (r.task.linearProject?.name)
1052
+ this.projectPathCache.set(r.task.linearProject.name, r.projectPath);
1053
+ }
1054
+ return buildProjectsInfo(this.lastFetchedTasks, running, queued, this.projectPathCache, this.enabledProjects);
1055
+ }
1056
+ }
1057
+ export function getRunner(config) {
1058
+ if (!runnerInstance && config) {
1059
+ runnerInstance = new AutonomousRunner(config);
1060
+ }
1061
+ if (!runnerInstance) {
1062
+ throw new Error('Runner not initialized. Call getRunner with config first.');
1063
+ }
1064
+ return runnerInstance;
1065
+ }
1066
+ /**
1067
+ * Start runner (convenience function)
1068
+ */
1069
+ export async function startAutonomous(config) {
1070
+ const runner = getRunner(config);
1071
+ await runner.start();
1072
+ return runner;
1073
+ }
1074
+ /**
1075
+ * Stop runner (convenience function)
1076
+ */
1077
+ export function stopAutonomous() {
1078
+ if (runnerInstance) {
1079
+ runnerInstance.stop();
1080
+ }
1081
+ }
1082
+ //# sourceMappingURL=autonomousRunner.js.map