upfynai-code 3.0.3 → 3.1.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 (243) hide show
  1. package/README.md +66 -91
  2. package/bin/cli.js +191 -0
  3. package/{client/dist → dist/client}/api-docs.html +838 -838
  4. package/{client/dist/assets/AppContent-Bvg0CPCO.js → dist/client/assets/AppContent-BofJquUs.js} +43 -43
  5. package/dist/client/assets/BrowserPanel-CSvD4jOX.js +2 -0
  6. package/dist/client/assets/CanvasFullScreen-onRfarpc.js +1 -0
  7. package/dist/client/assets/CanvasWorkspace-DvGKdL-k.js +259 -0
  8. package/dist/client/assets/DashboardPanel-DqAHbXDO.js +1 -0
  9. package/dist/client/assets/FileTree-BE0h-9M9.js +1 -0
  10. package/{client/dist/assets/GitPanel-RtyZUIWS.js → dist/client/assets/GitPanel-DdeJ0bp5.js} +2 -2
  11. package/{client/dist/assets/LoginModal-BWep8a6g.js → dist/client/assets/LoginModal-BP0pCTrH.js} +3 -3
  12. package/{client/dist/assets/MarkdownPreview-DHmk3qzu.js → dist/client/assets/MarkdownPreview-CESjI261.js} +1 -1
  13. package/dist/client/assets/MermaidBlock-D0rfEhrT.js +2 -0
  14. package/dist/client/assets/Onboarding-B2zQy-_6.js +1 -0
  15. package/dist/client/assets/SetupForm-Be7-WBe-.js +1 -0
  16. package/dist/client/assets/WorkflowsPanel-CusLbVJ6.js +1 -0
  17. package/{client/dist/assets/index-C5ptjuTl.js → dist/client/assets/index-BQy15irW.js} +25 -25
  18. package/dist/client/assets/index-CS0fDqEC.js +1 -0
  19. package/dist/client/assets/index-DYLSCCCp.css +1 -0
  20. package/dist/client/assets/vendor-canvas-QWTduIvM.js +23 -0
  21. package/{client/dist/assets/vendor-codemirror-CbtmxxaB.js → dist/client/assets/vendor-codemirror-D2ALgpaX.js} +1 -1
  22. package/{client/dist/assets/vendor-icons-BaD0x9SL.js → dist/client/assets/vendor-icons-kix3Gb31.js} +178 -138
  23. package/{client/dist/assets/vendor-mermaid-CH7SGc99.js → dist/client/assets/vendor-mermaid-CS3J4_Bz.js} +329 -326
  24. package/{client/dist/assets/vendor-syntax-DuHI9Ok6.js → dist/client/assets/vendor-syntax-LS_Nt30I.js} +1 -1
  25. package/{client/dist → dist/client}/clear-cache.html +85 -85
  26. package/dist/client/favicon.png +0 -0
  27. package/dist/client/favicon.svg +15 -0
  28. package/{client/dist → dist/client}/index.html +17 -17
  29. package/{client/dist → dist/client}/manifest.json +15 -15
  30. package/{client/dist → dist/client}/mcp-docs.html +108 -108
  31. package/{client/dist → dist/client}/offline.html +84 -84
  32. package/{client/dist → dist/client}/sw.js +82 -82
  33. package/package.json +55 -104
  34. package/scripts/postinstall.js +9 -0
  35. package/scripts/prepublish.js +77 -0
  36. package/src/animation.js +228 -0
  37. package/src/auth.js +142 -0
  38. package/src/config.js +40 -0
  39. package/src/connect.js +416 -0
  40. package/src/launch.js +81 -0
  41. package/src/mcp.js +57 -0
  42. package/src/permissions.js +140 -0
  43. package/src/persistent-shell.js +261 -0
  44. package/src/server.js +54 -0
  45. package/client/dist/assets/CanvasFullScreen-BdiJ35aq.js +0 -1
  46. package/client/dist/assets/CanvasWorkspace-Bk9R9_e0.js +0 -163
  47. package/client/dist/assets/DashboardPanel-CblJfTGi.js +0 -1
  48. package/client/dist/assets/FileTree-BDUnBheV.js +0 -1
  49. package/client/dist/assets/MermaidBlock-BuBc_G-F.js +0 -2
  50. package/client/dist/assets/Onboarding-Drnlt75a.js +0 -1
  51. package/client/dist/assets/SetupForm-CtCKitZG.js +0 -1
  52. package/client/dist/assets/WorkflowsPanel-B2mIXDvD.js +0 -1
  53. package/client/dist/assets/index-BFuqS0tY.css +0 -1
  54. package/client/dist/assets/vendor-canvas-D39yWul6.js +0 -49
  55. package/client/dist/favicon.png +0 -0
  56. package/client/dist/favicon.svg +0 -5
  57. package/commands/upfynai-connect.md +0 -59
  58. package/commands/upfynai-disconnect.md +0 -31
  59. package/commands/upfynai-doctor.md +0 -99
  60. package/commands/upfynai-export.md +0 -49
  61. package/commands/upfynai-local.md +0 -82
  62. package/commands/upfynai-status.md +0 -75
  63. package/commands/upfynai-stop.md +0 -49
  64. package/commands/upfynai-uninstall.md +0 -58
  65. package/commands/upfynai.md +0 -69
  66. package/scripts/build-client.js +0 -17
  67. package/scripts/fix-node-pty.js +0 -67
  68. package/scripts/install-commands.js +0 -78
  69. package/server/agent-loop.js +0 -242
  70. package/server/auto-compact.js +0 -99
  71. package/server/claude-sdk.js +0 -797
  72. package/server/cli-ui.js +0 -798
  73. package/server/cli.js +0 -751
  74. package/server/constants/config.js +0 -31
  75. package/server/cursor-cli.js +0 -270
  76. package/server/database/auth.db +0 -0
  77. package/server/database/db.js +0 -1451
  78. package/server/database/init.sql +0 -70
  79. package/server/index.js +0 -3814
  80. package/server/load-env.js +0 -26
  81. package/server/mcp-server.js +0 -621
  82. package/server/middleware/auth.js +0 -181
  83. package/server/middleware/relayHelpers.js +0 -44
  84. package/server/middleware/sandboxRouter.js +0 -174
  85. package/server/openai-codex.js +0 -403
  86. package/server/openrouter.js +0 -137
  87. package/server/projects.js +0 -1807
  88. package/server/provider-factory.js +0 -174
  89. package/server/relay-client.js +0 -390
  90. package/server/routes/agent.js +0 -1234
  91. package/server/routes/auth.js +0 -559
  92. package/server/routes/canvas.js +0 -53
  93. package/server/routes/cli-auth.js +0 -263
  94. package/server/routes/codex.js +0 -396
  95. package/server/routes/commands.js +0 -707
  96. package/server/routes/composio.js +0 -176
  97. package/server/routes/cursor.js +0 -770
  98. package/server/routes/dashboard.js +0 -295
  99. package/server/routes/git.js +0 -1208
  100. package/server/routes/keys.js +0 -34
  101. package/server/routes/mcp-utils.js +0 -48
  102. package/server/routes/mcp.js +0 -661
  103. package/server/routes/payments.js +0 -227
  104. package/server/routes/projects.js +0 -655
  105. package/server/routes/sessions.js +0 -146
  106. package/server/routes/settings.js +0 -261
  107. package/server/routes/taskmaster.js +0 -1928
  108. package/server/routes/user.js +0 -106
  109. package/server/routes/vapi-chat.js +0 -624
  110. package/server/routes/voice.js +0 -235
  111. package/server/routes/webhooks.js +0 -166
  112. package/server/routes/workflows.js +0 -312
  113. package/server/sandbox.js +0 -120
  114. package/server/services/composio.js +0 -204
  115. package/server/services/sessionRegistry.js +0 -139
  116. package/server/services/whisperService.js +0 -84
  117. package/server/services/workflowScheduler.js +0 -211
  118. package/server/tests/relay-flow.test.js +0 -570
  119. package/server/tests/sessions.test.js +0 -259
  120. package/server/utils/commandParser.js +0 -303
  121. package/server/utils/email.js +0 -66
  122. package/server/utils/gitConfig.js +0 -24
  123. package/server/utils/mcp-detector.js +0 -198
  124. package/server/utils/taskmaster-websocket.js +0 -129
  125. package/shared/integrationCatalog.d.ts +0 -12
  126. package/shared/integrationCatalog.js +0 -172
  127. package/shared/modelConstants.js +0 -96
  128. /package/{shared → dist}/agents/claude.js +0 -0
  129. /package/{shared → dist}/agents/codex.js +0 -0
  130. /package/{shared → dist}/agents/cursor.js +0 -0
  131. /package/{shared → dist}/agents/detect.js +0 -0
  132. /package/{shared → dist}/agents/exec.js +0 -0
  133. /package/{shared → dist}/agents/files.js +0 -0
  134. /package/{shared → dist}/agents/git.js +0 -0
  135. /package/{shared → dist}/agents/gitagent.js +0 -0
  136. /package/{shared → dist}/agents/index.js +0 -0
  137. /package/{shared → dist}/agents/shell.js +0 -0
  138. /package/{shared → dist}/agents/utils.js +0 -0
  139. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  140. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  141. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  142. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  143. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  144. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  145. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  146. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  147. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  148. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  149. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  150. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  151. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  152. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  153. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  154. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  155. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  156. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  157. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  158. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  159. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  160. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  161. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  162. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  163. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  164. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  165. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  166. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  167. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  168. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  169. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  170. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  171. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  172. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  173. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  174. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  175. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  176. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  177. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  178. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  179. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  180. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  181. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  182. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  183. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  184. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  185. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  186. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  187. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  188. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  189. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  190. /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  191. /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  192. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  193. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  194. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  195. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  196. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  197. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  198. /package/{client/dist → dist/client}/assets/PreviewPanel-CqCa92Tf.js +0 -0
  199. /package/{client/dist → dist/client}/assets/pdf-CE_K4jFx.js +0 -0
  200. /package/{client/dist → dist/client}/assets/vendor-canvas-BZV40eAE.css +0 -0
  201. /package/{client/dist → dist/client}/assets/vendor-diff-DNQpbhrT.js +0 -0
  202. /package/{client/dist → dist/client}/assets/vendor-i18n-DCFGyhQR.js +0 -0
  203. /package/{client/dist → dist/client}/assets/vendor-markdown-CimbIo6Y.js +0 -0
  204. /package/{client/dist → dist/client}/assets/vendor-react-96lCPsRK.js +0 -0
  205. /package/{client/dist → dist/client}/assets/vendor-xterm-CZq1hqo1.js +0 -0
  206. /package/{client/dist → dist/client}/assets/vendor-xterm-qxJ8_QYu.css +0 -0
  207. /package/{client/dist → dist/client}/convert-icons.md +0 -0
  208. /package/{client/dist → dist/client}/generate-icons.js +0 -0
  209. /package/{client/dist → dist/client}/icons/claude-ai-icon.svg +0 -0
  210. /package/{client/dist → dist/client}/icons/codex-white.svg +0 -0
  211. /package/{client/dist → dist/client}/icons/codex.svg +0 -0
  212. /package/{client/dist → dist/client}/icons/cursor-white.svg +0 -0
  213. /package/{client/dist → dist/client}/icons/cursor.svg +0 -0
  214. /package/{client/dist → dist/client}/icons/icon-128x128.png +0 -0
  215. /package/{client/dist → dist/client}/icons/icon-128x128.svg +0 -0
  216. /package/{client/dist → dist/client}/icons/icon-144x144.png +0 -0
  217. /package/{client/dist → dist/client}/icons/icon-144x144.svg +0 -0
  218. /package/{client/dist → dist/client}/icons/icon-152x152.png +0 -0
  219. /package/{client/dist → dist/client}/icons/icon-152x152.svg +0 -0
  220. /package/{client/dist → dist/client}/icons/icon-192x192.png +0 -0
  221. /package/{client/dist → dist/client}/icons/icon-192x192.svg +0 -0
  222. /package/{client/dist → dist/client}/icons/icon-384x384.png +0 -0
  223. /package/{client/dist → dist/client}/icons/icon-384x384.svg +0 -0
  224. /package/{client/dist → dist/client}/icons/icon-512x512.png +0 -0
  225. /package/{client/dist → dist/client}/icons/icon-512x512.svg +0 -0
  226. /package/{client/dist → dist/client}/icons/icon-72x72.png +0 -0
  227. /package/{client/dist → dist/client}/icons/icon-72x72.svg +0 -0
  228. /package/{client/dist → dist/client}/icons/icon-96x96.png +0 -0
  229. /package/{client/dist → dist/client}/icons/icon-96x96.svg +0 -0
  230. /package/{client/dist → dist/client}/icons/icon-template.svg +0 -0
  231. /package/{client/dist → dist/client}/logo-128.png +0 -0
  232. /package/{client/dist → dist/client}/logo-256.png +0 -0
  233. /package/{client/dist → dist/client}/logo-32.png +0 -0
  234. /package/{client/dist → dist/client}/logo-512.png +0 -0
  235. /package/{client/dist → dist/client}/logo-64.png +0 -0
  236. /package/{client/dist → dist/client}/logo.svg +0 -0
  237. /package/{client/dist → dist/client}/screenshots/cli-selection.png +0 -0
  238. /package/{client/dist → dist/client}/screenshots/desktop-main.png +0 -0
  239. /package/{client/dist → dist/client}/screenshots/mobile-chat.png +0 -0
  240. /package/{client/dist → dist/client}/screenshots/tools-modal.png +0 -0
  241. /package/{shared → dist}/gitagent/index.js +0 -0
  242. /package/{shared → dist}/gitagent/parser.js +0 -0
  243. /package/{shared → dist}/gitagent/prompt-builder.js +0 -0
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Upfyn-Code — Slash Command Installer
4
- *
5
- * Copies slash command .md files to ~/.claude/commands/
6
- * so they become available as /upfynai-* inside your AI CLI.
7
- *
8
- * Run manually: node scripts/install-commands.js
9
- * Or via CLI: uc install-commands
10
- */
11
-
12
- import fs from 'fs';
13
- import path from 'path';
14
- import os from 'os';
15
- import { fileURLToPath } from 'url';
16
-
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = path.dirname(__filename);
19
-
20
- const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
21
- const COMMANDS_DEST = path.join(os.homedir(), '.claude', 'commands');
22
-
23
- const colors = {
24
- reset: '\x1b[0m',
25
- green: '\x1b[32m',
26
- yellow: '\x1b[33m',
27
- cyan: '\x1b[36m',
28
- dim: '\x1b[2m',
29
- bright: '\x1b[1m',
30
- };
31
-
32
- function main() {
33
- console.log(`\n${colors.bright}Upfyn-Code — Installing Slash Commands${colors.reset}\n`);
34
-
35
- // Ensure ~/.claude/commands/ exists
36
- if (!fs.existsSync(COMMANDS_DEST)) {
37
- fs.mkdirSync(COMMANDS_DEST, { recursive: true });
38
- console.log(`${colors.green}✅${colors.reset} Created ${colors.dim}${COMMANDS_DEST}${colors.reset}`);
39
- }
40
-
41
- // Read all .md files from commands source
42
- if (!fs.existsSync(COMMANDS_SOURCE)) {
43
- console.error(`${colors.yellow}⚠️${colors.reset} Commands source not found: ${COMMANDS_SOURCE}`);
44
- process.exit(0);
45
- }
46
-
47
- const files = fs.readdirSync(COMMANDS_SOURCE).filter(f => f.endsWith('.md'));
48
-
49
- if (files.length === 0) {
50
- console.log(`${colors.yellow}⚠️${colors.reset} No command files found in ${COMMANDS_SOURCE}`);
51
- process.exit(0);
52
- }
53
-
54
- let installed = 0;
55
- let updated = 0;
56
-
57
- for (const file of files) {
58
- const src = path.join(COMMANDS_SOURCE, file);
59
- const dest = path.join(COMMANDS_DEST, file);
60
-
61
- const existed = fs.existsSync(dest);
62
- fs.copyFileSync(src, dest);
63
-
64
- if (existed) {
65
- updated++;
66
- console.log(`${colors.cyan}🔄${colors.reset} Updated /${file.replace('.md', '')}`);
67
- } else {
68
- installed++;
69
- console.log(`${colors.green}✅${colors.reset} Installed /${file.replace('.md', '')}`);
70
- }
71
- }
72
-
73
- console.log(`\n${colors.bright}Done!${colors.reset} ${installed} installed, ${updated} updated`);
74
- console.log(`${colors.dim}Commands are now available in your AI CLI${colors.reset}`);
75
- console.log(`${colors.dim}Try: /upfynai to start the web UI${colors.reset}\n`);
76
- }
77
-
78
- main();
@@ -1,242 +0,0 @@
1
- /**
2
- * Agent Loop: Stream → Tool Execute → Loop
3
- * Ported from opencode-ai/opencode internal/llm/agent/agent.go
4
- *
5
- * For cloud mode (BYOK): when the AI returns tool_use calls, execute them
6
- * server-side (or via relay), append results, and loop until no more tool calls.
7
- *
8
- * This implements the core agentic loop pattern:
9
- * 1. Stream response from LLM
10
- * 2. Collect tool calls from the response
11
- * 3. Execute tools (file read, shell, etc.)
12
- * 4. Append tool results to message history
13
- * 5. Loop back to step 1 until AI finishes without tool calls
14
- */
15
-
16
- import { streamChat, calculateCost } from './provider-factory.js';
17
- import { checkNeedsCompact, buildSummarizationPrompt, compactMessages } from './auto-compact.js';
18
-
19
- const MAX_TOOL_LOOPS = 25; // Safety limit to prevent infinite loops
20
-
21
- /**
22
- * Available server-side tools (opencode pattern: tool registry per agent type)
23
- * These are tools the AI can call during the agent loop.
24
- * For relay mode, tool execution is forwarded to the CLI.
25
- * For cloud mode, tools execute on the server or sandbox.
26
- */
27
- const TOOLS_SCHEMA = [
28
- {
29
- type: 'function',
30
- function: {
31
- name: 'read_file',
32
- description: 'Read the contents of a file',
33
- parameters: {
34
- type: 'object',
35
- properties: {
36
- path: { type: 'string', description: 'File path to read' },
37
- },
38
- required: ['path'],
39
- },
40
- },
41
- },
42
- {
43
- type: 'function',
44
- function: {
45
- name: 'write_file',
46
- description: 'Write content to a file',
47
- parameters: {
48
- type: 'object',
49
- properties: {
50
- path: { type: 'string', description: 'File path to write' },
51
- content: { type: 'string', description: 'File content' },
52
- },
53
- required: ['path', 'content'],
54
- },
55
- },
56
- },
57
- {
58
- type: 'function',
59
- function: {
60
- name: 'run_command',
61
- description: 'Execute a shell command',
62
- parameters: {
63
- type: 'object',
64
- properties: {
65
- command: { type: 'string', description: 'Shell command to run' },
66
- cwd: { type: 'string', description: 'Working directory (optional)' },
67
- },
68
- required: ['command'],
69
- },
70
- },
71
- },
72
- {
73
- type: 'function',
74
- function: {
75
- name: 'list_files',
76
- description: 'List files in a directory',
77
- parameters: {
78
- type: 'object',
79
- properties: {
80
- path: { type: 'string', description: 'Directory path' },
81
- pattern: { type: 'string', description: 'Glob pattern (optional)' },
82
- },
83
- required: ['path'],
84
- },
85
- },
86
- },
87
- {
88
- type: 'function',
89
- function: {
90
- name: 'search_files',
91
- description: 'Search for text in files',
92
- parameters: {
93
- type: 'object',
94
- properties: {
95
- query: { type: 'string', description: 'Search query (regex)' },
96
- path: { type: 'string', description: 'Directory to search in' },
97
- },
98
- required: ['query'],
99
- },
100
- },
101
- },
102
- ];
103
-
104
- /**
105
- * Run the agent loop (opencode pattern: processGeneration).
106
- *
107
- * @param {string} model - LLM model name
108
- * @param {Array} messages - Message history
109
- * @param {object} options - { apiKey, systemPrompt, onEvent, executeTool, maxLoops }
110
- * @returns {Promise<{ messages, usage, cost }>}
111
- *
112
- * options.onEvent(event) - Called for each streaming event:
113
- * { type: 'content_delta', content: '...' }
114
- * { type: 'tool_call', name: '...', input: {...} }
115
- * { type: 'tool_result', name: '...', output: '...' }
116
- * { type: 'complete' }
117
- * { type: 'error', error: '...' }
118
- *
119
- * options.executeTool(name, input) - Executes a tool, returns string result.
120
- * If not provided, tool calls are skipped (text-only mode).
121
- */
122
- export async function runAgentLoop(model, messages, options = {}) {
123
- const {
124
- apiKey,
125
- systemPrompt,
126
- onEvent = () => {},
127
- executeTool,
128
- maxLoops = MAX_TOOL_LOOPS,
129
- } = options;
130
-
131
- let totalUsage = { promptTokens: 0, completionTokens: 0 };
132
- let loopCount = 0;
133
- let currentMessages = [...messages];
134
-
135
- // Add system prompt if provided
136
- if (systemPrompt && (!currentMessages.length || currentMessages[0].role !== 'system')) {
137
- currentMessages.unshift({ role: 'system', content: systemPrompt });
138
- }
139
-
140
- while (loopCount < maxLoops) {
141
- loopCount++;
142
-
143
- // Check if we need auto-compact (opencode pattern: summarize at 85% context)
144
- const compactCheck = checkNeedsCompact(currentMessages, model);
145
- if (compactCheck.needsCompact) {
146
- onEvent({ type: 'system', message: 'Compacting conversation context...' });
147
- // In a real implementation, this would call the LLM to summarize
148
- // For now, just trim old messages
149
- if (currentMessages.length > 10) {
150
- const recent = currentMessages.slice(-6);
151
- currentMessages = [currentMessages[0], ...recent]; // keep system + recent
152
- }
153
- }
154
-
155
- // Stream response from LLM
156
- let assistantContent = '';
157
- let toolCalls = [];
158
- let currentToolCall = null;
159
-
160
- try {
161
- for await (const event of streamChat(model, currentMessages, {
162
- apiKey,
163
- tools: executeTool ? TOOLS_SCHEMA : undefined,
164
- })) {
165
- if (event.type === 'content_delta') {
166
- assistantContent += event.content;
167
- onEvent({ type: 'content_delta', content: event.content });
168
- } else if (event.type === 'tool_call_start') {
169
- currentToolCall = { name: event.name, id: event.id, input: '' };
170
- } else if (event.type === 'tool_call_delta') {
171
- if (currentToolCall) currentToolCall.input += event.content;
172
- } else if (event.type === 'tool_call_end') {
173
- if (currentToolCall) {
174
- try { currentToolCall.input = JSON.parse(currentToolCall.input); } catch {}
175
- toolCalls.push(currentToolCall);
176
- onEvent({ type: 'tool_call', name: currentToolCall.name, input: currentToolCall.input });
177
- currentToolCall = null;
178
- }
179
- } else if (event.type === 'usage') {
180
- totalUsage.promptTokens += event.promptTokens;
181
- totalUsage.completionTokens += event.completionTokens;
182
- } else if (event.type === 'complete') {
183
- break;
184
- }
185
- }
186
- } catch (err) {
187
- onEvent({ type: 'error', error: err.message });
188
- break;
189
- }
190
-
191
- // Append assistant message
192
- const assistantMsg = { role: 'assistant', content: assistantContent };
193
- if (toolCalls.length > 0) {
194
- assistantMsg.tool_calls = toolCalls.map(tc => ({
195
- id: tc.id || `call_${Date.now()}`,
196
- type: 'function',
197
- function: { name: tc.name, arguments: JSON.stringify(tc.input) },
198
- }));
199
- }
200
- currentMessages.push(assistantMsg);
201
-
202
- // If no tool calls, we're done (opencode pattern: finish reason != ToolUse → return)
203
- if (toolCalls.length === 0 || !executeTool) {
204
- onEvent({ type: 'complete' });
205
- break;
206
- }
207
-
208
- // Execute tools and append results (opencode pattern: tool execution loop)
209
- for (const tc of toolCalls) {
210
- let result;
211
- try {
212
- result = await executeTool(tc.name, tc.input);
213
- onEvent({ type: 'tool_result', name: tc.name, output: result });
214
- } catch (err) {
215
- result = `Error: ${err.message}`;
216
- onEvent({ type: 'tool_result', name: tc.name, output: result, isError: true });
217
- }
218
-
219
- currentMessages.push({
220
- role: 'tool',
221
- tool_call_id: tc.id || `call_${Date.now()}`,
222
- content: typeof result === 'string' ? result : JSON.stringify(result),
223
- });
224
- }
225
-
226
- // Loop back to get next response (step 5 → step 1)
227
- }
228
-
229
- if (loopCount >= maxLoops) {
230
- onEvent({ type: 'error', error: `Agent loop exceeded maximum iterations (${maxLoops})` });
231
- }
232
-
233
- const cost = calculateCost(model, totalUsage.promptTokens, totalUsage.completionTokens);
234
-
235
- return {
236
- messages: currentMessages,
237
- usage: totalUsage,
238
- costCents: cost,
239
- };
240
- }
241
-
242
- export { TOOLS_SCHEMA };
@@ -1,99 +0,0 @@
1
- /**
2
- * Auto-Compact Context Summarization
3
- * Ported from opencode-ai/opencode internal/llm/agent/agent.go (summarize logic)
4
- *
5
- * When a conversation approaches the context window limit, this module
6
- * summarizes the conversation and returns a condensed version.
7
- *
8
- * Used for BYOK cloud mode (OpenRouter, direct API calls) where the server
9
- * manages the message history. For relay mode, the CLI handles context internally.
10
- */
11
-
12
- // Model context windows (opencode pattern: models.go ContextWindow)
13
- const CONTEXT_WINDOWS = {
14
- 'claude-sonnet-4-20250514': 200000,
15
- 'claude-opus-4-20250514': 200000,
16
- 'claude-haiku-4-5-20251001': 200000,
17
- 'gpt-4o': 128000,
18
- 'gpt-4o-mini': 128000,
19
- 'o1': 200000,
20
- 'o3-mini': 200000,
21
- 'deepseek-chat': 64000,
22
- 'deepseek-reasoner': 64000,
23
- };
24
-
25
- // Rough token estimation (~4 chars per token)
26
- function estimateTokens(text) {
27
- return Math.ceil((text || '').length / 4);
28
- }
29
-
30
- /**
31
- * Check if a message history needs compaction.
32
- * @param {Array} messages - Array of { role, content } messages
33
- * @param {string} model - Model name
34
- * @param {number} threshold - Fraction of context window to trigger (default 0.85)
35
- * @returns {{ needsCompact: boolean, estimatedTokens: number, contextWindow: number }}
36
- */
37
- export function checkNeedsCompact(messages, model, threshold = 0.85) {
38
- const contextWindow = CONTEXT_WINDOWS[model] || 128000;
39
- const totalTokens = messages.reduce((sum, m) => sum + estimateTokens(m.content), 0);
40
- return {
41
- needsCompact: totalTokens > contextWindow * threshold,
42
- estimatedTokens: totalTokens,
43
- contextWindow,
44
- };
45
- }
46
-
47
- /**
48
- * Build a summarization prompt for compacting conversation history.
49
- * Returns messages array suitable for sending to an LLM.
50
- *
51
- * @param {Array} messages - Full message history
52
- * @param {string} systemPrompt - Original system prompt
53
- * @returns {Array} Messages for the summarization call
54
- */
55
- export function buildSummarizationPrompt(messages, systemPrompt = '') {
56
- // Take the conversation content and ask for a summary
57
- // (opencode pattern: AgentSummarizer with dedicated prompt)
58
- const conversationText = messages
59
- .map(m => `[${m.role}]: ${(m.content || '').slice(0, 2000)}`)
60
- .join('\n\n');
61
-
62
- return [
63
- {
64
- role: 'system',
65
- content: `You are a conversation summarizer. Summarize the following conversation between a user and an AI coding assistant. Focus on:
66
- 1. Key decisions made
67
- 2. Files modified and why
68
- 3. Current state of the task
69
- 4. Any unresolved issues or next steps
70
-
71
- Keep the summary concise but preserve all critical technical details. The summary will be used as context for continuing the conversation.`,
72
- },
73
- {
74
- role: 'user',
75
- content: `Summarize this conversation:\n\n${conversationText}`,
76
- },
77
- ];
78
- }
79
-
80
- /**
81
- * Compact a message history by replacing old messages with a summary.
82
- * The summary becomes the first message, preserving the most recent messages.
83
- *
84
- * @param {Array} messages - Full message history
85
- * @param {string} summary - Generated summary text
86
- * @param {number} keepRecent - Number of recent messages to keep verbatim (default 4)
87
- * @returns {Array} Compacted messages
88
- */
89
- export function compactMessages(messages, summary, keepRecent = 4) {
90
- const recentMessages = messages.slice(-keepRecent);
91
-
92
- return [
93
- {
94
- role: 'system',
95
- content: `[Conversation Summary]\n${summary}\n\n[The conversation continues below with the most recent messages]`,
96
- },
97
- ...recentMessages,
98
- ];
99
- }