@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,259 @@
1
+ import Database from 'better-sqlite3';
2
+ import { existsSync, mkdirSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+
5
+ let db: Database.Database | null = null;
6
+
7
+ export function getDb(dbPath: string): Database.Database {
8
+ if (db) return db;
9
+
10
+ const dir = dirname(dbPath);
11
+ if (!existsSync(dir)) {
12
+ mkdirSync(dir, { recursive: true });
13
+ }
14
+
15
+ db = new Database(dbPath);
16
+ db.pragma('journal_mode = WAL');
17
+ db.pragma('foreign_keys = ON');
18
+ initSchema(db);
19
+ return db;
20
+ }
21
+
22
+ function initSchema(db: Database.Database) {
23
+ // Migrations for existing tables (catch duplicate column errors silently)
24
+ const migrate = (sql: string) => {
25
+ try { db.exec(sql); } catch (e: any) {
26
+ if (!String(e.message).includes('duplicate column')) console.error('[db] Migration failed:', sql, e.message);
27
+ }
28
+ };
29
+ migrate('ALTER TABLE tasks ADD COLUMN scheduled_at TEXT');
30
+ migrate("ALTER TABLE tasks ADD COLUMN mode TEXT NOT NULL DEFAULT 'prompt'");
31
+ migrate('ALTER TABLE tasks ADD COLUMN watch_config TEXT');
32
+ migrate("ALTER TABLE skills ADD COLUMN type TEXT NOT NULL DEFAULT 'skill'");
33
+ migrate('ALTER TABLE skills ADD COLUMN archive TEXT');
34
+ migrate("ALTER TABLE skills ADD COLUMN installed_version TEXT NOT NULL DEFAULT ''");
35
+ migrate('ALTER TABLE skills ADD COLUMN rating REAL DEFAULT 0');
36
+ migrate('ALTER TABLE skills ADD COLUMN deleted_remotely INTEGER NOT NULL DEFAULT 0');
37
+ migrate('ALTER TABLE project_pipelines ADD COLUMN last_run_at TEXT');
38
+ migrate('ALTER TABLE pipeline_runs ADD COLUMN dedup_key TEXT');
39
+ migrate("ALTER TABLE tasks ADD COLUMN agent TEXT DEFAULT 'claude'");
40
+ // Recreate token_usage with day column (drop old version if schema changed)
41
+ try { db.exec("SELECT day FROM token_usage LIMIT 1"); } catch { try { db.exec("DROP TABLE IF EXISTS token_usage"); db.exec("DROP TABLE IF EXISTS usage_scan_state"); } catch {} }
42
+ // Unique index for dedup (only applies when dedup_key is NOT NULL)
43
+ try { db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_pipeline_runs_dedup ON pipeline_runs(project_path, workflow_name, dedup_key)'); } catch {}
44
+ // Migrate old issue_autofix_processed → pipeline_runs
45
+ try {
46
+ const old = db.prepare('SELECT * FROM issue_autofix_processed').all() as any[];
47
+ if (old.length > 0) {
48
+ const ins = db.prepare('INSERT OR IGNORE INTO pipeline_runs (id, project_path, workflow_name, pipeline_id, status, dedup_key, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)');
49
+ for (const r of old) {
50
+ ins.run(
51
+ r.pipeline_id?.slice(0, 8) || ('mig-' + r.issue_number),
52
+ r.project_path, 'issue-fix-and-review', r.pipeline_id || '',
53
+ r.status === 'processing' ? 'running' : (r.status || 'done'),
54
+ `issue:${r.issue_number}`, r.created_at || new Date().toISOString()
55
+ );
56
+ }
57
+ console.log(`[db] Migrated ${old.length} issue_autofix_processed records to pipeline_runs`);
58
+ }
59
+ } catch {}
60
+
61
+ db.exec(`
62
+ CREATE TABLE IF NOT EXISTS sessions (
63
+ id TEXT PRIMARY KEY,
64
+ name TEXT NOT NULL,
65
+ template_id TEXT NOT NULL,
66
+ provider TEXT NOT NULL,
67
+ model TEXT NOT NULL,
68
+ status TEXT NOT NULL DEFAULT 'idle',
69
+ memory_config TEXT NOT NULL,
70
+ system_prompt TEXT NOT NULL DEFAULT '',
71
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
72
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
73
+ );
74
+
75
+ CREATE TABLE IF NOT EXISTS messages (
76
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
77
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
78
+ role TEXT NOT NULL,
79
+ content TEXT NOT NULL,
80
+ provider TEXT NOT NULL,
81
+ model TEXT NOT NULL,
82
+ token_count INTEGER DEFAULT 0,
83
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
84
+ );
85
+
86
+ CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, created_at);
87
+
88
+ CREATE TABLE IF NOT EXISTS usage (
89
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
90
+ provider TEXT NOT NULL,
91
+ model TEXT NOT NULL,
92
+ session_id TEXT REFERENCES sessions(id),
93
+ input_tokens INTEGER NOT NULL DEFAULT 0,
94
+ output_tokens INTEGER NOT NULL DEFAULT 0,
95
+ cost REAL NOT NULL DEFAULT 0,
96
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
97
+ );
98
+
99
+ CREATE INDEX IF NOT EXISTS idx_usage_provider ON usage(provider, created_at);
100
+
101
+ CREATE TABLE IF NOT EXISTS tasks (
102
+ id TEXT PRIMARY KEY,
103
+ project_name TEXT NOT NULL,
104
+ project_path TEXT NOT NULL,
105
+ prompt TEXT NOT NULL,
106
+ status TEXT NOT NULL DEFAULT 'queued',
107
+ priority INTEGER NOT NULL DEFAULT 0,
108
+ conversation_id TEXT,
109
+ log TEXT NOT NULL DEFAULT '[]',
110
+ result_summary TEXT,
111
+ git_diff TEXT,
112
+ git_branch TEXT,
113
+ cost_usd REAL,
114
+ error TEXT,
115
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
116
+ started_at TEXT,
117
+ completed_at TEXT,
118
+ scheduled_at TEXT,
119
+ mode TEXT NOT NULL DEFAULT 'prompt',
120
+ watch_config TEXT
121
+ );
122
+
123
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status, created_at);
124
+
125
+ -- Cached Claude CLI sessions for tree view
126
+ CREATE TABLE IF NOT EXISTS cached_sessions (
127
+ project_name TEXT NOT NULL,
128
+ session_id TEXT NOT NULL,
129
+ summary TEXT,
130
+ first_prompt TEXT,
131
+ message_count INTEGER DEFAULT 0,
132
+ entry_count INTEGER DEFAULT 0,
133
+ created TEXT,
134
+ modified TEXT,
135
+ git_branch TEXT,
136
+ file_size INTEGER DEFAULT 0,
137
+ last_synced TEXT NOT NULL DEFAULT (datetime('now')),
138
+ PRIMARY KEY (project_name, session_id)
139
+ );
140
+
141
+ CREATE INDEX IF NOT EXISTS idx_cached_sessions_project ON cached_sessions(project_name, modified);
142
+
143
+ -- In-app notifications
144
+ CREATE TABLE IF NOT EXISTS notifications (
145
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
146
+ type TEXT NOT NULL,
147
+ title TEXT NOT NULL,
148
+ body TEXT,
149
+ read INTEGER NOT NULL DEFAULT 0,
150
+ task_id TEXT,
151
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
152
+ );
153
+
154
+ CREATE INDEX IF NOT EXISTS idx_notifications_read ON notifications(read, created_at);
155
+
156
+ -- Skills registry cache
157
+ CREATE TABLE IF NOT EXISTS skills (
158
+ name TEXT PRIMARY KEY,
159
+ type TEXT NOT NULL DEFAULT 'skill',
160
+ display_name TEXT NOT NULL,
161
+ description TEXT,
162
+ author TEXT,
163
+ version TEXT,
164
+ tags TEXT,
165
+ score INTEGER DEFAULT 0,
166
+ rating REAL DEFAULT 0,
167
+ source_url TEXT,
168
+ archive TEXT,
169
+ installed_global INTEGER NOT NULL DEFAULT 0,
170
+ installed_projects TEXT NOT NULL DEFAULT '[]',
171
+ installed_version TEXT NOT NULL DEFAULT '',
172
+ synced_at TEXT NOT NULL DEFAULT (datetime('now'))
173
+ );
174
+
175
+ -- Tab state (projects, docs, etc)
176
+ CREATE TABLE IF NOT EXISTS tab_state (
177
+ type TEXT PRIMARY KEY,
178
+ data TEXT NOT NULL DEFAULT '{}'
179
+ );
180
+
181
+ -- Project pipeline bindings (which workflows are attached to which projects)
182
+ CREATE TABLE IF NOT EXISTS project_pipelines (
183
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
184
+ project_path TEXT NOT NULL,
185
+ project_name TEXT NOT NULL,
186
+ workflow_name TEXT NOT NULL,
187
+ enabled INTEGER NOT NULL DEFAULT 1,
188
+ config TEXT NOT NULL DEFAULT '{}',
189
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
190
+ UNIQUE(project_path, workflow_name)
191
+ );
192
+
193
+ -- Pipeline execution log (per project, replaces issue_autofix_processed)
194
+ CREATE TABLE IF NOT EXISTS pipeline_runs (
195
+ id TEXT PRIMARY KEY,
196
+ project_path TEXT NOT NULL,
197
+ workflow_name TEXT NOT NULL,
198
+ pipeline_id TEXT NOT NULL,
199
+ status TEXT NOT NULL DEFAULT 'running',
200
+ summary TEXT,
201
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
202
+ );
203
+
204
+ -- Project favorites
205
+ CREATE TABLE IF NOT EXISTS project_favorites (
206
+ project_path TEXT PRIMARY KEY,
207
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
208
+ );
209
+
210
+ -- Session watchers — monitor sessions and notify via Telegram
211
+ CREATE TABLE IF NOT EXISTS session_watchers (
212
+ id TEXT PRIMARY KEY,
213
+ project_name TEXT NOT NULL,
214
+ session_id TEXT,
215
+ label TEXT,
216
+ check_interval INTEGER NOT NULL DEFAULT 60,
217
+ last_entry_count INTEGER DEFAULT 0,
218
+ last_checked TEXT,
219
+ notify_on_change INTEGER NOT NULL DEFAULT 1,
220
+ notify_on_idle INTEGER NOT NULL DEFAULT 1,
221
+ idle_threshold INTEGER NOT NULL DEFAULT 300,
222
+ active INTEGER NOT NULL DEFAULT 1,
223
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
224
+ );
225
+
226
+ -- Token usage tracking (per session + day + model for accurate daily breakdown)
227
+ CREATE TABLE IF NOT EXISTS token_usage (
228
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
229
+ session_id TEXT NOT NULL,
230
+ source TEXT NOT NULL DEFAULT 'terminal',
231
+ project_path TEXT NOT NULL,
232
+ project_name TEXT NOT NULL,
233
+ model TEXT NOT NULL DEFAULT 'unknown',
234
+ day TEXT NOT NULL,
235
+ input_tokens INTEGER NOT NULL DEFAULT 0,
236
+ output_tokens INTEGER NOT NULL DEFAULT 0,
237
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
238
+ cache_create_tokens INTEGER NOT NULL DEFAULT 0,
239
+ cost_usd REAL NOT NULL DEFAULT 0,
240
+ message_count INTEGER NOT NULL DEFAULT 0,
241
+ task_id TEXT,
242
+ UNIQUE(session_id, source, model, day)
243
+ );
244
+
245
+ -- Track scan progress for incremental JSONL scanning
246
+ CREATE TABLE IF NOT EXISTS usage_scan_state (
247
+ file_path TEXT PRIMARY KEY,
248
+ last_size INTEGER NOT NULL DEFAULT 0,
249
+ last_scan TEXT NOT NULL DEFAULT (datetime('now'))
250
+ );
251
+ `);
252
+ }
253
+
254
+ export function closeDb() {
255
+ if (db) {
256
+ db.close();
257
+ db = null;
258
+ }
259
+ }
@@ -0,0 +1,32 @@
1
+ import type { Message, MemoryConfig } from '@/src/types';
2
+
3
+ /**
4
+ * Apply memory strategy to filter/transform messages before sending to AI.
5
+ */
6
+ export function getMemoryMessages(messages: Message[], config: MemoryConfig): Message[] {
7
+ switch (config.strategy) {
8
+ case 'none':
9
+ // Only return the last user message
10
+ return messages.length > 0 ? [messages[messages.length - 1]] : [];
11
+
12
+ case 'sliding_window': {
13
+ const windowSize = config.windowSize || 20;
14
+ return messages.slice(-windowSize);
15
+ }
16
+
17
+ case 'full':
18
+ return messages;
19
+
20
+ case 'full_with_summary':
21
+ // TODO: Implement compression — for now, same as full
22
+ // Future: compress older messages into a summary using summaryModel
23
+ return messages;
24
+
25
+ case 'external':
26
+ // Only return the last user message; full history is in Obsidian
27
+ return messages.length > 0 ? [messages[messages.length - 1]] : [];
28
+
29
+ default:
30
+ return messages;
31
+ }
32
+ }
@@ -0,0 +1,65 @@
1
+ import { streamText, generateText, type ModelMessage } from 'ai';
2
+ import { getModel } from '@/src/core/providers/registry';
3
+ import type { ProviderName } from '@/src/types';
4
+
5
+ export interface ChatOptions {
6
+ provider: ProviderName;
7
+ model?: string;
8
+ systemPrompt?: string;
9
+ messages: ModelMessage[];
10
+ onToken?: (token: string) => void;
11
+ }
12
+
13
+ export interface ChatResult {
14
+ content: string;
15
+ inputTokens: number;
16
+ outputTokens: number;
17
+ model: string;
18
+ provider: ProviderName;
19
+ }
20
+
21
+ export async function chat(options: ChatOptions): Promise<ChatResult> {
22
+ const model = getModel(options.provider, options.model);
23
+ const modelId = options.model || (model as any).modelId || options.model || 'unknown';
24
+
25
+ const result = await generateText({
26
+ model,
27
+ system: options.systemPrompt,
28
+ messages: options.messages,
29
+ });
30
+
31
+ return {
32
+ content: result.text,
33
+ inputTokens: result.usage?.inputTokens ?? 0,
34
+ outputTokens: result.usage?.outputTokens ?? 0,
35
+ model: modelId,
36
+ provider: options.provider,
37
+ };
38
+ }
39
+
40
+ export async function chatStream(options: ChatOptions): Promise<ChatResult> {
41
+ const model = getModel(options.provider, options.model);
42
+ const modelId = options.model || (model as any).modelId || options.model || 'unknown';
43
+
44
+ const result = streamText({
45
+ model,
46
+ system: options.systemPrompt,
47
+ messages: options.messages,
48
+ });
49
+
50
+ let content = '';
51
+ for await (const chunk of result.textStream) {
52
+ content += chunk;
53
+ options.onToken?.(chunk);
54
+ }
55
+
56
+ const usage = await result.usage;
57
+
58
+ return {
59
+ content,
60
+ inputTokens: usage?.inputTokens ?? 0,
61
+ outputTokens: usage?.outputTokens ?? 0,
62
+ model: modelId,
63
+ provider: options.provider,
64
+ };
65
+ }
@@ -0,0 +1,60 @@
1
+ import { createAnthropic } from '@ai-sdk/anthropic';
2
+ import { createGoogleGenerativeAI } from '@ai-sdk/google';
3
+ import { createOpenAI } from '@ai-sdk/openai';
4
+ import type { LanguageModel } from 'ai';
5
+ import { loadConfig, getProviderApiKey } from '@/src/config';
6
+ import type { ProviderName } from '@/src/types';
7
+
8
+ const providerInstances = new Map<string, LanguageModel>();
9
+
10
+ export function getModel(provider: ProviderName, model?: string): LanguageModel {
11
+ const config = loadConfig();
12
+ const providerConfig = config.providers[provider];
13
+ const modelId = model || providerConfig.defaultModel;
14
+ const cacheKey = `${provider}:${modelId}`;
15
+
16
+ const cached = providerInstances.get(cacheKey);
17
+ if (cached) return cached;
18
+
19
+ const apiKey = providerConfig.apiKey || getProviderApiKey(provider);
20
+ const instance = createModel(provider, modelId, apiKey);
21
+ providerInstances.set(cacheKey, instance);
22
+ return instance;
23
+ }
24
+
25
+ function createModel(provider: ProviderName, modelId: string, apiKey?: string): LanguageModel {
26
+ switch (provider) {
27
+ case 'anthropic': {
28
+ const anthropic = createAnthropic({ apiKey });
29
+ return anthropic(modelId);
30
+ }
31
+ case 'google': {
32
+ const google = createGoogleGenerativeAI({ apiKey });
33
+ return google(modelId);
34
+ }
35
+ case 'openai': {
36
+ const openai = createOpenAI({ apiKey });
37
+ return openai(modelId);
38
+ }
39
+ case 'grok': {
40
+ // Grok uses OpenAI-compatible API
41
+ const grok = createOpenAI({
42
+ apiKey,
43
+ baseURL: 'https://api.x.ai/v1',
44
+ });
45
+ return grok(modelId);
46
+ }
47
+ default:
48
+ throw new Error(`Unknown provider: ${provider}`);
49
+ }
50
+ }
51
+
52
+ export function listAvailableProviders(): { name: ProviderName; displayName: string; hasKey: boolean; enabled: boolean }[] {
53
+ const config = loadConfig();
54
+ return Object.values(config.providers).map(p => ({
55
+ name: p.name,
56
+ displayName: p.displayName,
57
+ hasKey: !!(p.apiKey || getProviderApiKey(p.name)),
58
+ enabled: p.enabled,
59
+ }));
60
+ }
@@ -0,0 +1,190 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import type { ModelMessage } from 'ai';
3
+ import { getDb } from '@/src/core/db/database';
4
+ import { getDbPath, loadTemplate } from '@/src/config';
5
+ import { chatStream, type ChatResult } from '@/src/core/providers/chat';
6
+ import { getMemoryMessages } from '@/src/core/memory/strategy';
7
+ import type { Session, SessionStatus, Message, ProviderName, MemoryConfig } from '@/src/types';
8
+
9
+ export class SessionManager {
10
+ private db;
11
+
12
+ constructor() {
13
+ this.db = getDb(getDbPath());
14
+ }
15
+
16
+ create(opts: { name: string; templateId: string; provider?: ProviderName; model?: string }): Session {
17
+ const template = loadTemplate(opts.templateId);
18
+ if (!template) throw new Error(`Template not found: ${opts.templateId}`);
19
+
20
+ const id = randomUUID().slice(0, 8);
21
+ const provider = opts.provider || template.provider;
22
+ const model = opts.model || template.model || '';
23
+ const memoryConfig = JSON.stringify(template.memory);
24
+
25
+ this.db.prepare(`
26
+ INSERT INTO sessions (id, name, template_id, provider, model, status, memory_config, system_prompt)
27
+ VALUES (?, ?, ?, ?, ?, 'idle', ?, ?)
28
+ `).run(id, opts.name, opts.templateId, provider, model, memoryConfig, template.systemPrompt);
29
+
30
+ return this.get(id)!;
31
+ }
32
+
33
+ get(id: string): Session | null {
34
+ const row = this.db.prepare(`
35
+ SELECT s.*, COUNT(m.id) as message_count,
36
+ (SELECT content FROM messages WHERE session_id = s.id ORDER BY created_at DESC LIMIT 1) as last_message
37
+ FROM sessions s
38
+ LEFT JOIN messages m ON m.session_id = s.id
39
+ WHERE s.id = ?
40
+ GROUP BY s.id
41
+ `).get(id) as any;
42
+
43
+ if (!row) return null;
44
+ return this.rowToSession(row);
45
+ }
46
+
47
+ getByName(name: string): Session | null {
48
+ const row = this.db.prepare(`
49
+ SELECT s.*, COUNT(m.id) as message_count,
50
+ (SELECT content FROM messages WHERE session_id = s.id ORDER BY created_at DESC LIMIT 1) as last_message
51
+ FROM sessions s
52
+ LEFT JOIN messages m ON m.session_id = s.id
53
+ WHERE s.name = ?
54
+ GROUP BY s.id
55
+ `).get(name) as any;
56
+
57
+ if (!row) return null;
58
+ return this.rowToSession(row);
59
+ }
60
+
61
+ list(status?: SessionStatus): Session[] {
62
+ let query = `
63
+ SELECT s.*, COUNT(m.id) as message_count,
64
+ (SELECT content FROM messages WHERE session_id = s.id ORDER BY created_at DESC LIMIT 1) as last_message
65
+ FROM sessions s
66
+ LEFT JOIN messages m ON m.session_id = s.id
67
+ `;
68
+ const params: string[] = [];
69
+ if (status) {
70
+ query += ' WHERE s.status = ?';
71
+ params.push(status);
72
+ }
73
+ query += ' GROUP BY s.id ORDER BY s.updated_at DESC';
74
+
75
+ const rows = this.db.prepare(query).all(...params) as any[];
76
+ return rows.map(r => this.rowToSession(r));
77
+ }
78
+
79
+ updateStatus(id: string, status: SessionStatus) {
80
+ this.db.prepare(`UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE id = ?`).run(status, id);
81
+ }
82
+
83
+ async sendMessage(
84
+ sessionId: string,
85
+ userMessage: string,
86
+ onToken?: (token: string) => void
87
+ ): Promise<ChatResult> {
88
+ const session = this.get(sessionId);
89
+ if (!session) throw new Error(`Session not found: ${sessionId}`);
90
+
91
+ // Save user message
92
+ this.addMessage(sessionId, 'user', userMessage, session.provider, session.model);
93
+
94
+ // Get messages based on memory strategy
95
+ const allMessages = this.getMessages(sessionId);
96
+ const memoryMessages = getMemoryMessages(allMessages, session.memory);
97
+
98
+ // Convert to ModelMessage format
99
+ const coreMessages: ModelMessage[] = memoryMessages.map(m => ({
100
+ role: m.role as 'user' | 'assistant',
101
+ content: m.content,
102
+ }));
103
+
104
+ this.updateStatus(sessionId, 'running');
105
+
106
+ try {
107
+ const result = await chatStream({
108
+ provider: session.provider,
109
+ model: session.model || undefined,
110
+ systemPrompt: session.systemPrompt,
111
+ messages: coreMessages,
112
+ onToken,
113
+ });
114
+
115
+ // Save assistant message
116
+ this.addMessage(sessionId, 'assistant', result.content, result.provider, result.model);
117
+
118
+ // Record usage
119
+ this.recordUsage(sessionId, result);
120
+
121
+ this.updateStatus(sessionId, 'idle');
122
+ return result;
123
+ } catch (err) {
124
+ this.updateStatus(sessionId, 'error');
125
+ throw err;
126
+ }
127
+ }
128
+
129
+ addMessage(sessionId: string, role: string, content: string, provider: string, model: string) {
130
+ this.db.prepare(`
131
+ INSERT INTO messages (session_id, role, content, provider, model)
132
+ VALUES (?, ?, ?, ?, ?)
133
+ `).run(sessionId, role, content, provider, model);
134
+
135
+ this.db.prepare(`UPDATE sessions SET updated_at = datetime('now') WHERE id = ?`).run(sessionId);
136
+ }
137
+
138
+ getMessages(sessionId: string, limit?: number): Message[] {
139
+ let query = 'SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC';
140
+ const params: (string | number)[] = [sessionId];
141
+ if (limit) {
142
+ // Get last N messages
143
+ query = `SELECT * FROM (
144
+ SELECT * FROM messages WHERE session_id = ? ORDER BY created_at DESC LIMIT ?
145
+ ) ORDER BY created_at ASC`;
146
+ params.push(limit);
147
+ }
148
+ return this.db.prepare(query).all(...params) as Message[];
149
+ }
150
+
151
+ delete(id: string) {
152
+ this.db.prepare('DELETE FROM sessions WHERE id = ?').run(id);
153
+ }
154
+
155
+ getUsageSummary(): { provider: string; totalInput: number; totalOutput: number; totalCost: number }[] {
156
+ return this.db.prepare(`
157
+ SELECT provider,
158
+ SUM(input_tokens) as totalInput,
159
+ SUM(output_tokens) as totalOutput,
160
+ SUM(cost) as totalCost
161
+ FROM usage
162
+ WHERE created_at >= date('now', '-30 days')
163
+ GROUP BY provider
164
+ `).all() as any[];
165
+ }
166
+
167
+ private recordUsage(sessionId: string, result: ChatResult) {
168
+ this.db.prepare(`
169
+ INSERT INTO usage (provider, model, session_id, input_tokens, output_tokens, cost)
170
+ VALUES (?, ?, ?, ?, ?, ?)
171
+ `).run(result.provider, result.model, sessionId, result.inputTokens, result.outputTokens, 0);
172
+ }
173
+
174
+ private rowToSession(row: any): Session {
175
+ return {
176
+ id: row.id,
177
+ name: row.name,
178
+ templateId: row.template_id,
179
+ provider: row.provider,
180
+ model: row.model,
181
+ status: row.status,
182
+ memory: JSON.parse(row.memory_config),
183
+ systemPrompt: row.system_prompt,
184
+ messageCount: row.message_count || 0,
185
+ createdAt: row.created_at,
186
+ updatedAt: row.updated_at,
187
+ lastMessage: row.last_message || undefined,
188
+ };
189
+ }
190
+ }