@aion0/forge 0.5.26 → 0.5.27

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 (253) hide show
  1. package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
  2. package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
  3. package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
  4. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
  5. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
  6. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
  7. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
  8. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
  9. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
  10. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  11. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  12. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  13. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
  14. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
  15. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
  16. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
  17. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
  18. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
  19. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
  20. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
  21. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
  22. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
  23. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
  24. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
  25. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
  26. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
  27. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
  28. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
  29. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
  30. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
  31. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
  32. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
  33. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
  34. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
  35. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
  36. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
  37. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
  38. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
  39. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
  40. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
  41. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
  42. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
  43. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
  44. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
  45. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
  46. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
  47. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
  48. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
  49. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
  50. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
  51. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
  52. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
  53. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
  54. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
  55. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
  56. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
  57. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
  58. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
  59. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
  60. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
  61. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
  62. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
  63. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
  64. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
  65. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
  66. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
  67. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
  68. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
  69. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
  70. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
  71. package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
  72. package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
  73. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
  74. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
  75. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
  76. package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
  77. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
  78. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
  79. package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
  80. package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
  81. package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
  82. package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
  83. package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
  84. package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
  85. package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
  86. package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
  87. package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
  88. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
  89. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
  90. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
  91. package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
  92. package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
  93. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
  94. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
  95. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
  96. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
  97. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
  98. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
  99. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
  100. package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
  101. package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
  102. package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
  103. package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
  104. package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
  105. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
  106. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
  107. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
  108. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
  109. package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
  110. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
  111. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
  112. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
  113. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
  114. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
  115. package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
  116. package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
  117. package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
  118. package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
  119. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
  120. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
  121. package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
  122. package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
  123. package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
  124. package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
  125. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
  126. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
  127. package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
  128. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
  129. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
  130. package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
  131. package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
  132. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
  133. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
  134. package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
  135. package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
  136. package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
  137. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
  138. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
  139. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
  140. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
  141. package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
  142. package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
  143. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
  144. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
  145. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
  146. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
  147. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
  148. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
  149. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
  150. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
  151. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
  152. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
  153. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
  154. package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
  155. package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
  156. package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
  157. package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
  158. package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
  159. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
  160. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
  161. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
  162. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
  163. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
  164. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
  165. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
  166. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
  167. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
  168. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
  169. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
  170. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
  171. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
  172. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
  173. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
  174. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
  175. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
  176. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
  177. package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
  178. package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
  179. package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
  180. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
  181. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
  182. package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
  183. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
  184. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
  185. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
  186. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
  187. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
  188. package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
  189. package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
  190. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
  191. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
  192. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
  193. package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
  194. package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
  195. package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
  196. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
  197. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
  198. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
  199. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
  200. package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
  201. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  202. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
  203. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
  204. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
  205. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
  206. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
  207. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
  208. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
  209. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
  210. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
  211. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
  212. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
  213. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
  214. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
  215. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
  216. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
  217. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
  218. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
  219. package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
  220. package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
  221. package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
  222. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
  223. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
  224. package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
  225. package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
  226. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
  227. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
  228. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
  229. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  230. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  231. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  232. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
  233. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  234. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  235. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  236. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  237. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
  238. package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
  239. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
  240. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
  241. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
  242. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
  243. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
  244. package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
  245. package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
  246. package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
  247. package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
  248. package/RELEASE_NOTES.md +11 -28
  249. package/app/api/terminal-bell/route.ts +6 -2
  250. package/components/WebTerminal.tsx +36 -2
  251. package/lib/terminal-standalone.ts +19 -2
  252. package/next-env.d.ts +1 -1
  253. package/package.json +1 -1
@@ -0,0 +1,655 @@
1
+ /**
2
+ * AgentWorker — manages the lifecycle of a single workspace agent.
3
+ *
4
+ * Responsibilities:
5
+ * - Multi-step execution with context accumulation
6
+ * - Pause / resume / stop
7
+ * - Bus message injection between steps
8
+ * - Event emission for UI and orchestrator
9
+ */
10
+
11
+ import { EventEmitter } from 'node:events';
12
+ import type {
13
+ WorkspaceAgentConfig,
14
+ AgentState,
15
+ TaskStatus,
16
+ SmithStatus,
17
+ AgentBackend,
18
+ WorkerEvent,
19
+ Artifact,
20
+ BusMessage,
21
+ DaemonWakeReason,
22
+ } from './types';
23
+ import type { TaskLogEntry } from '../../src/types';
24
+
25
+ export interface AgentWorkerOptions {
26
+ config: WorkspaceAgentConfig;
27
+ backend: AgentBackend;
28
+ projectPath: string;
29
+ workspaceId?: string;
30
+ initialTaskStatus?: 'idle' | 'running' | 'done' | 'failed';
31
+ // Bus communication callbacks (injected by orchestrator)
32
+ onBusSend?: (to: string, content: string) => void;
33
+ onBusRequest?: (to: string, question: string) => Promise<string>;
34
+ peerAgentIds?: string[];
35
+ // Message status callback — smith marks its own messages
36
+ onMessageDone?: (messageId: string) => void;
37
+ onMessageFailed?: (messageId: string) => void;
38
+ // Memory (injected by orchestrator)
39
+ memoryContext?: string;
40
+ onMemoryUpdate?: (stepResults: string[]) => void;
41
+ }
42
+
43
+ export class AgentWorker extends EventEmitter {
44
+ readonly config: WorkspaceAgentConfig;
45
+ private state: AgentState;
46
+ private backend: AgentBackend;
47
+ private projectPath: string;
48
+ private workspaceId?: string;
49
+ private busCallbacks: {
50
+ onBusSend?: (to: string, content: string) => void;
51
+ onBusRequest?: (to: string, question: string) => Promise<string>;
52
+ peerAgentIds?: string[];
53
+ };
54
+
55
+ // Control
56
+ private abortController: AbortController | null = null;
57
+ private paused = false;
58
+ private pauseResolve: (() => void) | null = null;
59
+
60
+ // Bus messages queued between steps
61
+ private pendingMessages: TaskLogEntry[] = [];
62
+
63
+ // Memory
64
+ private memoryContext?: string;
65
+ private onMemoryUpdate?: (stepResults: string[]) => void;
66
+ private stepResults: string[] = [];
67
+
68
+ // Daemon mode
69
+ private wakeResolve: ((reason: DaemonWakeReason) => void) | null = null;
70
+ private pendingWake: DaemonWakeReason | null = null;
71
+ private daemonRetryCount = 0;
72
+ private currentMessageId: string | null = null; // ID of the bus message being processed
73
+ private onMessageDone?: (messageId: string) => void;
74
+ private onMessageFailed?: (messageId: string) => void;
75
+
76
+ constructor(opts: AgentWorkerOptions) {
77
+ super();
78
+ this.config = opts.config;
79
+ this.backend = opts.backend;
80
+ this.projectPath = opts.projectPath;
81
+ this.busCallbacks = {
82
+ onBusSend: opts.onBusSend,
83
+ onBusRequest: opts.onBusRequest,
84
+ peerAgentIds: opts.peerAgentIds,
85
+ };
86
+ this.workspaceId = opts.workspaceId;
87
+ this.memoryContext = opts.memoryContext;
88
+ this.onMemoryUpdate = opts.onMemoryUpdate;
89
+ this.onMessageDone = opts.onMessageDone;
90
+ this.onMessageFailed = opts.onMessageFailed;
91
+ this.state = {
92
+ smithStatus: 'down',
93
+ taskStatus: opts.initialTaskStatus || 'idle',
94
+ history: [],
95
+ artifacts: [],
96
+ };
97
+ }
98
+
99
+ // ─── Public API ──────────────────────────────────────
100
+
101
+ /**
102
+ * Execute all steps starting from `startStep`.
103
+ * For recovery, pass `lastCheckpoint + 1`.
104
+ */
105
+ async execute(startStep = 0, upstreamContext?: string): Promise<void> {
106
+ const { steps } = this.config;
107
+ if (steps.length === 0) {
108
+ this.setTaskStatus('done');
109
+ this.emitEvent({ type: 'done', agentId: this.config.id, summary: 'No steps defined' });
110
+ return;
111
+ }
112
+
113
+ // Prepend memory to upstream context
114
+ if (this.memoryContext) {
115
+ upstreamContext = upstreamContext
116
+ ? this.memoryContext + '\n\n---\n\n' + upstreamContext
117
+ : this.memoryContext;
118
+ }
119
+
120
+ this.stepResults = [];
121
+ this.abortController = new AbortController();
122
+ this.setTaskStatus('running');
123
+ this.state.startedAt = Date.now();
124
+
125
+ for (let i = startStep; i < steps.length; i++) {
126
+ // Check pause
127
+ await this.waitIfPaused();
128
+
129
+ // Check abort
130
+ if (this.abortController.signal.aborted) {
131
+ console.log(`[worker] ${this.config.label}: abort detected before step ${i} (signal already aborted)`);
132
+ this.markMessageFailed();
133
+ this.setTaskStatus('failed', 'Interrupted');
134
+ return;
135
+ }
136
+
137
+ const step = steps[i];
138
+ this.state.currentStep = i;
139
+ this.emitEvent({ type: 'step', agentId: this.config.id, stepIndex: i, stepLabel: step.label });
140
+
141
+ // Consume pending bus messages → append to history as context
142
+ if (this.pendingMessages.length > 0) {
143
+ for (const msg of this.pendingMessages) {
144
+ this.state.history.push(msg);
145
+ }
146
+ this.pendingMessages = [];
147
+ }
148
+
149
+ try {
150
+ const result = await this.backend.executeStep({
151
+ config: this.config,
152
+ step,
153
+ stepIndex: i,
154
+ history: this.state.history,
155
+ projectPath: this.projectPath,
156
+ workspaceId: this.workspaceId,
157
+ upstreamContext: i === startStep ? upstreamContext : undefined,
158
+ onBusSend: this.busCallbacks.onBusSend,
159
+ onBusRequest: this.busCallbacks.onBusRequest,
160
+ peerAgentIds: this.busCallbacks.peerAgentIds,
161
+ abortSignal: this.abortController.signal,
162
+ onLog: (entry) => {
163
+ this.state.history.push(entry);
164
+ this.emitEvent({ type: 'log', agentId: this.config.id, entry });
165
+ },
166
+ });
167
+
168
+ // Validate result — detect if the agent hit an error but backend didn't catch it
169
+ const failureCheck = detectStepFailure(result.response);
170
+ if (failureCheck) {
171
+ throw new Error(failureCheck);
172
+ }
173
+
174
+ // Record the assistant's final response for this step
175
+ this.state.history.push({
176
+ type: 'result',
177
+ subtype: 'step_complete',
178
+ content: result.response,
179
+ timestamp: new Date().toISOString(),
180
+ });
181
+
182
+ // Collect artifacts
183
+ for (const artifact of result.artifacts) {
184
+ this.state.artifacts.push(artifact);
185
+ this.emitEvent({ type: 'artifact', agentId: this.config.id, artifact });
186
+ }
187
+
188
+ // Emit step summary (compact, human-friendly)
189
+ const stepSummary = summarizeStepResult(step.label, result.response, result.artifacts);
190
+ this.emitEvent({
191
+ type: 'log', agentId: this.config.id,
192
+ entry: { type: 'system', subtype: 'step_summary', content: stepSummary, timestamp: new Date().toISOString() },
193
+ });
194
+
195
+ // Collect step result for memory update
196
+ this.stepResults.push(result.response);
197
+
198
+ // Checkpoint: this step succeeded
199
+ this.state.lastCheckpoint = i;
200
+
201
+ } catch (err: any) {
202
+ const msg = err?.message || String(err);
203
+ // Aborted = graceful stop (SIGTERM/SIGINT), not an error
204
+ if (msg === 'Aborted' || this.abortController?.signal.aborted) {
205
+ console.log(`[worker] ${this.config.label}: step catch — msg="${msg}", aborted=${this.abortController?.signal.aborted}`);
206
+ this.markMessageFailed();
207
+ this.setTaskStatus('failed', 'Interrupted');
208
+ return;
209
+ }
210
+ this.markMessageFailed();
211
+ this.setTaskStatus('failed', msg);
212
+ this.emitEvent({ type: 'error', agentId: this.config.id, error: this.state.error! });
213
+ return;
214
+ }
215
+ }
216
+
217
+ // All steps done
218
+ this.markMessageDone(); // mark trigger message before emitting done
219
+ this.setTaskStatus('done');
220
+ this.state.completedAt = Date.now();
221
+
222
+ // Trigger memory update (orchestrator handles the actual LLM call)
223
+ if (this.onMemoryUpdate && this.stepResults.length > 0) {
224
+ try { this.onMemoryUpdate(this.stepResults); } catch {}
225
+ }
226
+
227
+ // Emit final summary
228
+ const finalSummary = buildFinalSummary(this.config.label, this.config.steps, this.stepResults, this.state.artifacts);
229
+ this.emitEvent({
230
+ type: 'log', agentId: this.config.id,
231
+ entry: { type: 'result', subtype: 'final_summary', content: finalSummary, timestamp: new Date().toISOString() },
232
+ });
233
+
234
+ const summary = this.state.artifacts.length > 0
235
+ ? `Completed. Artifacts: ${this.state.artifacts.map(a => a.path || a.summary).join(', ')}`
236
+ : 'Completed.';
237
+ this.emitEvent({ type: 'done', agentId: this.config.id, summary });
238
+ }
239
+
240
+ /** Stop execution (abort current step and daemon loop) */
241
+ stop(): void {
242
+ console.log(`[worker] stop() called for ${this.config.label} (task=${this.state.taskStatus}, smith=${this.state.smithStatus})`, new Error().stack?.split('\n').slice(1, 4).join(' <- '));
243
+ this.abortController?.abort();
244
+ this.backend.abort();
245
+ this.setSmithStatus('down');
246
+ // Don't change taskStatus — keep done/failed/idle as-is
247
+ // Only change running → done (graceful stop of an in-progress task)
248
+ if (this.state.taskStatus === 'running') {
249
+ this.setTaskStatus('failed', 'Interrupted');
250
+ }
251
+ // If paused, release the pause wait
252
+ if (this.pauseResolve) {
253
+ this.pauseResolve();
254
+ this.pauseResolve = null;
255
+ }
256
+ // If in daemon wait, wake with abort
257
+ if (this.wakeResolve) {
258
+ this.wakeResolve({ type: 'abort' });
259
+ this.wakeResolve = null;
260
+ }
261
+ }
262
+
263
+ /** Pause after current step completes */
264
+ pause(): void {
265
+ if (this.state.taskStatus !== 'running') return;
266
+ this.paused = true;
267
+ // Paused is a sub-state of running, no separate taskStatus
268
+ }
269
+
270
+ /** Resume from paused state */
271
+ resume(): void {
272
+ if (!this.paused) return;
273
+ this.paused = false;
274
+ if (this.pauseResolve) {
275
+ this.pauseResolve();
276
+ this.pauseResolve = null;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Inject a message from the bus (or human) into the pending queue.
282
+ * Will be consumed at the start of the next step.
283
+ */
284
+ injectMessage(entry: TaskLogEntry): void {
285
+ this.pendingMessages.push(entry);
286
+ }
287
+
288
+ // ─── Daemon Mode ──────────────────────────────────────
289
+
290
+ /**
291
+ * Execute steps then enter daemon loop — agent stays alive waiting for events.
292
+ * Errors do NOT kill the daemon; agent retries with backoff.
293
+ */
294
+ async executeDaemon(startStep = 0, upstreamContext?: string, skipSteps = false): Promise<void> {
295
+ if (!skipSteps) {
296
+ // Run initial steps
297
+ await this.execute(startStep, upstreamContext);
298
+
299
+ // If aborted, don't enter daemon loop
300
+ if (this.abortController?.signal.aborted) return;
301
+ } else {
302
+ // Skip steps — just prepare for daemon loop
303
+ this.abortController = new AbortController();
304
+ }
305
+
306
+ // Enter daemon mode: smith = active, task keeps its status (done/failed/idle)
307
+ this.state.daemonIteration = 0;
308
+ this.daemonRetryCount = 0;
309
+ this.setSmithStatus('active');
310
+
311
+ this.emitEvent({
312
+ type: 'log', agentId: this.config.id,
313
+ entry: { type: 'system', subtype: 'daemon', content: `[Smith] Active — listening for messages`, timestamp: new Date().toISOString() },
314
+ });
315
+
316
+ // Daemon loop — wait for messages, execute, repeat
317
+ while (!this.abortController?.signal.aborted) {
318
+ const reason = await this.waitForWake();
319
+
320
+ if (reason.type === 'abort') break;
321
+
322
+ this.state.daemonIteration = (this.state.daemonIteration || 0) + 1;
323
+ this.daemonRetryCount = 0;
324
+
325
+ try {
326
+ await this.executeDaemonStep(reason);
327
+ this.setTaskStatus('done');
328
+ // Emit done BEFORE markMessageDone — handleAgentDone needs getCurrentMessageId for causedBy
329
+ this.emitEvent({ type: 'done', agentId: this.config.id, summary: `Daemon iteration ${this.state.daemonIteration}` });
330
+ this.markMessageDone();
331
+ } catch (err: any) {
332
+ const msg = err?.message || String(err);
333
+
334
+ // Aborted = graceful stop, exit daemon loop
335
+ if (msg === 'Aborted' || this.abortController?.signal.aborted) break;
336
+
337
+ // Real errors: mark message failed, then backoff
338
+ this.markMessageFailed();
339
+ this.setTaskStatus('failed', msg);
340
+ this.emitEvent({ type: 'error', agentId: this.config.id, error: msg });
341
+ this.emitEvent({
342
+ type: 'log', agentId: this.config.id,
343
+ entry: { type: 'system', subtype: 'daemon', content: `[Smith] Error: ${msg}. Waiting for next event.`, timestamp: new Date().toISOString() },
344
+ });
345
+
346
+ const backoffMs = Math.min(5000 * Math.pow(2, this.daemonRetryCount++), 60_000);
347
+ await this.sleep(backoffMs);
348
+
349
+ if (this.abortController?.signal.aborted) break;
350
+ // Keep failed status — next wake event will set running again
351
+ }
352
+ }
353
+
354
+ // Exiting daemon loop
355
+ this.setSmithStatus('down');
356
+ }
357
+
358
+ /** Wake the daemon from listening state */
359
+ wake(reason: DaemonWakeReason): void {
360
+ if (this.wakeResolve) {
361
+ this.wakeResolve(reason);
362
+ this.wakeResolve = null;
363
+ } else {
364
+ // Worker hasn't entered waitForWake yet — buffer the wake
365
+ this.pendingWake = reason;
366
+ }
367
+ }
368
+
369
+ /** Check if smith is active and idle (ready to receive messages) */
370
+ isListening(): boolean {
371
+ return this.state.smithStatus === 'active' && this.state.taskStatus !== 'running';
372
+ }
373
+
374
+ /** Set the bus message ID being processed — smith marks it done/failed on completion */
375
+ setProcessingMessage(messageId: string): void {
376
+ this.currentMessageId = messageId;
377
+ this.state.currentMessageId = messageId;
378
+ }
379
+
380
+ /** Get the current message ID being processed */
381
+ getCurrentMessageId(): string | null {
382
+ return this.currentMessageId;
383
+ }
384
+
385
+ /** Mark current message as done — keep messageId for causedBy tracing */
386
+ private markMessageDone(): void {
387
+ if (this.currentMessageId && this.onMessageDone) {
388
+ this.onMessageDone(this.currentMessageId);
389
+ }
390
+ // Don't clear currentMessageId — it's needed by handleAgentDone for causedBy.
391
+ // It gets overwritten when next message is set via setProcessingMessage().
392
+ }
393
+
394
+ /** Mark current message as failed — keep messageId for error tracing */
395
+ private markMessageFailed(): void {
396
+ if (this.currentMessageId && this.onMessageFailed) {
397
+ this.onMessageFailed(this.currentMessageId);
398
+ }
399
+ // Don't clear — same reason as markMessageDone.
400
+ }
401
+
402
+ private waitForWake(): Promise<DaemonWakeReason> {
403
+ // Check if a wake was buffered while we were busy
404
+ if (this.pendingWake) {
405
+ const reason = this.pendingWake;
406
+ this.pendingWake = null;
407
+ return Promise.resolve(reason);
408
+ }
409
+ return new Promise<DaemonWakeReason>((resolve) => {
410
+ this.wakeResolve = resolve;
411
+ // Also resolve on abort
412
+ const onAbort = () => resolve({ type: 'abort' });
413
+ this.abortController?.signal.addEventListener('abort', onAbort, { once: true });
414
+ });
415
+ }
416
+
417
+ private async executeDaemonStep(reason: DaemonWakeReason): Promise<void> {
418
+ if (reason.type === 'abort') return;
419
+
420
+ this.setTaskStatus('running');
421
+
422
+ // Build prompt based on wake reason
423
+ let prompt: string;
424
+ switch (reason.type) {
425
+ case 'bus_message':
426
+ prompt = `You received new messages from other agents:\n${reason.messages.map(m => m.content).join('\n')}\n\nAct on these messages. Be concise — don't repeat the message content back. Use tools to inspect files or git history if you need more details.`;
427
+ break;
428
+ case 'upstream_changed':
429
+ prompt = `Your upstream dependency (agent ${reason.agentId}) has produced new output: ${reason.files.join(', ')}.\n\nRe-analyze and update your work based on the new upstream output.`;
430
+ break;
431
+ case 'input_changed':
432
+ prompt = `New requirements have been provided:\n${reason.content}\n\nUpdate your work based on these new requirements.`;
433
+ break;
434
+ case 'user_message':
435
+ prompt = `User message: ${reason.content}\n\nRespond and take action as needed.`;
436
+ break;
437
+ }
438
+
439
+ // Consume any pending bus messages
440
+ const contextMessages = [...this.pendingMessages];
441
+ this.pendingMessages = [];
442
+
443
+ for (const msg of contextMessages) {
444
+ this.state.history.push(msg);
445
+ }
446
+
447
+ // Execute using step template, or create one from the prompt if no steps defined
448
+ const stepTemplate = this.config.steps[this.config.steps.length - 1] || this.config.steps[0];
449
+
450
+ const step = {
451
+ ...(stepTemplate || { id: '', label: '', prompt: '' }),
452
+ id: `daemon-${this.state.daemonIteration}`,
453
+ label: `Daemon iteration ${this.state.daemonIteration}`,
454
+ prompt,
455
+ };
456
+
457
+ this.emitEvent({ type: 'step', agentId: this.config.id, stepIndex: -1, stepLabel: step.label });
458
+
459
+ const result = await this.backend.executeStep({
460
+ config: this.config,
461
+ step,
462
+ stepIndex: -1,
463
+ history: this.state.history,
464
+ projectPath: this.projectPath,
465
+ workspaceId: this.workspaceId,
466
+ onBusSend: this.busCallbacks.onBusSend,
467
+ onBusRequest: this.busCallbacks.onBusRequest,
468
+ peerAgentIds: this.busCallbacks.peerAgentIds,
469
+ abortSignal: this.abortController?.signal,
470
+ onLog: (entry) => {
471
+ this.state.history.push(entry);
472
+ this.emitEvent({ type: 'log', agentId: this.config.id, entry });
473
+ },
474
+ });
475
+
476
+ // Validate result
477
+ const failureCheck = detectStepFailure(result.response);
478
+ if (failureCheck) {
479
+ throw new Error(failureCheck);
480
+ }
481
+
482
+ // Record result
483
+ this.state.history.push({
484
+ type: 'result', subtype: 'daemon_step',
485
+ content: result.response,
486
+ timestamp: new Date().toISOString(),
487
+ });
488
+
489
+ // Collect artifacts
490
+ for (const artifact of result.artifacts) {
491
+ this.state.artifacts.push(artifact);
492
+ this.emitEvent({ type: 'artifact', agentId: this.config.id, artifact });
493
+ }
494
+
495
+ const stepSummary = summarizeStepResult(step.label, result.response, result.artifacts);
496
+ this.emitEvent({
497
+ type: 'log', agentId: this.config.id,
498
+ entry: { type: 'system', subtype: 'step_summary', content: stepSummary, timestamp: new Date().toISOString() },
499
+ });
500
+ }
501
+
502
+ private sleep(ms: number): Promise<void> {
503
+ return new Promise(resolve => setTimeout(resolve, ms));
504
+ }
505
+
506
+ /** Get current state snapshot (immutable copy) */
507
+ getState(): Readonly<AgentState> {
508
+ return { ...this.state, currentMessageId: this.currentMessageId || this.state.currentMessageId };
509
+ }
510
+
511
+ /** Get the config */
512
+ getConfig(): Readonly<WorkspaceAgentConfig> {
513
+ return this.config;
514
+ }
515
+
516
+ // ─── Private ─────────────────────────────────────────
517
+
518
+ private setTaskStatus(taskStatus: TaskStatus, error?: string): void {
519
+ this.state.taskStatus = taskStatus;
520
+ this.state.error = error;
521
+ this.emitEvent({ type: 'task_status', agentId: this.config.id, taskStatus, error });
522
+ }
523
+
524
+ private setSmithStatus(smithStatus: SmithStatus): void {
525
+ this.state.smithStatus = smithStatus;
526
+ this.emitEvent({ type: 'smith_status', agentId: this.config.id, smithStatus });
527
+ }
528
+
529
+ private emitEvent(event: WorkerEvent): void {
530
+ this.emit('event', event);
531
+ }
532
+
533
+ private waitIfPaused(): Promise<void> {
534
+ if (!this.paused) return Promise.resolve();
535
+ return new Promise<void>(resolve => {
536
+ this.pauseResolve = resolve;
537
+ });
538
+ }
539
+ }
540
+
541
+ // ─── Summary helpers (no LLM, pure heuristic) ────────────
542
+
543
+ /** Extract a compact step summary from raw output */
544
+ /**
545
+ * Detect if a step's result indicates failure — covers all agent types:
546
+ * - Rate/usage limits (codex, claude, API)
547
+ * - Auth failures
548
+ * - Subscription limits (claude code)
549
+ * - Empty/meaningless output
550
+ * Returns error message if failure detected, null if OK.
551
+ */
552
+ function detectStepFailure(response: string): string | null {
553
+ if (!response || response.trim().length === 0) {
554
+ return 'Agent produced no output — step may not have executed';
555
+ }
556
+
557
+ const patterns: [RegExp, string][] = [
558
+ // Usage/rate limits
559
+ [/usage limit/i, 'Usage limit reached'],
560
+ [/rate limit/i, 'Rate limit reached'],
561
+ [/hit your.*limit/i, 'Account limit reached'],
562
+ [/upgrade to (plus|pro|max)/i, 'Subscription upgrade required'],
563
+ [/try again (at|in|after)/i, 'Temporarily unavailable — try again later'],
564
+ // Claude Code specific
565
+ [/exceeded.*monthly.*limit/i, 'Monthly usage limit exceeded'],
566
+ [/opus limit|sonnet limit/i, 'Model usage limit reached'],
567
+ [/you've been rate limited/i, 'Rate limited'],
568
+ // API errors
569
+ [/api key.*invalid/i, 'Invalid API key'],
570
+ [/authentication failed/i, 'Authentication failed'],
571
+ [/insufficient.*quota/i, 'Insufficient API quota'],
572
+ [/billing.*not.*active/i, 'Billing not active'],
573
+ [/overloaded|server error|503|502/i, 'Service temporarily unavailable'],
574
+ ];
575
+
576
+ for (const [pattern, msg] of patterns) {
577
+ if (pattern.test(response)) {
578
+ // Extract the actual error line for context
579
+ const errorLine = response.split('\n').find(l => pattern.test(l))?.trim();
580
+ return `${msg}${errorLine ? ': ' + errorLine.slice(0, 150) : ''}`;
581
+ }
582
+ }
583
+
584
+ // Check for very short output that's just noise (spinner artifacts, etc.)
585
+ const meaningful = response.replace(/[^a-zA-Z0-9\u4e00-\u9fff]/g, '').trim();
586
+ if (meaningful.length < 10 && response.length > 50) {
587
+ return 'Agent output appears to be only terminal noise — execution may have failed';
588
+ }
589
+
590
+ return null;
591
+ }
592
+
593
+ function summarizeStepResult(stepLabel: string, rawResult: string, artifacts: { path?: string; summary?: string }[]): string {
594
+ const lines: string[] = [];
595
+ lines.push(`✅ Step "${stepLabel}" done`);
596
+
597
+ // Extract key sentences (first meaningful line, skip noise)
598
+ const meaningful = rawResult
599
+ .split('\n')
600
+ .map(l => l.trim())
601
+ .filter(l => l.length > 15 && l.length < 300)
602
+ .filter(l => !/^[#\-*>|`]/.test(l)) // skip markdown headers, bullets, code blocks
603
+ .filter(l => !/^(Working|W$|Wo$|•)/.test(l)); // skip codex noise
604
+
605
+ if (meaningful.length > 0) {
606
+ lines.push(` ${meaningful[0].slice(0, 120)}`);
607
+ }
608
+
609
+ // List artifacts
610
+ const filePaths = artifacts.filter(a => a.path).map(a => a.path!);
611
+ if (filePaths.length > 0) {
612
+ lines.push(` Files: ${filePaths.join(', ')}`);
613
+ }
614
+
615
+ return lines.join('\n');
616
+ }
617
+
618
+ /** Build a final summary after all steps complete */
619
+ function buildFinalSummary(
620
+ agentLabel: string,
621
+ steps: { label: string }[],
622
+ stepResults: string[],
623
+ artifacts: { path?: string; summary?: string }[],
624
+ ): string {
625
+ const lines: string[] = [];
626
+ lines.push(`══════════════════════════════════════`);
627
+ lines.push(`📊 ${agentLabel} — Summary`);
628
+ lines.push(`──────────────────────────────────────`);
629
+
630
+ // Steps completed
631
+ lines.push(`Steps: ${steps.map(s => s.label).join(' → ')}`);
632
+
633
+ // Key output per step (one line each)
634
+ for (let i = 0; i < steps.length; i++) {
635
+ const result = stepResults[i] || '';
636
+ const firstLine = result
637
+ .split('\n')
638
+ .map(l => l.trim())
639
+ .filter(l => l.length > 15 && l.length < 200)
640
+ .filter(l => !/^[#\-*>|`]/.test(l))
641
+ .filter(l => !/^(Working|W$|Wo$|•)/.test(l))[0];
642
+ if (firstLine) {
643
+ lines.push(` ${steps[i].label}: ${firstLine.slice(0, 100)}`);
644
+ }
645
+ }
646
+
647
+ // All artifacts
648
+ const files = artifacts.filter(a => a.path).map(a => a.path!);
649
+ if (files.length > 0) {
650
+ lines.push(`Produced: ${files.join(', ')}`);
651
+ }
652
+
653
+ lines.push(`══════════════════════════════════════`);
654
+ return lines.join('\n');
655
+ }