apex-dev 3.0.2 โ†’ 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 (153) hide show
  1. package/dist/highlights-eq9cgrbb.scm +604 -0
  2. package/dist/highlights-ghv9g403.scm +205 -0
  3. package/dist/highlights-hk7bwhj4.scm +284 -0
  4. package/dist/highlights-r812a2qc.scm +150 -0
  5. package/dist/highlights-x6tmsnaa.scm +115 -0
  6. package/dist/index.js +62590 -0
  7. package/dist/injections-73j83es3.scm +27 -0
  8. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  9. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  10. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  11. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  12. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  13. package/package.json +10 -4
  14. package/.config/amp/settings.json +0 -3
  15. package/.config/opencode/oh-my-opencode.json +0 -58
  16. package/.config/opencode/opencode.json +0 -6
  17. package/.local/share/amp/device-id.json +0 -3
  18. package/.local/share/amp/history.jsonl +0 -78
  19. package/.local/share/amp/session.json +0 -6
  20. package/.local/share/amp/threads/T-019c93b8-fce7-7083-aab9-d5f1c88a9545.json +0 -2528
  21. package/.local/share/amp/threads/T-019c93c8-4b7a-71df-94ac-867d8236a288.json +0 -7
  22. package/.local/share/amp/threads/T-019c93cd-5a7d-728e-8289-02e0ef4ca2ff.json +0 -680
  23. package/.local/share/amp/threads/T-019c93e7-83ca-7633-9eed-12bdcc118163.json +0 -873
  24. package/.local/share/amp/threads/T-019c93ea-ccd3-765a-88c9-42d7b631e977.json +0 -620
  25. package/.local/share/amp/threads/T-019c93ee-5977-71af-9ab7-c4611004b703.json +0 -1000
  26. package/.local/share/amp/threads/T-019c93f0-8328-71ed-a250-6da169cebfe1.json +0 -829
  27. package/.local/share/amp/threads/T-019c93f5-7bdd-703b-b2cd-0a04da64441a.json +0 -459
  28. package/.local/share/amp/threads/T-019c93f8-2b2e-733b-8249-9876546d9b5b.json +0 -764
  29. package/.local/share/amp/threads/T-019c93fd-fade-7195-a3b7-358f180d40b8.json +0 -7
  30. package/.local/share/amp/threads/T-019c93fe-2e56-705e-827e-eb99bd02e257.json +0 -3593
  31. package/.local/share/amp/threads/T-019c9408-6e64-77e1-9519-b913e3b24a03.json +0 -1559
  32. package/.local/share/amp/threads/T-019c9409-feeb-736d-b92c-4f7a263a643c.json +0 -7
  33. package/.local/share/amp/threads/T-019c940b-8d11-755b-b9e1-f923d8a5e6ba.json +0 -7
  34. package/.local/share/amp/threads/T-019c943a-6c5e-76a5-bf4e-170f7ad452ce.json +0 -979
  35. package/.local/share/amp/threads/T-019c94b2-1c8f-76d8-96d0-82449a028849.json +0 -1584
  36. package/.local/share/amp/threads/T-019c94b6-68f0-726e-92dd-90c5411ca28c.json +0 -7
  37. package/.local/share/amp/threads/T-019c94bf-a589-72a3-b3c2-a81359d9e0a6.json +0 -7
  38. package/.local/share/amp/threads/T-019c94e1-1bd9-70ab-b6f2-abd5cab4f4ce.json +0 -1035
  39. package/.local/share/amp/threads/T-019c94fd-cc4a-714b-896a-74f94020f6eb.json +0 -1310
  40. package/.local/share/amp/threads/T-019c9501-8976-7138-aca6-245a01a8fe9b.json +0 -7
  41. package/.local/share/amp/threads/T-019c9504-4b51-763e-8a9f-5d4cdfcf0cfa.json +0 -496
  42. package/.local/share/amp/threads/T-019c9506-4e3b-74fd-8eda-cedbf3793598.json +0 -2679
  43. package/.local/share/amp/threads/T-019c9508-178c-718c-88d2-caf816d64f65.json +0 -965
  44. package/.local/share/amp/threads/T-019c9509-2812-71fd-8fd2-923e29ad34fa.json +0 -7
  45. package/.local/share/amp/threads/T-019c950e-69fe-77d6-9854-fc73b77a3148.json +0 -4570
  46. package/.local/share/amp/threads/T-019c9707-6e2b-741c-b4d4-117026a78449.json +0 -2899
  47. package/.local/share/amp/threads/T-019c971b-6bc0-77b8-8868-f8956d3e71a8.json +0 -7
  48. package/.local/share/amp/threads/T-019c971b-c87c-75f3-a61f-beb18a1cb25f.json +0 -474
  49. package/.local/share/amp/threads/T-019c971d-d371-70ac-9805-5c739908e73b.json +0 -802
  50. package/.local/share/amp/threads/T-019c9722-d73d-74f1-9d1d-8fafaad0ede7.json +0 -7
  51. package/.local/share/amp/threads/T-019c9761-858c-719b-911f-bc2e4c8cbdde.json +0 -188
  52. package/.local/share/amp/threads/T-019c9761-f5f3-7606-a900-ebe7f10d6e37.json +0 -121
  53. package/.local/share/amp/threads/T-019c9763-b1ae-729d-90aa-f59938ce912e.json +0 -799
  54. package/.local/share/amp/threads/T-019c9769-4a8a-77b8-beab-f48973276f9a.json +0 -1541
  55. package/.local/share/amp/threads/T-019c9772-edac-7075-b26e-0ada1f8697d2.json +0 -7
  56. package/.local/share/amp/threads/T-019c97e8-a9ab-71a1-a8f9-109c540c98bf.json +0 -111
  57. package/.local/share/amp/threads/T-019c97e9-2277-753c-8c5d-df745fa6cfff.json +0 -7
  58. package/.local/share/amp/threads/T-019c97e9-f28e-758d-9663-e37047a8ed95.json +0 -111
  59. package/.local/share/amp/threads/T-019c97ea-17c7-77b8-92b2-f641c069bcc9.json +0 -71
  60. package/.local/share/amp/threads/T-019c97ea-44c6-75b8-88bc-d88113194f6a.json +0 -1611
  61. package/.local/share/amp/threads/T-019c97ec-abae-7251-a5f6-693adf496a1c.json +0 -7
  62. package/.local/share/amp/threads/T-019c97f5-8e61-73ad-8c5d-2637abedcde6.json +0 -1341
  63. package/.local/share/amp/threads/T-019c989d-4f4e-7249-bde0-21d19455ccae.json +0 -163
  64. package/.local/share/amp/threads/T-019c989d-9024-73c4-bee8-e2ae45028a39.json +0 -124
  65. package/.local/share/amp/threads/T-019c989e-1394-74ad-8234-ac573fcdb4c7.json +0 -1260
  66. package/.local/share/amp/threads/T-019c989f-e3dd-772e-85ac-525d0fc88fda.json +0 -403
  67. package/.local/share/amp/threads/T-019c98a1-7b0c-778a-b311-2e1cff85d710.json +0 -3422
  68. package/.local/share/amp/threads/T-019c98c5-4b7f-7284-99e9-88aa8c18ba66.json +0 -1830
  69. package/.local/share/amp/threads/T-019c98d0-f27f-76ec-be10-6df96f22be99.json +0 -4061
  70. package/.local/share/amp/threads/T-019c98f9-d031-704d-a0c2-f2f395f68f2b.json +0 -509
  71. package/.local/share/amp/threads/T-019c9919-f9ee-766c-90be-af7a07f6a4c6.json +0 -2075
  72. package/.local/share/amp/threads/T-019c991c-b98b-7158-9083-cc52408beb13.json +0 -7
  73. package/.local/share/amp/threads/T-019c991d-66d6-72aa-a9a1-105f7df0ea06.json +0 -7
  74. package/.local/share/amp/threads/T-019c9c2e-71a4-77ff-bd7f-b053da7f9000.json +0 -1637
  75. package/.local/share/amp/threads/T-019c9c45-27ca-728b-ba77-835115dfa9b2.json +0 -3893
  76. package/.local/share/amp/threads/T-019c9c48-45dc-736a-9752-e4119fe698f9.json +0 -7
  77. package/.local/share/amp/threads/T-019c9c4d-266b-72d0-b56e-74a5777e6583.json +0 -7
  78. package/.local/share/amp/threads/T-019c9c52-ab89-758f-9178-bda99c39d10b.json +0 -7
  79. package/.local/share/amp/threads/T-019c9c56-5715-72e2-b8b4-87711a842dd1.json +0 -1799
  80. package/.local/share/amp/threads/T-019c9c5b-88b1-74cb-97e9-16b23e03daa2.json +0 -727
  81. package/.local/share/amp/threads/T-019c9c5c-3b3e-721c-ad2e-a2ef245dce3f.json +0 -738
  82. package/.local/share/amp/threads/T-019c9c5c-fd78-736f-9d29-a66d23839d40.json +0 -256
  83. package/.local/share/amp/threads/T-019c9c5d-db4a-74cd-ad2a-925fac87131d.json +0 -1859
  84. package/.local/share/kilo/kilo.db +0 -0
  85. package/.local/share/kilo/kilo.db-shm +0 -0
  86. package/.local/share/kilo/kilo.db-wal +0 -0
  87. package/.local/share/kilo/storage/migration +0 -1
  88. package/.local/share/kilo/storage/session_diff/ses_36bea4cb9ffe1b0j5HEL14KEaU.json +0 -1
  89. package/.local/share/kilo/storage/session_diff/ses_36beaa8f2ffeeZ3Y39SQ9UDWQQ.json +0 -1
  90. package/.local/share/kilo/telemetry-id +0 -1
  91. package/.local/share/opencode/auth.json +0 -6
  92. package/.local/share/opencode/opencode.db +0 -0
  93. package/.local/share/opencode/opencode.db-shm +0 -0
  94. package/.local/share/opencode/opencode.db-wal +0 -0
  95. package/.local/share/opencode/storage/agent-usage-reminder/ses_36870ea98ffe8S5ZOCE4F11yFh.json +0 -6
  96. package/.local/share/opencode/storage/agent-usage-reminder/ses_3687a3e9affewUnHBzvpiPR6df.json +0 -6
  97. package/.local/share/opencode/storage/agent-usage-reminder/ses_36886e68dffeKVgUWf6lzXdEEt.json +0 -6
  98. package/.local/share/opencode/storage/agent-usage-reminder/ses_36bee9f1effeJbiHHLWLR6O3WJ.json +0 -6
  99. package/.local/share/opencode/storage/agent-usage-reminder/ses_36c25e50affef2nhaXq9aSgKH3.json +0 -6
  100. package/.local/share/opencode/storage/agent-usage-reminder/ses_36c260708ffel4wG4yhdo0knDD.json +0 -6
  101. package/.local/share/opencode/storage/agent-usage-reminder/ses_36c261531ffeoVcvqXxry2bN9H.json +0 -6
  102. package/.local/share/opencode/storage/agent-usage-reminder/ses_36c291bddffePWRiaFLLJAC1y7.json +0 -6
  103. package/.local/share/opencode/storage/migration +0 -1
  104. package/.local/share/opencode/storage/session_diff/ses_36870ea98ffe8S5ZOCE4F11yFh.json +0 -1
  105. package/.local/share/opencode/storage/session_diff/ses_3687a3e9affewUnHBzvpiPR6df.json +0 -1
  106. package/.local/share/opencode/storage/session_diff/ses_36886e68dffeKVgUWf6lzXdEEt.json +0 -1
  107. package/.local/share/opencode/storage/session_diff/ses_36bee9f1effeJbiHHLWLR6O3WJ.json +0 -1
  108. package/.local/share/opencode/storage/session_diff/ses_36c25e50affef2nhaXq9aSgKH3.json +0 -1
  109. package/.local/share/opencode/storage/session_diff/ses_36c260708ffel4wG4yhdo0knDD.json +0 -1
  110. package/.local/share/opencode/storage/session_diff/ses_36c261531ffeoVcvqXxry2bN9H.json +0 -1
  111. package/.local/share/opencode/storage/session_diff/ses_36c291bddffePWRiaFLLJAC1y7.json +0 -1
  112. package/.local/share/opencode/storage/session_diff/ses_36c2af1c5ffegxEaOZOGcVykyy.json +0 -1
  113. package/.local/share/opencode/storage/session_diff/ses_36c2be235ffeOa6x8UCk1HW4kU.json +0 -1
  114. package/.local/share/opencode/tool-output/tool_c93da840c0016GrdyAkOnHGezU +0 -2330
  115. package/.local/share/opencode/tool-output/tool_c9411e784001cRoQqwVDb1a6lY +0 -1017
  116. package/.local/state/replit/log-query.db +0 -0
  117. package/.local/state/replit/log-query.db-shm +0 -0
  118. package/.local/state/replit/log-query.db-wal +0 -0
  119. package/.replit +0 -41
  120. package/.upm/store.json +0 -1
  121. package/AGENTS.md +0 -28
  122. package/bun.lock +0 -271
  123. package/generated-icon.png +0 -0
  124. package/hello.txt +0 -1
  125. package/index.jsx +0 -24
  126. package/src/agent.js +0 -504
  127. package/src/app.jsx +0 -96
  128. package/src/commands.js +0 -133
  129. package/src/components/AssistantMessage.jsx +0 -83
  130. package/src/components/ChatArea.jsx +0 -84
  131. package/src/components/DiffView.jsx +0 -26
  132. package/src/components/Divider.jsx +0 -8
  133. package/src/components/Header.jsx +0 -44
  134. package/src/components/HelpModal.jsx +0 -81
  135. package/src/components/InputBar.jsx +0 -32
  136. package/src/components/Spinner.jsx +0 -23
  137. package/src/components/StatusBar.jsx +0 -44
  138. package/src/components/SystemMessage.jsx +0 -31
  139. package/src/components/ThinkBlock.jsx +0 -29
  140. package/src/components/ToolCallItem.jsx +0 -43
  141. package/src/components/UserMessage.jsx +0 -11
  142. package/src/components/Welcome.jsx +0 -14
  143. package/src/config.js +0 -196
  144. package/src/hooks/useLayout.js +0 -15
  145. package/src/hooks/useStore.js +0 -6
  146. package/src/prompt.js +0 -101
  147. package/src/store.js +0 -99
  148. package/src/theme.js +0 -19
  149. package/src/thinking.js +0 -54
  150. package/src/toolExecutors.js +0 -853
  151. package/src/tools.js +0 -335
  152. package/src/utils.js +0 -32
  153. package/tsconfig.json +0 -10
package/src/agent.js DELETED
@@ -1,504 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- NVIDIA_MODEL,
5
- MAX_TOOL_ITERATIONS,
6
- nvidiaClient,
7
- session,
8
- sleep,
9
- getMode,
10
- } = require('./config');
11
- const { buildSystemPrompt } = require('./prompt');
12
- const { toolDefs } = require('./tools');
13
- const { executeTool } = require('./toolExecutors');
14
- const { toolDetailStr } = require('./utils');
15
- const store = require('./store');
16
- const {
17
- parseThinkBlocks,
18
- findThinkClose,
19
- stripStrayCloseTag,
20
- splitAtPartialTag,
21
- } = require('./thinking');
22
-
23
- // ===== State =====
24
- let isProcessing = false;
25
-
26
- function getIsProcessing() { return isProcessing; }
27
-
28
- // ===== Exploration Detection =====
29
- const EXPLORATION_KEYWORDS = [
30
- 'explore', 'find files', 'where is', 'search for', 'locate',
31
- 'what files', 'which files', 'show me all', 'list all',
32
- 'find code', 'search code', 'find function', 'find class',
33
- 'find module', 'find component', 'find endpoint', 'find route',
34
- 'find api', 'find service', 'find model', 'find controller',
35
- 'where can i find', 'how do i find', 'look for',
36
- 'codebase structure', 'project structure', 'directory structure'
37
- ];
38
-
39
- function isExplorationTask(text) {
40
- const lower = text.toLowerCase();
41
- return EXPLORATION_KEYWORDS.some(keyword => lower.includes(keyword));
42
- }
43
-
44
- // ===== Complexity Detection (for MAX mode auto-thinker) =====
45
- const COMPLEX_TASK_KEYWORDS = [
46
- 'refactor', 'redesign', 'architect', 'migrate', 'overhaul',
47
- 'implement', 'build', 'create a system', 'design pattern',
48
- 'add feature', 'new feature', 'integrate', 'convert',
49
- 'multiple files', 'across files', 'full stack',
50
- 'database schema', 'api endpoint', 'authentication',
51
- 'complex', 'tricky', 'challenging', 'difficult',
52
- ];
53
-
54
- function isComplexTask(text) {
55
- const lower = text.toLowerCase();
56
- const wordCount = text.split(/\s+/).length;
57
- const keywordMatch = COMPLEX_TASK_KEYWORDS.some(kw => lower.includes(kw));
58
- return keywordMatch || wordCount > 40;
59
- }
60
-
61
- // ===== AI Conversation โ€” Agentic Loop =====
62
- async function handleUserInput(userInput) {
63
- isProcessing = true;
64
- store.setState({ isProcessing: true });
65
- session.turnCount++;
66
-
67
- store.addMessage({ role: 'user', content: userInput });
68
- session.conversationHistory.push({ role: 'user', content: userInput });
69
-
70
- // Auto-delegate to FilePickerMax for exploration tasks
71
- if (isExplorationTask(userInput)) {
72
- const exploreId = store.addMessage({ role: 'system', content: 'Exploring codebase...', label: '๐Ÿ” Exploring' });
73
- try {
74
- const pickerResult = await executeTool('FilePickerMax', { prompt: userInput }, (partial) => {
75
- store.updateMessage(exploreId, { content: partial, label: '๐Ÿ” Exploring' });
76
- });
77
- store.updateMessage(exploreId, { content: pickerResult, label: '๐Ÿ” Codebase Exploration Results' });
78
- session.conversationHistory.push({
79
- role: 'assistant',
80
- content: `I've explored the codebase and found the following relevant files:\n\n${pickerResult}`,
81
- });
82
- } catch (err) {
83
- store.updateMessage(exploreId, { content: `Exploration failed: ${err.message}` });
84
- }
85
- }
86
-
87
- // MAX mode: Auto-think before complex tasks
88
- const mode = getMode();
89
- if (mode === 'max' && isComplexTask(userInput)) {
90
- const thinkId = store.addMessage({ role: 'system', content: 'Deep thinking...', label: '๐Ÿง  Thinker' });
91
- try {
92
- const thinkerResult = await executeTool('Thinker', { prompt: userInput }, (partial) => {
93
- store.updateMessage(thinkId, { content: partial, label: '๐Ÿง  Thinker' });
94
- });
95
- store.updateMessage(thinkId, { content: thinkerResult, label: '๐Ÿง  Thinker' });
96
- session.conversationHistory.push({
97
- role: 'assistant',
98
- content: `[Thinker analysis]\n${thinkerResult}`,
99
- });
100
- } catch (err) {
101
- store.updateMessage(thinkId, { content: `Thinker failed: ${err.message}` });
102
- }
103
- }
104
-
105
- // MAX mode: Auto-prune context when history is long
106
- if (mode === 'max' && session.conversationHistory.length > 20) {
107
- try {
108
- await executeTool('ContextPruner', {}, null);
109
- } catch { /* ignore pruning failures */ }
110
- }
111
-
112
- const startTime = Date.now();
113
- let turnTokens = 0;
114
- let turnToolCalls = 0;
115
-
116
- try {
117
- // Add assistant header message
118
- store.addMessage({ role: 'divider' });
119
-
120
- const systemPrompt = buildSystemPrompt();
121
- let messages = [
122
- { role: 'system', content: systemPrompt },
123
- ...session.conversationHistory,
124
- ];
125
-
126
- let iterations = 0;
127
-
128
- while (iterations < MAX_TOOL_ITERATIONS) {
129
- iterations++;
130
-
131
- let stream;
132
- const maxRetries = 3;
133
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
134
- try {
135
- stream = await nvidiaClient.chat.completions.create({
136
- model: NVIDIA_MODEL,
137
- messages: messages.map(m => {
138
- const clean = { role: m.role, content: m.content };
139
- if (m.tool_calls) clean.tool_calls = m.tool_calls.map(tc => ({
140
- id: tc.id, type: 'function',
141
- function: { name: tc.function.name, arguments: tc.function.arguments },
142
- }));
143
- if (m.tool_call_id) clean.tool_call_id = m.tool_call_id;
144
- if (m.role === 'assistant' && !m.content) clean.content = null;
145
- return clean;
146
- }),
147
- max_tokens: 4096,
148
- temperature: 0.6,
149
- top_p: 0.95,
150
- tools: toolDefs,
151
- tool_choice: 'auto',
152
- stream: true,
153
- });
154
- break;
155
- } catch (apiErr) {
156
- if (attempt < maxRetries && apiErr.status >= 400 && apiErr.status < 500) {
157
- await sleep(1000 * Math.pow(2, attempt));
158
- continue;
159
- }
160
- throw apiErr;
161
- }
162
- }
163
-
164
- // Accumulate streamed response
165
- let fullContent = '';
166
- const toolCallDeltas = {};
167
- const toolCallMsgIds = {};
168
- const seenToolCalls = new Set();
169
- let finishReason = null;
170
- let streamUsage = null;
171
- let reasoningText = '';
172
-
173
- // Display state
174
- let displayState = 'buffering';
175
- let contentAccum = '';
176
- let thinkAccum = '';
177
- let displayContent = '';
178
- let thinkContent = '';
179
- let lastFlushTime = Date.now();
180
-
181
- for await (const chunk of stream) {
182
- if (chunk.usage) streamUsage = chunk.usage;
183
-
184
- const delta = chunk.choices?.[0]?.delta;
185
- if (!delta) {
186
- if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;
187
- continue;
188
- }
189
- if (chunk.choices[0].finish_reason) finishReason = chunk.choices[0].finish_reason;
190
-
191
- // Tool call deltas
192
- if (delta.tool_calls) {
193
- for (const tc of delta.tool_calls) {
194
- const idx = tc.index;
195
- if (!toolCallDeltas[idx]) {
196
- toolCallDeltas[idx] = { id: tc.id || '', name: tc.function?.name || '', arguments: '' };
197
- }
198
- if (tc.id) toolCallDeltas[idx].id = tc.id;
199
- if (tc.function?.name) {
200
- toolCallDeltas[idx].name = tc.function.name;
201
- if (!seenToolCalls.has(idx)) {
202
- seenToolCalls.add(idx);
203
- toolCallMsgIds[idx] = store.addMessage({
204
- role: 'tool',
205
- name: tc.function.name,
206
- detail: '...',
207
- status: 'pending',
208
- });
209
- }
210
- }
211
- if (tc.function?.arguments) {
212
- toolCallDeltas[idx].arguments += tc.function.arguments;
213
- }
214
- }
215
- }
216
-
217
- if (delta.reasoning_content) {
218
- reasoningText += delta.reasoning_content;
219
- store.updateStreaming(displayContent, reasoningText);
220
- }
221
-
222
- // Handle content tokens
223
- if (delta.content) {
224
- fullContent += delta.content;
225
- const hasTool = Object.keys(toolCallDeltas).length > 0;
226
-
227
- if (displayState === 'streaming') {
228
- contentAccum += delta.content;
229
- contentAccum = stripStrayCloseTag(contentAccum);
230
- const openIdx = contentAccum.indexOf('<think>');
231
- if (openIdx !== -1) {
232
- if (openIdx > 0) displayContent += contentAccum.slice(0, openIdx);
233
- thinkAccum = contentAccum.slice(openIdx + 7);
234
- contentAccum = '';
235
- displayState = 'thinking';
236
- const closeMatch = findThinkClose(thinkAccum);
237
- if (closeMatch) {
238
- const thought = thinkAccum.slice(0, closeMatch.pos).trim();
239
- const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
240
- thinkAccum = '';
241
- if (thought) store.addMessage({ role: 'thinking', content: thought });
242
- displayState = 'streaming';
243
- contentAccum = after;
244
- if (!hasTool && after) displayContent += after;
245
- contentAccum = '';
246
- thinkContent = '';
247
- } else {
248
- thinkContent = thinkAccum;
249
- }
250
- } else {
251
- const { safe, pending } = splitAtPartialTag(contentAccum);
252
- contentAccum = pending;
253
- if (!hasTool && safe) displayContent += safe;
254
- }
255
- store.updateStreaming(displayContent, thinkContent || reasoningText);
256
-
257
- } else if (displayState === 'thinking') {
258
- thinkAccum += delta.content;
259
- const closeMatch = findThinkClose(thinkAccum);
260
- if (closeMatch) {
261
- const thought = thinkAccum.slice(0, closeMatch.pos).trim();
262
- const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
263
- thinkAccum = '';
264
- if (thought) store.addMessage({ role: 'thinking', content: thought });
265
- displayState = 'streaming';
266
- contentAccum = after;
267
- if (!hasTool && after) displayContent += after;
268
- contentAccum = '';
269
- thinkContent = '';
270
- store.updateStreaming(displayContent, reasoningText);
271
- } else {
272
- thinkContent = thinkAccum;
273
- store.updateStreaming(displayContent, thinkContent || reasoningText);
274
- }
275
-
276
- } else {
277
- // buffering
278
- contentAccum += delta.content;
279
- contentAccum = stripStrayCloseTag(contentAccum);
280
- const openIdx = contentAccum.indexOf('<think>');
281
- if (openIdx !== -1) {
282
- const before = contentAccum.slice(0, openIdx);
283
- thinkAccum = contentAccum.slice(openIdx + 7);
284
- contentAccum = '';
285
- if (!hasTool && before.trim()) displayContent += before;
286
- displayState = 'thinking';
287
- const closeMatch = findThinkClose(thinkAccum);
288
- if (closeMatch) {
289
- const thought = thinkAccum.slice(0, closeMatch.pos).trim();
290
- const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
291
- thinkAccum = '';
292
- if (thought) store.addMessage({ role: 'thinking', content: thought });
293
- displayState = 'streaming';
294
- contentAccum = after;
295
- if (!hasTool && after) displayContent += after;
296
- contentAccum = '';
297
- thinkContent = '';
298
- } else {
299
- thinkContent = thinkAccum;
300
- }
301
- store.updateStreaming(displayContent, thinkContent || reasoningText);
302
- } else {
303
- const { safe, pending } = splitAtPartialTag(contentAccum);
304
- if (safe.length > 0) {
305
- displayState = 'streaming';
306
- if (!hasTool) displayContent += safe;
307
- contentAccum = pending;
308
- store.updateStreaming(displayContent, reasoningText);
309
- }
310
- }
311
- }
312
- }
313
-
314
- // Yield to the event loop periodically so the terminal renderer can paint
315
- const now = Date.now();
316
- if (now - lastFlushTime > 16) {
317
- lastFlushTime = now;
318
- await new Promise(r => setTimeout(r, 1));
319
- }
320
- }
321
-
322
- // Stream ended โ€” flush remaining buffers
323
- if (displayState === 'thinking') {
324
- const thought = (thinkAccum + contentAccum).trim();
325
- if (thought) store.addMessage({ role: 'thinking', content: thought });
326
- thinkAccum = '';
327
- contentAccum = '';
328
- } else if (displayState === 'buffering') {
329
- const hasTool = Object.keys(toolCallDeltas).length > 0;
330
- if (!hasTool && contentAccum.trim()) displayContent += contentAccum;
331
- contentAccum = '';
332
- } else if (contentAccum) {
333
- const hasTool = Object.keys(toolCallDeltas).length > 0;
334
- if (!hasTool) displayContent += contentAccum;
335
- contentAccum = '';
336
- }
337
-
338
- // Show reasoning from reasoning_content field
339
- if (reasoningText.trim()) {
340
- store.addMessage({ role: 'thinking', content: reasoningText.trim() });
341
- }
342
-
343
- const { content: parsedContent } = parseThinkBlocks(fullContent);
344
- turnTokens += streamUsage?.total_tokens || 0;
345
-
346
- // Reconstruct tool calls
347
- const sortedIndices = Object.keys(toolCallDeltas).sort((a, b) => a - b);
348
- const toolCalls = sortedIndices.map(idx => ({
349
- id: toolCallDeltas[idx].id,
350
- type: 'function',
351
- function: { name: toolCallDeltas[idx].name, arguments: toolCallDeltas[idx].arguments },
352
- }));
353
-
354
- const msg = {
355
- role: 'assistant',
356
- content: fullContent || null,
357
- ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
358
- };
359
-
360
- // If the model wants to call tools
361
- if (toolCalls.length > 0) {
362
- store.clearStreaming();
363
- messages.push(msg);
364
-
365
- // Show intermediate text if any
366
- if (displayContent.trim()) {
367
- store.addMessage({ role: 'assistant', content: displayContent.trim() });
368
- }
369
-
370
- // Update tool messages with full args and execute
371
- sortedIndices.forEach((idx, i) => {
372
- const tc = toolCalls[i];
373
- let toolArgs;
374
- try { toolArgs = JSON.parse(tc.function.arguments); } catch { toolArgs = {}; }
375
- const detail = toolDetailStr(tc.function.name, toolArgs);
376
- const msgId = toolCallMsgIds[idx];
377
- if (msgId) store.updateMessage(msgId, { detail, status: 'running' });
378
- });
379
-
380
- const toolPromises = toolCalls.map(async (toolCall, i) => {
381
- const toolName = toolCall.function.name;
382
- let toolArgs;
383
- try { toolArgs = JSON.parse(toolCall.function.arguments); } catch { toolArgs = {}; }
384
- const detail = toolDetailStr(toolName, toolArgs);
385
- const callStart = Date.now();
386
- const msgId = toolCallMsgIds[sortedIndices[i]];
387
-
388
- const result = await executeTool(toolName, toolArgs, (partial) => {
389
- if (msgId) store.updateMessage(msgId, { output: partial });
390
- });
391
- const success = !result.startsWith('Error');
392
- const elapsed = Date.now() - callStart;
393
-
394
- session.toolCallCount++;
395
- turnToolCalls++;
396
-
397
- if (msgId) {
398
- store.updateMessage(msgId, {
399
- detail,
400
- status: success ? 'done' : 'error',
401
- success,
402
- elapsed,
403
- output: result,
404
- });
405
- }
406
-
407
- // Show diff for edit operations
408
- if ((toolName === 'Edit' || toolName === 'Patch') && success) {
409
- store.addMessage({ role: 'diff', filename: toolArgs.path, content: result });
410
- }
411
-
412
- return { id: toolCall.id, result };
413
- });
414
-
415
- const toolResults = await Promise.all(toolPromises);
416
-
417
- for (const { id, result } of toolResults) {
418
- messages.push({ role: 'tool', tool_call_id: id, content: result });
419
- }
420
-
421
- if (finishReason === 'stop') break;
422
- displayContent = '';
423
- continue;
424
- }
425
-
426
- // No tool calls โ€” final text response
427
- if (fullContent) {
428
- const cleanedContent = parsedContent.trim() || fullContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
429
- if (cleanedContent) {
430
- store.finishStreaming({ role: 'assistant', content: cleanedContent });
431
- } else {
432
- store.clearStreaming();
433
- }
434
- session.conversationHistory.push({ role: 'assistant', content: cleanedContent || fullContent });
435
- } else {
436
- store.clearStreaming();
437
- }
438
- break;
439
- }
440
-
441
- if (iterations >= MAX_TOOL_ITERATIONS) {
442
- store.addMessage({ role: 'system', content: `โš  Reached maximum tool iterations (${MAX_TOOL_ITERATIONS}). Stopping.` });
443
- }
444
-
445
- session.totalTokens += turnTokens;
446
-
447
- // Auto-delegate to CodeReview for code changes
448
- if (session.filesModified.size > 0) {
449
- const reviewPrompt = `User request: ${userInput}\n\n${turnTokens > 0 ? `Processed with ${turnTokens} tokens and ${turnToolCalls} tool calls.` : ''}`;
450
-
451
- if (mode === 'max') {
452
- // MAX mode: Multi-perspective code review
453
- const reviewId = store.addMessage({ role: 'system', content: 'Multi-perspective code review...', label: '๐Ÿ“‹ Code Review (MAX)' });
454
- try {
455
- const reviewResult = await executeTool('CodeReviewMulti', { prompt: reviewPrompt }, (partial) => {
456
- store.updateMessage(reviewId, { content: partial, label: '๐Ÿ“‹ Code Review (MAX)' });
457
- });
458
- store.updateMessage(reviewId, { content: reviewResult, label: '๐Ÿ“‹ Code Review (MAX)' });
459
- session.conversationHistory.push({
460
- role: 'assistant',
461
- content: `\n\n--- Multi-Perspective Code Review ---\n${reviewResult}`,
462
- });
463
- } catch (err) {
464
- store.updateMessage(reviewId, { content: `Multi-review failed: ${err.message}` });
465
- }
466
- } else if (mode !== 'lite') {
467
- // Default mode: Single code review
468
- const reviewId = store.addMessage({ role: 'system', content: 'Reviewing code changes...', label: '๐Ÿ“‹ Code Review' });
469
- try {
470
- const reviewResult = await executeTool('CodeReview', { prompt: reviewPrompt }, (partial) => {
471
- store.updateMessage(reviewId, { content: partial, label: '๐Ÿ“‹ Code Review' });
472
- });
473
- store.updateMessage(reviewId, { content: reviewResult, label: '๐Ÿ“‹ Code Review' });
474
- session.conversationHistory.push({
475
- role: 'assistant',
476
- content: `\n\n--- Code Review ---\n${reviewResult}`,
477
- });
478
- } catch (err) {
479
- store.updateMessage(reviewId, { content: `Code review failed: ${err.message}` });
480
- }
481
- }
482
- // Lite mode: skip code review entirely
483
- }
484
- } catch (err) {
485
- store.clearStreaming();
486
- let errorMsg = `Error: ${err.message}`;
487
- if (err.status) {
488
- errorMsg += `\nStatus: ${err.status}`;
489
- }
490
- if (!process.env.NVIDIA_API_KEY) {
491
- errorMsg += '\nSet the NVIDIA_API_KEY environment variable with your API key from build.nvidia.com';
492
- }
493
- store.addMessage({ role: 'system', content: errorMsg });
494
- }
495
-
496
- store.addMessage({ role: 'divider' });
497
- isProcessing = false;
498
- store.setState({ isProcessing: false });
499
- }
500
-
501
- module.exports = {
502
- handleUserInput,
503
- getIsProcessing,
504
- };
package/src/app.jsx DELETED
@@ -1,96 +0,0 @@
1
- import { useCallback } from 'react';
2
- import { useKeyboard } from '@opentui/react';
3
- import { useStore } from './hooks/useStore.js';
4
- import { setState, addMessage, clearMessages, getRenderer } from './store.js';
5
- import { session } from './config.js';
6
- import { handleUserInput } from './agent.js';
7
- import { handleSlashCommand } from './commands.js';
8
- import Header from './components/Header.jsx';
9
- import Divider from './components/Divider.jsx';
10
- import ChatArea from './components/ChatArea.jsx';
11
- import InputBar from './components/InputBar.jsx';
12
- import StatusBar from './components/StatusBar.jsx';
13
- import HelpModal from './components/HelpModal.jsx';
14
-
15
- function exitApp() {
16
- const renderer = getRenderer();
17
- if (renderer) renderer.destroy();
18
-
19
- const elapsed = ((Date.now() - session.startTime) / 1000 / 60).toFixed(1);
20
- const parts = [
21
- `${elapsed} min`,
22
- `${session.turnCount} turns`,
23
- `${session.toolCallCount} tool calls`,
24
- `${session.totalTokens.toLocaleString()} tokens`,
25
- `$${session.totalCost.toFixed(4)}`,
26
- ];
27
- if (session.filesModified.size > 0) parts.push(`${session.filesModified.size} files modified`);
28
- if (session.commandsRun.length > 0) parts.push(`${session.commandsRun.length} commands`);
29
-
30
- console.log(`\n Session: ${parts.join(' ยท ')}\n`);
31
- console.log(' Goodbye! โœฆ\n');
32
- process.exit(0);
33
- }
34
-
35
- export default function App() {
36
- const state = useStore();
37
-
38
- useKeyboard((key) => {
39
- if (key.ctrl && key.name === 'c') {
40
- exitApp();
41
- }
42
- });
43
-
44
- const handleInput = useCallback(async (value) => {
45
- if (value === 'exit' || value === 'quit') {
46
- exitApp();
47
- return;
48
- }
49
-
50
- if (value.startsWith('/')) {
51
- const result = await handleSlashCommand(value);
52
- if (result?.action === 'quit') {
53
- exitApp();
54
- }
55
- return;
56
- }
57
-
58
- handleUserInput(value).catch(err => {
59
- addMessage({ role: 'system', content: `Error: ${err.message}` });
60
- setState({ isProcessing: false });
61
- });
62
- }, []);
63
-
64
- const handleHelpCommand = useCallback((cmd) => {
65
- if (cmd) {
66
- handleSlashCommand(cmd).then(result => {
67
- if (result?.action === 'quit') exitApp();
68
- });
69
- }
70
- }, []);
71
-
72
- return (
73
- <box style={{ flexDirection: 'column', flexGrow: 1 }}>
74
- <Header />
75
- <Divider />
76
- <ChatArea
77
- messages={state.messages}
78
- streamingContent={state.streamingContent}
79
- streamingThinking={state.streamingThinking}
80
- isProcessing={state.isProcessing}
81
- />
82
- <Divider />
83
- <StatusBar isProcessing={state.isProcessing} />
84
- <InputBar
85
- disabled={state.isProcessing || state.showHelp}
86
- onSubmit={handleInput}
87
- />
88
- {state.showHelp ? (
89
- <HelpModal
90
- onClose={() => setState({ showHelp: false })}
91
- onCommand={handleHelpCommand}
92
- />
93
- ) : null}
94
- </box>
95
- );
96
- }