mistagent 0.1.18 → 0.1.20
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/src/components/App.d.ts +2 -0
- package/dist/src/components/App.js +23 -5
- package/dist/src/components/Composer.js +2 -1
- package/dist/src/components/Header.js +74 -166
- package/dist/src/components/MainContent.js +3 -14
- package/dist/src/components/shared/MarkdownRenderer.js +15 -26
- package/dist/src/components/shared/TextInput.js +62 -3
- package/dist/src/contexts/ChatContext.d.ts +2 -0
- package/dist/src/contexts/ChatContext.js +1 -1
- package/dist/src/hooks/useChat.js +71 -10
- package/dist/src/main.js +62 -4
- package/dist/src/types/api.d.ts +4 -0
- package/dist/src/utils/config.d.ts +2 -0
- package/dist/src/utils/config.js +23 -0
- package/dist/src/utils/constants.d.ts +1 -1
- package/dist/src/utils/constants.js +1 -1
- package/dist/src/utils/fileTunnel.d.ts +7 -2
- package/dist/src/utils/fileTunnel.js +376 -5
- package/dist/src/utils/markdown.d.ts +10 -0
- package/dist/src/utils/markdown.js +223 -0
- package/dist/src/utils/updateChecker.js +10 -4
- package/package.json +3 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/src/api/auth.d.ts.map +0 -1
- package/dist/src/api/auth.js.map +0 -1
- package/dist/src/api/chat.d.ts.map +0 -1
- package/dist/src/api/chat.js.map +0 -1
- package/dist/src/api/client.d.ts.map +0 -1
- package/dist/src/api/client.js.map +0 -1
- package/dist/src/api/models.d.ts.map +0 -1
- package/dist/src/api/models.js.map +0 -1
- package/dist/src/api/sessions.d.ts.map +0 -1
- package/dist/src/api/sessions.js.map +0 -1
- package/dist/src/api/skills.d.ts.map +0 -1
- package/dist/src/api/skills.js.map +0 -1
- package/dist/src/api/tools.d.ts.map +0 -1
- package/dist/src/api/tools.js.map +0 -1
- package/dist/src/api/tunnel.d.ts.map +0 -1
- package/dist/src/api/tunnel.js.map +0 -1
- package/dist/src/components/App.d.ts.map +0 -1
- package/dist/src/components/App.js.map +0 -1
- package/dist/src/components/AppLayout.d.ts.map +0 -1
- package/dist/src/components/AppLayout.js.map +0 -1
- package/dist/src/components/Composer.d.ts.map +0 -1
- package/dist/src/components/Composer.js.map +0 -1
- package/dist/src/components/Footer.d.ts.map +0 -1
- package/dist/src/components/Footer.js.map +0 -1
- package/dist/src/components/Header.d.ts.map +0 -1
- package/dist/src/components/Header.js.map +0 -1
- package/dist/src/components/HistoryItemDisplay.d.ts.map +0 -1
- package/dist/src/components/HistoryItemDisplay.js.map +0 -1
- package/dist/src/components/InputPrompt.d.ts.map +0 -1
- package/dist/src/components/InputPrompt.js.map +0 -1
- package/dist/src/components/LoadingIndicator.d.ts.map +0 -1
- package/dist/src/components/LoadingIndicator.js.map +0 -1
- package/dist/src/components/LoginPrompt.d.ts.map +0 -1
- package/dist/src/components/LoginPrompt.js.map +0 -1
- package/dist/src/components/MainContent.d.ts.map +0 -1
- package/dist/src/components/MainContent.js.map +0 -1
- package/dist/src/components/ModelPicker.d.ts.map +0 -1
- package/dist/src/components/ModelPicker.js.map +0 -1
- package/dist/src/components/SessionPicker.d.ts.map +0 -1
- package/dist/src/components/SessionPicker.js.map +0 -1
- package/dist/src/components/SuggestionsDisplay.d.ts.map +0 -1
- package/dist/src/components/SuggestionsDisplay.js.map +0 -1
- package/dist/src/components/ThemePicker.d.ts.map +0 -1
- package/dist/src/components/ThemePicker.js.map +0 -1
- package/dist/src/components/messages/AssistantMessage.d.ts.map +0 -1
- package/dist/src/components/messages/AssistantMessage.js.map +0 -1
- package/dist/src/components/messages/CommandResult.d.ts.map +0 -1
- package/dist/src/components/messages/CommandResult.js.map +0 -1
- package/dist/src/components/messages/ErrorMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ErrorMessage.js.map +0 -1
- package/dist/src/components/messages/InfoMessage.d.ts.map +0 -1
- package/dist/src/components/messages/InfoMessage.js.map +0 -1
- package/dist/src/components/messages/ModelMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ModelMessage.js.map +0 -1
- package/dist/src/components/messages/SessionMessage.d.ts.map +0 -1
- package/dist/src/components/messages/SessionMessage.js.map +0 -1
- package/dist/src/components/messages/ToolCallMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ToolCallMessage.js.map +0 -1
- package/dist/src/components/messages/UserMessage.d.ts.map +0 -1
- package/dist/src/components/messages/UserMessage.js.map +0 -1
- package/dist/src/components/shared/HorizontalLine.d.ts.map +0 -1
- package/dist/src/components/shared/HorizontalLine.js.map +0 -1
- package/dist/src/components/shared/MarkdownRenderer.d.ts.map +0 -1
- package/dist/src/components/shared/MarkdownRenderer.js.map +0 -1
- package/dist/src/components/shared/Spinner.d.ts.map +0 -1
- package/dist/src/components/shared/Spinner.js.map +0 -1
- package/dist/src/components/shared/TextInput.d.ts.map +0 -1
- package/dist/src/components/shared/TextInput.js.map +0 -1
- package/dist/src/contexts/AppContext.d.ts.map +0 -1
- package/dist/src/contexts/AppContext.js.map +0 -1
- package/dist/src/contexts/ChatContext.d.ts.map +0 -1
- package/dist/src/contexts/ChatContext.js.map +0 -1
- package/dist/src/contexts/KeypressContext.d.ts.map +0 -1
- package/dist/src/contexts/KeypressContext.js.map +0 -1
- package/dist/src/contexts/ModelContext.d.ts.map +0 -1
- package/dist/src/contexts/ModelContext.js.map +0 -1
- package/dist/src/contexts/SessionContext.d.ts.map +0 -1
- package/dist/src/contexts/SessionContext.js.map +0 -1
- package/dist/src/contexts/UIContext.d.ts.map +0 -1
- package/dist/src/contexts/UIContext.js.map +0 -1
- package/dist/src/hooks/useChat.d.ts.map +0 -1
- package/dist/src/hooks/useChat.js.map +0 -1
- package/dist/src/hooks/useFileCompletion.d.ts.map +0 -1
- package/dist/src/hooks/useFileCompletion.js.map +0 -1
- package/dist/src/hooks/useInputHistory.d.ts.map +0 -1
- package/dist/src/hooks/useInputHistory.js.map +0 -1
- package/dist/src/hooks/useKeypress.d.ts.map +0 -1
- package/dist/src/hooks/useKeypress.js.map +0 -1
- package/dist/src/hooks/useLoadingIndicator.d.ts.map +0 -1
- package/dist/src/hooks/useLoadingIndicator.js.map +0 -1
- package/dist/src/hooks/usePasteBuffer.d.ts.map +0 -1
- package/dist/src/hooks/usePasteBuffer.js.map +0 -1
- package/dist/src/hooks/useSlashCommand.d.ts.map +0 -1
- package/dist/src/hooks/useSlashCommand.js.map +0 -1
- package/dist/src/hooks/useStdinInterceptor.d.ts.map +0 -1
- package/dist/src/hooks/useStdinInterceptor.js.map +0 -1
- package/dist/src/hooks/useSymbolCompletion.d.ts.map +0 -1
- package/dist/src/hooks/useSymbolCompletion.js.map +0 -1
- package/dist/src/hooks/useTextBuffer.d.ts.map +0 -1
- package/dist/src/hooks/useTextBuffer.js.map +0 -1
- package/dist/src/main.d.ts.map +0 -1
- package/dist/src/main.js.map +0 -1
- package/dist/src/tools/code-analyzer/config/ignore-service.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/config/ignore-service.js.map +0 -1
- package/dist/src/tools/code-analyzer/config/supported-languages.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/config/supported-languages.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/graph.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/graph.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/types.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/types.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/call-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/call-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/community-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/community-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/import-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/import-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/pipeline.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/pipeline.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/process-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/process-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/utils.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/utils.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.js.map +0 -1
- package/dist/src/tools/code-analyzer/index.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/index.js.map +0 -1
- package/dist/src/tools/code-analyzer/lib/utils.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/lib/utils.js.map +0 -1
- package/dist/src/tools/code-analyzer/types/pipeline.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/types/pipeline.js.map +0 -1
- package/dist/src/types/api.d.ts.map +0 -1
- package/dist/src/types/api.js.map +0 -1
- package/dist/src/types/history.d.ts.map +0 -1
- package/dist/src/types/history.js.map +0 -1
- package/dist/src/utils/colors.d.ts.map +0 -1
- package/dist/src/utils/colors.js.map +0 -1
- package/dist/src/utils/config.d.ts.map +0 -1
- package/dist/src/utils/config.js.map +0 -1
- package/dist/src/utils/constants.d.ts.map +0 -1
- package/dist/src/utils/constants.js.map +0 -1
- package/dist/src/utils/fileRef.d.ts.map +0 -1
- package/dist/src/utils/fileRef.js.map +0 -1
- package/dist/src/utils/fileTunnel.d.ts.map +0 -1
- package/dist/src/utils/fileTunnel.js.map +0 -1
- package/dist/src/utils/formatters.d.ts.map +0 -1
- package/dist/src/utils/formatters.js.map +0 -1
- package/dist/src/utils/pasteUtils.d.ts.map +0 -1
- package/dist/src/utils/pasteUtils.js.map +0 -1
- package/dist/src/utils/skillScanner.d.ts.map +0 -1
- package/dist/src/utils/skillScanner.js.map +0 -1
- package/dist/src/utils/textUtils.d.ts.map +0 -1
- package/dist/src/utils/textUtils.js.map +0 -1
- package/dist/src/utils/updateChecker.d.ts.map +0 -1
- package/dist/src/utils/updateChecker.js.map +0 -1
|
@@ -9,12 +9,60 @@ import { executeFileOperation, isSensitiveOperation, isAlwaysConfirmRequired } f
|
|
|
9
9
|
import { DEBUG_LOG_AITEXT, DEBUG_AITEXT_FILE, CODE_ANALYZER_DIR } from '../utils/config.js';
|
|
10
10
|
import { useChatState, useChatDispatch, StreamingState, } from '../contexts/ChatContext.js';
|
|
11
11
|
import { useModelState } from '../contexts/ModelContext.js';
|
|
12
|
+
/**
|
|
13
|
+
* Throttled token buffer — accumulates tokens and flushes at most every
|
|
14
|
+
* FLUSH_INTERVAL_MS to reduce Ink re-renders and prevent iTerm scroll jitter.
|
|
15
|
+
*/
|
|
16
|
+
const FLUSH_INTERVAL_MS = 50;
|
|
17
|
+
function createTokenBuffer(dispatch) {
|
|
18
|
+
let buffer = '';
|
|
19
|
+
let timer = null;
|
|
20
|
+
function flush() {
|
|
21
|
+
timer = null;
|
|
22
|
+
if (buffer) {
|
|
23
|
+
const content = buffer;
|
|
24
|
+
buffer = '';
|
|
25
|
+
dispatch({ type: 'STREAM_TOKEN', content });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
push(content) {
|
|
30
|
+
buffer += content;
|
|
31
|
+
if (!timer) {
|
|
32
|
+
timer = setTimeout(flush, FLUSH_INTERVAL_MS);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
/** Flush any remaining buffered tokens immediately (e.g. on stream end) */
|
|
36
|
+
flushNow() {
|
|
37
|
+
if (timer) {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
timer = null;
|
|
40
|
+
}
|
|
41
|
+
if (buffer) {
|
|
42
|
+
const content = buffer;
|
|
43
|
+
buffer = '';
|
|
44
|
+
dispatch({ type: 'STREAM_TOKEN', content });
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
dispose() {
|
|
48
|
+
if (timer) {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
timer = null;
|
|
51
|
+
}
|
|
52
|
+
buffer = '';
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
12
56
|
export function useChat() {
|
|
13
57
|
const state = useChatState();
|
|
14
58
|
const dispatch = useChatDispatch();
|
|
15
59
|
const { currentModel } = useModelState();
|
|
16
60
|
const abortRef = useRef(null);
|
|
17
61
|
const toolApprovalResolveRef = useRef(null);
|
|
62
|
+
// Ref 保持 sessionAutoApproved 最新值,避免 stale closure 问题
|
|
63
|
+
// (plan confirm 流式执行中 handleToolExecuteRequest 闭包会捕获旧值)
|
|
64
|
+
const sessionAutoApprovedRef = useRef(state.sessionAutoApproved);
|
|
65
|
+
sessionAutoApprovedRef.current = state.sessionAutoApproved;
|
|
18
66
|
/**
|
|
19
67
|
* 请求用户批准敏感操作。返回 Promise,在用户做出选择后 resolve。
|
|
20
68
|
*/
|
|
@@ -46,9 +94,10 @@ export function useChat() {
|
|
|
46
94
|
name: `[local] ${data.tool_name}`,
|
|
47
95
|
args: JSON.stringify(data.args),
|
|
48
96
|
});
|
|
49
|
-
const sensitive = isSensitiveOperation(data.tool_name);
|
|
97
|
+
const sensitive = isSensitiveOperation(data.tool_name, data.args);
|
|
50
98
|
const alwaysConfirm = isAlwaysConfirmRequired(data);
|
|
51
|
-
|
|
99
|
+
// 使用 ref 读取最新值,避免 stale closure(plan confirm 流式中尤为重要)
|
|
100
|
+
const autoApproved = sessionAutoApprovedRef.current.has(data.tool_name);
|
|
52
101
|
// 需要确认:(1) 敏感操作且未自动批准,或 (2) 始终需要确认的高危操作
|
|
53
102
|
if (sensitive && (!autoApproved || alwaysConfirm)) {
|
|
54
103
|
const decision = await requestToolApproval(data, alwaysConfirm);
|
|
@@ -75,7 +124,7 @@ export function useChat() {
|
|
|
75
124
|
error: 'Failed to submit tunnel result',
|
|
76
125
|
}).catch(() => { });
|
|
77
126
|
}
|
|
78
|
-
}, [dispatch,
|
|
127
|
+
}, [dispatch, requestToolApproval]);
|
|
79
128
|
const sendMessage = useCallback(async (message) => {
|
|
80
129
|
if (state.streamingState !== StreamingState.Idle)
|
|
81
130
|
return;
|
|
@@ -100,6 +149,7 @@ export function useChat() {
|
|
|
100
149
|
dispatch({ type: 'ADD_ITEM', item: { type: 'user', text: displayText } });
|
|
101
150
|
// Enter streaming state immediately so UI shows loading indicator
|
|
102
151
|
dispatch({ type: 'STREAM_START', threadId: state.threadId ?? 'pending' });
|
|
152
|
+
const tokenBuf = createTokenBuffer(dispatch);
|
|
103
153
|
try {
|
|
104
154
|
const reader = await chatApi.stream(finalAiText, state.threadId, currentModel?.model, currentModel?.provider, state.forceMode === 'plan' ? 'plan' : null);
|
|
105
155
|
const decoder = new TextDecoder();
|
|
@@ -126,7 +176,7 @@ export function useChat() {
|
|
|
126
176
|
}
|
|
127
177
|
case 'text': {
|
|
128
178
|
const data = JSON.parse(event.data);
|
|
129
|
-
|
|
179
|
+
tokenBuf.push(data.content);
|
|
130
180
|
break;
|
|
131
181
|
}
|
|
132
182
|
case 'status': {
|
|
@@ -158,10 +208,12 @@ export function useChat() {
|
|
|
158
208
|
break;
|
|
159
209
|
}
|
|
160
210
|
case 'plan_confirm': {
|
|
211
|
+
tokenBuf.flushNow();
|
|
161
212
|
const data = JSON.parse(event.data);
|
|
162
213
|
const planData = data.content || {};
|
|
163
214
|
const steps = planData.plan || [];
|
|
164
|
-
|
|
215
|
+
const planContent = planData.content || '';
|
|
216
|
+
dispatch({ type: 'PLAN_CONFIRM', steps, content: planContent });
|
|
165
217
|
// Stop reading the SSE stream — the plan review UI takes over.
|
|
166
218
|
abort.abort();
|
|
167
219
|
break;
|
|
@@ -192,19 +244,20 @@ export function useChat() {
|
|
|
192
244
|
const planText = '\n**Plan:**\n' +
|
|
193
245
|
meta.plan.map((s, i) => `${i + 1}. ${s}`).join('\n') +
|
|
194
246
|
'\n\n';
|
|
195
|
-
|
|
247
|
+
tokenBuf.push(planText);
|
|
196
248
|
}
|
|
197
249
|
// Show replan result
|
|
198
250
|
if (nodeName === 'replan' && meta.plan) {
|
|
199
251
|
const replanText = '\n**Updated Plan:**\n' +
|
|
200
252
|
meta.plan.map((s, i) => `${i + 1}. ${s}`).join('\n') +
|
|
201
253
|
'\n\n';
|
|
202
|
-
|
|
254
|
+
tokenBuf.push(replanText);
|
|
203
255
|
}
|
|
204
256
|
dispatch({ type: 'TOOL_CALL_DONE' });
|
|
205
257
|
break;
|
|
206
258
|
}
|
|
207
259
|
case 'done': {
|
|
260
|
+
tokenBuf.flushNow();
|
|
208
261
|
dispatch({ type: 'STREAM_DONE' });
|
|
209
262
|
break;
|
|
210
263
|
}
|
|
@@ -233,10 +286,12 @@ export function useChat() {
|
|
|
233
286
|
}
|
|
234
287
|
// If stream ended without a done event, finalize
|
|
235
288
|
if (!streamDoneReceived && !abort.signal.aborted) {
|
|
289
|
+
tokenBuf.flushNow();
|
|
236
290
|
dispatch({ type: 'STREAM_DONE' });
|
|
237
291
|
}
|
|
238
292
|
}
|
|
239
293
|
catch (err) {
|
|
294
|
+
tokenBuf.dispose();
|
|
240
295
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
241
296
|
dispatch({ type: 'STREAM_ERROR', error: errorMessage });
|
|
242
297
|
}
|
|
@@ -249,6 +304,7 @@ export function useChat() {
|
|
|
249
304
|
const decoder = new TextDecoder();
|
|
250
305
|
const abort = new AbortController();
|
|
251
306
|
abortRef.current = abort;
|
|
307
|
+
const tokenBuf = createTokenBuffer(dispatch);
|
|
252
308
|
dispatch({ type: 'STREAM_START', threadId: state.threadId ?? 'pending' });
|
|
253
309
|
const parser = createParser({
|
|
254
310
|
onEvent(event) {
|
|
@@ -265,7 +321,7 @@ export function useChat() {
|
|
|
265
321
|
}
|
|
266
322
|
case 'text': {
|
|
267
323
|
const data = JSON.parse(event.data);
|
|
268
|
-
|
|
324
|
+
tokenBuf.push(data.content);
|
|
269
325
|
break;
|
|
270
326
|
}
|
|
271
327
|
case 'tool_call': {
|
|
@@ -305,23 +361,27 @@ export function useChat() {
|
|
|
305
361
|
if (data.content === 'replan' && meta.plan) {
|
|
306
362
|
const text = '\n**Updated Plan:**\n' +
|
|
307
363
|
meta.plan.map((s, i) => `${i + 1}. ${s}`).join('\n') + '\n\n';
|
|
308
|
-
|
|
364
|
+
tokenBuf.push(text);
|
|
309
365
|
}
|
|
310
366
|
dispatch({ type: 'TOOL_CALL_DONE' });
|
|
311
367
|
break;
|
|
312
368
|
}
|
|
313
369
|
case 'plan_confirm': {
|
|
370
|
+
tokenBuf.flushNow();
|
|
314
371
|
const data = JSON.parse(event.data);
|
|
315
372
|
const planData = data.content || {};
|
|
316
373
|
const steps = planData.plan || [];
|
|
317
|
-
|
|
374
|
+
const planContent = planData.content || '';
|
|
375
|
+
dispatch({ type: 'PLAN_CONFIRM', steps, content: planContent });
|
|
318
376
|
break;
|
|
319
377
|
}
|
|
320
378
|
case 'done': {
|
|
379
|
+
tokenBuf.flushNow();
|
|
321
380
|
dispatch({ type: 'STREAM_DONE' });
|
|
322
381
|
break;
|
|
323
382
|
}
|
|
324
383
|
case 'error': {
|
|
384
|
+
tokenBuf.dispose();
|
|
325
385
|
const data = JSON.parse(event.data);
|
|
326
386
|
dispatch({ type: 'STREAM_ERROR', error: data.content });
|
|
327
387
|
break;
|
|
@@ -342,6 +402,7 @@ export function useChat() {
|
|
|
342
402
|
parser.feed(text);
|
|
343
403
|
}
|
|
344
404
|
if (!streamDoneReceived && !abort.signal.aborted) {
|
|
405
|
+
tokenBuf.flushNow();
|
|
345
406
|
dispatch({ type: 'STREAM_DONE' });
|
|
346
407
|
}
|
|
347
408
|
abortRef.current = null;
|
package/dist/src/main.js
CHANGED
|
@@ -9,7 +9,7 @@ import { toolsApi } from './api/tools.js';
|
|
|
9
9
|
import { skillsApi } from './api/skills.js';
|
|
10
10
|
import { scanLocalSkills } from './utils/skillScanner.js';
|
|
11
11
|
import { sessionsApi } from './api/sessions.js';
|
|
12
|
-
import { getServerUrl, loadToken, saveToken, clearToken, loadTheme, loadModel } from './utils/config.js';
|
|
12
|
+
import { getServerUrl, loadToken, saveToken, clearToken, loadTheme, loadModel, saveRefreshToken, loadRefreshToken } from './utils/config.js';
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { setTheme, palette } from './utils/colors.js';
|
|
15
15
|
import { VERSION } from './utils/constants.js';
|
|
@@ -64,7 +64,6 @@ export async function main() {
|
|
|
64
64
|
const muted = chalk.hex(palette.textMuted);
|
|
65
65
|
const accent = chalk.hex(palette.accent);
|
|
66
66
|
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
67
|
-
// Detect specific failure reasons
|
|
68
67
|
let reason;
|
|
69
68
|
let hint;
|
|
70
69
|
if (rawMsg.includes('ECONNREFUSED') || rawMsg.includes('fetch failed')) {
|
|
@@ -101,6 +100,41 @@ export async function main() {
|
|
|
101
100
|
catch {
|
|
102
101
|
authStatus = { auth_enabled: false };
|
|
103
102
|
}
|
|
103
|
+
// Token refresh (Supabase tokens expire in ~1 hour)
|
|
104
|
+
let refreshTimer = null;
|
|
105
|
+
const startTokenRefresh = () => {
|
|
106
|
+
if (refreshTimer)
|
|
107
|
+
return;
|
|
108
|
+
const supabaseUrl = authStatus.supabase_url;
|
|
109
|
+
const supabaseKey = authStatus.supabase_anon_key;
|
|
110
|
+
if (!supabaseUrl || !supabaseKey)
|
|
111
|
+
return;
|
|
112
|
+
refreshTimer = setInterval(async () => {
|
|
113
|
+
const rt = loadRefreshToken();
|
|
114
|
+
if (!rt)
|
|
115
|
+
return;
|
|
116
|
+
try {
|
|
117
|
+
const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
'apikey': supabaseKey,
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({ refresh_token: rt }),
|
|
124
|
+
});
|
|
125
|
+
if (res.ok) {
|
|
126
|
+
const data = await res.json();
|
|
127
|
+
client.setToken(data.access_token);
|
|
128
|
+
saveToken(data.access_token);
|
|
129
|
+
saveRefreshToken(data.refresh_token);
|
|
130
|
+
token = data.access_token;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Silent — will retry next interval
|
|
135
|
+
}
|
|
136
|
+
}, 50 * 60 * 1000); // 50 minutes
|
|
137
|
+
};
|
|
104
138
|
// Try to restore token
|
|
105
139
|
let token = null;
|
|
106
140
|
let username = null;
|
|
@@ -114,6 +148,7 @@ export async function main() {
|
|
|
114
148
|
token = savedToken;
|
|
115
149
|
username = me.username;
|
|
116
150
|
isAuthenticated = true;
|
|
151
|
+
startTokenRefresh();
|
|
117
152
|
}
|
|
118
153
|
catch {
|
|
119
154
|
client.setToken(null);
|
|
@@ -167,16 +202,27 @@ export async function main() {
|
|
|
167
202
|
];
|
|
168
203
|
// Login handler
|
|
169
204
|
let loginError = null;
|
|
170
|
-
|
|
205
|
+
// Terminal size — tracked outside React so rerender() can inject fresh values
|
|
206
|
+
// This mirrors the claude-code pattern: Ink's renderer owns resize detection,
|
|
207
|
+
// and we piggyback by re-calling render()/rerender() with updated props.
|
|
208
|
+
const getTerminalSize = () => ({
|
|
209
|
+
terminalWidth: process.stdout.columns || 80,
|
|
210
|
+
terminalHeight: process.stdout.rows || 24,
|
|
211
|
+
});
|
|
212
|
+
const renderApp = (overrides = {}) => (_jsx(App, { serverUrl: serverUrl, token: overrides.token ?? token, username: overrides.username ?? username, authEnabled: authStatus.auth_enabled, version: healthData?.version ?? VERSION, healthData: healthData, availableCommands: commands, availableTools: tools, initialModels: models, initialCurrentModel: currentModel, initialSessions: initialSessions, initialTheme: initialTheme, onLogin: handleLogin, onLogout: handleLogout, loginError: overrides.loginError ?? loginError, isAuthenticated: overrides.isAuthenticated ?? isAuthenticated, ...getTerminalSize() }));
|
|
171
213
|
const handleLogin = async (user, pass) => {
|
|
172
214
|
try {
|
|
173
215
|
const res = await authApi.login(user, pass);
|
|
174
216
|
client.setToken(res.access_token);
|
|
175
217
|
saveToken(res.access_token);
|
|
218
|
+
if (res.refresh_token) {
|
|
219
|
+
saveRefreshToken(res.refresh_token);
|
|
220
|
+
}
|
|
176
221
|
token = res.access_token;
|
|
177
222
|
username = res.user.username;
|
|
178
223
|
isAuthenticated = true;
|
|
179
224
|
loginError = null;
|
|
225
|
+
startTokenRefresh();
|
|
180
226
|
rerender(renderApp({ token, username, isAuthenticated: true, loginError: null }));
|
|
181
227
|
}
|
|
182
228
|
catch (err) {
|
|
@@ -185,6 +231,10 @@ export async function main() {
|
|
|
185
231
|
}
|
|
186
232
|
};
|
|
187
233
|
const handleLogout = () => {
|
|
234
|
+
if (refreshTimer) {
|
|
235
|
+
clearInterval(refreshTimer);
|
|
236
|
+
refreshTimer = null;
|
|
237
|
+
}
|
|
188
238
|
client.setToken(null);
|
|
189
239
|
clearToken();
|
|
190
240
|
token = null;
|
|
@@ -199,9 +249,17 @@ export async function main() {
|
|
|
199
249
|
process.stderr.write(formatUpdateMessage(updateInfo) + '\n\n');
|
|
200
250
|
}
|
|
201
251
|
// Render the app
|
|
202
|
-
const { rerender } = render(renderApp(), {
|
|
252
|
+
const { rerender, clear } = render(renderApp(), {
|
|
203
253
|
exitOnCtrlC: false,
|
|
204
254
|
kittyKeyboard: { mode: 'auto' },
|
|
205
255
|
});
|
|
256
|
+
// On resize: clear the screen first to avoid stale rows left over when the
|
|
257
|
+
// Header shrinks (e.g. horizontal→compact reduces height by ~10 rows and
|
|
258
|
+
// Ink's in-place diff won't erase the leftover lines). Then rerender with
|
|
259
|
+
// fresh terminal dimensions.
|
|
260
|
+
process.stdout.on('resize', () => {
|
|
261
|
+
clear();
|
|
262
|
+
rerender(renderApp());
|
|
263
|
+
});
|
|
206
264
|
}
|
|
207
265
|
//# sourceMappingURL=main.js.map
|
package/dist/src/types/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface LoginResponse {
|
|
2
2
|
success: boolean;
|
|
3
3
|
access_token: string;
|
|
4
|
+
refresh_token?: string;
|
|
4
5
|
token_type: string;
|
|
5
6
|
user: {
|
|
6
7
|
user_id: string;
|
|
@@ -13,9 +14,12 @@ export interface UserInfo {
|
|
|
13
14
|
user_id: string;
|
|
14
15
|
username: string;
|
|
15
16
|
display_name: string;
|
|
17
|
+
is_admin?: boolean;
|
|
16
18
|
}
|
|
17
19
|
export interface AuthStatus {
|
|
18
20
|
auth_enabled: boolean;
|
|
21
|
+
supabase_url?: string;
|
|
22
|
+
supabase_anon_key?: string;
|
|
19
23
|
}
|
|
20
24
|
export interface SessionInfo {
|
|
21
25
|
thread_id: string;
|
|
@@ -3,6 +3,8 @@ export declare function getServerUrl(cliArg?: string): string;
|
|
|
3
3
|
export declare function saveToken(token: string): void;
|
|
4
4
|
export declare function loadToken(): string | null;
|
|
5
5
|
export declare function clearToken(): void;
|
|
6
|
+
export declare function saveRefreshToken(token: string): void;
|
|
7
|
+
export declare function loadRefreshToken(): string | null;
|
|
6
8
|
export type ThemeMode = 'dark' | 'light';
|
|
7
9
|
export declare function saveTheme(mode: ThemeMode): void;
|
|
8
10
|
export declare function loadTheme(): ThemeMode;
|
package/dist/src/utils/config.js
CHANGED
|
@@ -6,6 +6,8 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
const CONFIG_DIR = join(homedir(), '.mistagent');
|
|
7
7
|
// 登录 token 文件路径,文件权限 0o600 仅当前用户可读写
|
|
8
8
|
const TOKEN_FILE = join(CONFIG_DIR, 'token');
|
|
9
|
+
// Supabase refresh token(用于自动续期 access_token)
|
|
10
|
+
const REFRESH_TOKEN_FILE = join(CONFIG_DIR, 'refresh_token');
|
|
9
11
|
// 后端服务器默认地址,可通过 CLI 参数或环境变量 MISTAGENT_SERVER 覆盖
|
|
10
12
|
// 发布时由 publish.sh 自动切换为 'https://solidity.slowmist.ai',发布后自动改回
|
|
11
13
|
export const DEFAULT_SERVER_URL = 'https://solidity.slowmist.ai';
|
|
@@ -41,11 +43,32 @@ export function loadToken() {
|
|
|
41
43
|
export function clearToken() {
|
|
42
44
|
try {
|
|
43
45
|
writeFileSync(TOKEN_FILE, '', { mode: 0o600 });
|
|
46
|
+
writeFileSync(REFRESH_TOKEN_FILE, '', { mode: 0o600 });
|
|
44
47
|
}
|
|
45
48
|
catch {
|
|
46
49
|
// 写入失败时静默处理
|
|
47
50
|
}
|
|
48
51
|
}
|
|
52
|
+
// 保存 refresh token(Supabase token 续期用)
|
|
53
|
+
export function saveRefreshToken(token) {
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
56
|
+
writeFileSync(REFRESH_TOKEN_FILE, token, { mode: 0o600 });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// 写入失败时静默处理
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 加载 refresh token
|
|
63
|
+
export function loadRefreshToken() {
|
|
64
|
+
try {
|
|
65
|
+
const val = readFileSync(REFRESH_TOKEN_FILE, 'utf-8').trim();
|
|
66
|
+
return val || null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
49
72
|
// ─── 主题偏好 ────────────────────────────────────────────────
|
|
50
73
|
// 主题偏好文件路径,保存 'dark' 或 'light'
|
|
51
74
|
const THEME_FILE = join(CONFIG_DIR, 'theme');
|
|
@@ -23,7 +23,12 @@ export interface TunnelResult {
|
|
|
23
23
|
* 工具名称和参数与 LangChain FileManagementToolkit 保持一致。
|
|
24
24
|
*/
|
|
25
25
|
export declare function executeFileOperation(req: TunnelRequest): Promise<TunnelResult>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 是否为需要用户确认的敏感操作
|
|
28
|
+
* - 写入/修改工具:始终 sensitive
|
|
29
|
+
* - bash_shell_run:只读命令不 sensitive,其他命令 sensitive
|
|
30
|
+
* - read_file / list_directory 等只读工具:不 sensitive
|
|
31
|
+
*/
|
|
32
|
+
export declare function isSensitiveOperation(toolName: string, args?: Record<string, unknown>): boolean;
|
|
28
33
|
export declare function isAlwaysConfirmRequired(req: TunnelRequest): boolean;
|
|
29
34
|
//# sourceMappingURL=fileTunnel.d.ts.map
|