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.
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index.js +62590 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/package.json +10 -4
- package/.config/amp/settings.json +0 -3
- package/.config/opencode/oh-my-opencode.json +0 -58
- package/.config/opencode/opencode.json +0 -6
- package/.local/share/amp/device-id.json +0 -3
- package/.local/share/amp/history.jsonl +0 -78
- package/.local/share/amp/session.json +0 -6
- package/.local/share/amp/threads/T-019c93b8-fce7-7083-aab9-d5f1c88a9545.json +0 -2528
- package/.local/share/amp/threads/T-019c93c8-4b7a-71df-94ac-867d8236a288.json +0 -7
- package/.local/share/amp/threads/T-019c93cd-5a7d-728e-8289-02e0ef4ca2ff.json +0 -680
- package/.local/share/amp/threads/T-019c93e7-83ca-7633-9eed-12bdcc118163.json +0 -873
- package/.local/share/amp/threads/T-019c93ea-ccd3-765a-88c9-42d7b631e977.json +0 -620
- package/.local/share/amp/threads/T-019c93ee-5977-71af-9ab7-c4611004b703.json +0 -1000
- package/.local/share/amp/threads/T-019c93f0-8328-71ed-a250-6da169cebfe1.json +0 -829
- package/.local/share/amp/threads/T-019c93f5-7bdd-703b-b2cd-0a04da64441a.json +0 -459
- package/.local/share/amp/threads/T-019c93f8-2b2e-733b-8249-9876546d9b5b.json +0 -764
- package/.local/share/amp/threads/T-019c93fd-fade-7195-a3b7-358f180d40b8.json +0 -7
- package/.local/share/amp/threads/T-019c93fe-2e56-705e-827e-eb99bd02e257.json +0 -3593
- package/.local/share/amp/threads/T-019c9408-6e64-77e1-9519-b913e3b24a03.json +0 -1559
- package/.local/share/amp/threads/T-019c9409-feeb-736d-b92c-4f7a263a643c.json +0 -7
- package/.local/share/amp/threads/T-019c940b-8d11-755b-b9e1-f923d8a5e6ba.json +0 -7
- package/.local/share/amp/threads/T-019c943a-6c5e-76a5-bf4e-170f7ad452ce.json +0 -979
- package/.local/share/amp/threads/T-019c94b2-1c8f-76d8-96d0-82449a028849.json +0 -1584
- package/.local/share/amp/threads/T-019c94b6-68f0-726e-92dd-90c5411ca28c.json +0 -7
- package/.local/share/amp/threads/T-019c94bf-a589-72a3-b3c2-a81359d9e0a6.json +0 -7
- package/.local/share/amp/threads/T-019c94e1-1bd9-70ab-b6f2-abd5cab4f4ce.json +0 -1035
- package/.local/share/amp/threads/T-019c94fd-cc4a-714b-896a-74f94020f6eb.json +0 -1310
- package/.local/share/amp/threads/T-019c9501-8976-7138-aca6-245a01a8fe9b.json +0 -7
- package/.local/share/amp/threads/T-019c9504-4b51-763e-8a9f-5d4cdfcf0cfa.json +0 -496
- package/.local/share/amp/threads/T-019c9506-4e3b-74fd-8eda-cedbf3793598.json +0 -2679
- package/.local/share/amp/threads/T-019c9508-178c-718c-88d2-caf816d64f65.json +0 -965
- package/.local/share/amp/threads/T-019c9509-2812-71fd-8fd2-923e29ad34fa.json +0 -7
- package/.local/share/amp/threads/T-019c950e-69fe-77d6-9854-fc73b77a3148.json +0 -4570
- package/.local/share/amp/threads/T-019c9707-6e2b-741c-b4d4-117026a78449.json +0 -2899
- package/.local/share/amp/threads/T-019c971b-6bc0-77b8-8868-f8956d3e71a8.json +0 -7
- package/.local/share/amp/threads/T-019c971b-c87c-75f3-a61f-beb18a1cb25f.json +0 -474
- package/.local/share/amp/threads/T-019c971d-d371-70ac-9805-5c739908e73b.json +0 -802
- package/.local/share/amp/threads/T-019c9722-d73d-74f1-9d1d-8fafaad0ede7.json +0 -7
- package/.local/share/amp/threads/T-019c9761-858c-719b-911f-bc2e4c8cbdde.json +0 -188
- package/.local/share/amp/threads/T-019c9761-f5f3-7606-a900-ebe7f10d6e37.json +0 -121
- package/.local/share/amp/threads/T-019c9763-b1ae-729d-90aa-f59938ce912e.json +0 -799
- package/.local/share/amp/threads/T-019c9769-4a8a-77b8-beab-f48973276f9a.json +0 -1541
- package/.local/share/amp/threads/T-019c9772-edac-7075-b26e-0ada1f8697d2.json +0 -7
- package/.local/share/amp/threads/T-019c97e8-a9ab-71a1-a8f9-109c540c98bf.json +0 -111
- package/.local/share/amp/threads/T-019c97e9-2277-753c-8c5d-df745fa6cfff.json +0 -7
- package/.local/share/amp/threads/T-019c97e9-f28e-758d-9663-e37047a8ed95.json +0 -111
- package/.local/share/amp/threads/T-019c97ea-17c7-77b8-92b2-f641c069bcc9.json +0 -71
- package/.local/share/amp/threads/T-019c97ea-44c6-75b8-88bc-d88113194f6a.json +0 -1611
- package/.local/share/amp/threads/T-019c97ec-abae-7251-a5f6-693adf496a1c.json +0 -7
- package/.local/share/amp/threads/T-019c97f5-8e61-73ad-8c5d-2637abedcde6.json +0 -1341
- package/.local/share/amp/threads/T-019c989d-4f4e-7249-bde0-21d19455ccae.json +0 -163
- package/.local/share/amp/threads/T-019c989d-9024-73c4-bee8-e2ae45028a39.json +0 -124
- package/.local/share/amp/threads/T-019c989e-1394-74ad-8234-ac573fcdb4c7.json +0 -1260
- package/.local/share/amp/threads/T-019c989f-e3dd-772e-85ac-525d0fc88fda.json +0 -403
- package/.local/share/amp/threads/T-019c98a1-7b0c-778a-b311-2e1cff85d710.json +0 -3422
- package/.local/share/amp/threads/T-019c98c5-4b7f-7284-99e9-88aa8c18ba66.json +0 -1830
- package/.local/share/amp/threads/T-019c98d0-f27f-76ec-be10-6df96f22be99.json +0 -4061
- package/.local/share/amp/threads/T-019c98f9-d031-704d-a0c2-f2f395f68f2b.json +0 -509
- package/.local/share/amp/threads/T-019c9919-f9ee-766c-90be-af7a07f6a4c6.json +0 -2075
- package/.local/share/amp/threads/T-019c991c-b98b-7158-9083-cc52408beb13.json +0 -7
- package/.local/share/amp/threads/T-019c991d-66d6-72aa-a9a1-105f7df0ea06.json +0 -7
- package/.local/share/amp/threads/T-019c9c2e-71a4-77ff-bd7f-b053da7f9000.json +0 -1637
- package/.local/share/amp/threads/T-019c9c45-27ca-728b-ba77-835115dfa9b2.json +0 -3893
- package/.local/share/amp/threads/T-019c9c48-45dc-736a-9752-e4119fe698f9.json +0 -7
- package/.local/share/amp/threads/T-019c9c4d-266b-72d0-b56e-74a5777e6583.json +0 -7
- package/.local/share/amp/threads/T-019c9c52-ab89-758f-9178-bda99c39d10b.json +0 -7
- package/.local/share/amp/threads/T-019c9c56-5715-72e2-b8b4-87711a842dd1.json +0 -1799
- package/.local/share/amp/threads/T-019c9c5b-88b1-74cb-97e9-16b23e03daa2.json +0 -727
- package/.local/share/amp/threads/T-019c9c5c-3b3e-721c-ad2e-a2ef245dce3f.json +0 -738
- package/.local/share/amp/threads/T-019c9c5c-fd78-736f-9d29-a66d23839d40.json +0 -256
- package/.local/share/amp/threads/T-019c9c5d-db4a-74cd-ad2a-925fac87131d.json +0 -1859
- package/.local/share/kilo/kilo.db +0 -0
- package/.local/share/kilo/kilo.db-shm +0 -0
- package/.local/share/kilo/kilo.db-wal +0 -0
- package/.local/share/kilo/storage/migration +0 -1
- package/.local/share/kilo/storage/session_diff/ses_36bea4cb9ffe1b0j5HEL14KEaU.json +0 -1
- package/.local/share/kilo/storage/session_diff/ses_36beaa8f2ffeeZ3Y39SQ9UDWQQ.json +0 -1
- package/.local/share/kilo/telemetry-id +0 -1
- package/.local/share/opencode/auth.json +0 -6
- package/.local/share/opencode/opencode.db +0 -0
- package/.local/share/opencode/opencode.db-shm +0 -0
- package/.local/share/opencode/opencode.db-wal +0 -0
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36870ea98ffe8S5ZOCE4F11yFh.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_3687a3e9affewUnHBzvpiPR6df.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36886e68dffeKVgUWf6lzXdEEt.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36bee9f1effeJbiHHLWLR6O3WJ.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36c25e50affef2nhaXq9aSgKH3.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36c260708ffel4wG4yhdo0knDD.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36c261531ffeoVcvqXxry2bN9H.json +0 -6
- package/.local/share/opencode/storage/agent-usage-reminder/ses_36c291bddffePWRiaFLLJAC1y7.json +0 -6
- package/.local/share/opencode/storage/migration +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36870ea98ffe8S5ZOCE4F11yFh.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_3687a3e9affewUnHBzvpiPR6df.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36886e68dffeKVgUWf6lzXdEEt.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36bee9f1effeJbiHHLWLR6O3WJ.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c25e50affef2nhaXq9aSgKH3.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c260708ffel4wG4yhdo0knDD.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c261531ffeoVcvqXxry2bN9H.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c291bddffePWRiaFLLJAC1y7.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c2af1c5ffegxEaOZOGcVykyy.json +0 -1
- package/.local/share/opencode/storage/session_diff/ses_36c2be235ffeOa6x8UCk1HW4kU.json +0 -1
- package/.local/share/opencode/tool-output/tool_c93da840c0016GrdyAkOnHGezU +0 -2330
- package/.local/share/opencode/tool-output/tool_c9411e784001cRoQqwVDb1a6lY +0 -1017
- package/.local/state/replit/log-query.db +0 -0
- package/.local/state/replit/log-query.db-shm +0 -0
- package/.local/state/replit/log-query.db-wal +0 -0
- package/.replit +0 -41
- package/.upm/store.json +0 -1
- package/AGENTS.md +0 -28
- package/bun.lock +0 -271
- package/generated-icon.png +0 -0
- package/hello.txt +0 -1
- package/index.jsx +0 -24
- package/src/agent.js +0 -504
- package/src/app.jsx +0 -96
- package/src/commands.js +0 -133
- package/src/components/AssistantMessage.jsx +0 -83
- package/src/components/ChatArea.jsx +0 -84
- package/src/components/DiffView.jsx +0 -26
- package/src/components/Divider.jsx +0 -8
- package/src/components/Header.jsx +0 -44
- package/src/components/HelpModal.jsx +0 -81
- package/src/components/InputBar.jsx +0 -32
- package/src/components/Spinner.jsx +0 -23
- package/src/components/StatusBar.jsx +0 -44
- package/src/components/SystemMessage.jsx +0 -31
- package/src/components/ThinkBlock.jsx +0 -29
- package/src/components/ToolCallItem.jsx +0 -43
- package/src/components/UserMessage.jsx +0 -11
- package/src/components/Welcome.jsx +0 -14
- package/src/config.js +0 -196
- package/src/hooks/useLayout.js +0 -15
- package/src/hooks/useStore.js +0 -6
- package/src/prompt.js +0 -101
- package/src/store.js +0 -99
- package/src/theme.js +0 -19
- package/src/thinking.js +0 -54
- package/src/toolExecutors.js +0 -853
- package/src/tools.js +0 -335
- package/src/utils.js +0 -32
- 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
|
-
}
|