aegiscode 3.1.8 → 3.1.10
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/README.md +23 -15
- package/dist/main.js +549 -552
- package/package.json +4 -2
- package/src/agent/Agent.ts +903 -0
- package/src/agent/SimpleAgent.ts +48 -0
- package/src/agent/index.ts +54 -0
- package/src/agent/orchestrator/AppBuilder.ts +443 -0
- package/src/agent/orchestrator/CouncilAgent.ts +310 -0
- package/src/agent/orchestrator/DiscussionRoom.ts +462 -0
- package/src/agent/orchestrator/OrchestratorAgent.ts +637 -0
- package/src/agent/orchestrator/index.ts +38 -0
- package/src/agent/orchestrator/utils.ts +397 -0
- package/src/agent/pricing.ts +115 -0
- package/src/agent/router.ts +74 -0
- package/src/agent/routerStats.ts +121 -0
- package/src/agent/types.ts +318 -0
- package/src/auth/login.ts +383 -0
- package/src/cli/config.ts +189 -0
- package/src/cli/index.ts +17 -0
- package/src/cli/middleware.ts +119 -0
- package/src/cli/types.ts +75 -0
- package/src/config/ConfigManager.ts +587 -0
- package/src/config/index.ts +7 -0
- package/src/config/types.ts +584 -0
- package/src/context/CompactionService.ts +300 -0
- package/src/context/ContextManager.ts +450 -0
- package/src/context/FileAnalyzer.ts +267 -0
- package/src/context/TokenCounter.ts +265 -0
- package/src/context/index.ts +27 -0
- package/src/context/storage/CacheStore.ts +176 -0
- package/src/context/storage/JSONLStore.ts +201 -0
- package/src/context/storage/MemoryStore.ts +205 -0
- package/src/context/storage/PersistentStore.ts +327 -0
- package/src/context/storage/index.ts +9 -0
- package/src/context/storage/pathUtils.ts +114 -0
- package/src/context/test.ts +309 -0
- package/src/context/types.ts +268 -0
- package/src/hooks/HookExecutor.ts +434 -0
- package/src/hooks/HookManager.ts +596 -0
- package/src/hooks/HookService.ts +269 -0
- package/src/hooks/Matcher.ts +157 -0
- package/src/hooks/index.ts +63 -0
- package/src/hooks/types.ts +424 -0
- package/src/main.tsx +596 -0
- package/src/mcp/HealthMonitor.ts +150 -0
- package/src/mcp/McpClient.ts +491 -0
- package/src/mcp/McpRegistry.ts +321 -0
- package/src/mcp/createMcpTool.ts +251 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/server.ts +334 -0
- package/src/mcp/test-server.ts +88 -0
- package/src/mcp/test.ts +372 -0
- package/src/mcp/types.ts +247 -0
- package/src/memory/AgentMemoryBus.ts +432 -0
- package/src/memory/CloudSync.ts +99 -0
- package/src/memory/DriveSync.ts +106 -0
- package/src/memory/SharedMemory.ts +951 -0
- package/src/memory/index.ts +14 -0
- package/src/memory/machineFingerprint.ts +40 -0
- package/src/orchestrator/SubAgentMetadata.ts +136 -0
- package/src/prompts/builder.ts +213 -0
- package/src/prompts/default.ts +144 -0
- package/src/prompts/index.ts +16 -0
- package/src/prompts/plan.ts +64 -0
- package/src/prompts/test.ts +78 -0
- package/src/services/AnthropicChatService.ts +341 -0
- package/src/services/ChatService.ts +347 -0
- package/src/services/ClaudeCliChatService.ts +256 -0
- package/src/services/CloudSync.ts +168 -0
- package/src/services/CostLedger.ts +211 -0
- package/src/services/Heartbeat.ts +135 -0
- package/src/services/LearningCollector.ts +291 -0
- package/src/services/OllamaInstaller.ts +342 -0
- package/src/services/VersionChecker.ts +445 -0
- package/src/services/index.ts +57 -0
- package/src/services/streaming/OpenAIEventAdapter.ts +126 -0
- package/src/services/streaming/RenderingProfile.ts +90 -0
- package/src/services/streaming/StreamEventParser.ts +181 -0
- package/src/services/streaming/ThrottledRenderer.ts +139 -0
- package/src/services/streaming/TranscriptBuffer.ts +574 -0
- package/src/services/streaming/eventStatusMap.ts +52 -0
- package/src/services/streaming/index.ts +46 -0
- package/src/services/streaming/renderFormatting.ts +79 -0
- package/src/services/streaming/types.ts +234 -0
- package/src/skills/SkillLoader.ts +126 -0
- package/src/skills/SkillRegistry.ts +366 -0
- package/src/skills/index.ts +48 -0
- package/src/skills/types.ts +146 -0
- package/src/slash-commands/billing.ts +70 -0
- package/src/slash-commands/build.ts +413 -0
- package/src/slash-commands/builtinCommands.ts +2733 -0
- package/src/slash-commands/clone.ts +242 -0
- package/src/slash-commands/council.ts +125 -0
- package/src/slash-commands/custom/CustomCommandExecutor.ts +143 -0
- package/src/slash-commands/custom/CustomCommandLoader.ts +251 -0
- package/src/slash-commands/custom/CustomCommandRegistry.ts +144 -0
- package/src/slash-commands/custom/index.ts +7 -0
- package/src/slash-commands/debate.ts +254 -0
- package/src/slash-commands/gmail.ts +105 -0
- package/src/slash-commands/index.ts +388 -0
- package/src/slash-commands/mcpCommand.ts +205 -0
- package/src/slash-commands/types.ts +201 -0
- package/src/store/index.ts +76 -0
- package/src/store/selectors.ts +246 -0
- package/src/store/slices/appSlice.ts +205 -0
- package/src/store/slices/commandSlice.ts +115 -0
- package/src/store/slices/configSlice.ts +46 -0
- package/src/store/slices/focusSlice.ts +64 -0
- package/src/store/slices/index.ts +9 -0
- package/src/store/slices/sessionSlice.ts +424 -0
- package/src/store/streaming-buffer.ts +425 -0
- package/src/store/test.ts +296 -0
- package/src/store/types.ts +274 -0
- package/src/store/vanilla.ts +186 -0
- package/src/tools/builtin/bash.ts +236 -0
- package/src/tools/builtin/council.ts +105 -0
- package/src/tools/builtin/edit.ts +213 -0
- package/src/tools/builtin/glob.ts +136 -0
- package/src/tools/builtin/grep.ts +263 -0
- package/src/tools/builtin/index.ts +61 -0
- package/src/tools/builtin/memory.ts +66 -0
- package/src/tools/builtin/read.ts +168 -0
- package/src/tools/builtin/skill.ts +97 -0
- package/src/tools/builtin/snapshot.ts +40 -0
- package/src/tools/builtin/task.ts +106 -0
- package/src/tools/builtin/write.ts +134 -0
- package/src/tools/createTool.ts +221 -0
- package/src/tools/execution/ExecutionPipeline.ts +263 -0
- package/src/tools/execution/index.ts +40 -0
- package/src/tools/execution/stages/CacheStage.ts +131 -0
- package/src/tools/execution/stages/ConfirmationStage.ts +203 -0
- package/src/tools/execution/stages/DiscoveryStage.ts +46 -0
- package/src/tools/execution/stages/ExecutionStage.ts +48 -0
- package/src/tools/execution/stages/FormattingStage.ts +44 -0
- package/src/tools/execution/stages/HookStage.ts +72 -0
- package/src/tools/execution/stages/PermissionStage.ts +287 -0
- package/src/tools/execution/stages/PostHookStage.ts +71 -0
- package/src/tools/execution/stages/index.ts +12 -0
- package/src/tools/execution/test.ts +266 -0
- package/src/tools/execution/types.ts +273 -0
- package/src/tools/index.ts +81 -0
- package/src/tools/registry.ts +304 -0
- package/src/tools/schemas.ts +109 -0
- package/src/tools/test.ts +220 -0
- package/src/tools/types.ts +175 -0
- package/src/tools/validation/PermissionChecker.ts +242 -0
- package/src/tools/validation/SensitiveFileDetector.ts +210 -0
- package/src/tools/validation/index.ts +11 -0
- package/src/ui/App.tsx +166 -0
- package/src/ui/components/AegisInterface.tsx +484 -0
- package/src/ui/components/common/ChatSearch.tsx +150 -0
- package/src/ui/components/common/ErrorBoundary.tsx +82 -0
- package/src/ui/components/common/ExitMessage.tsx +120 -0
- package/src/ui/components/common/LoadingIndicator.tsx +49 -0
- package/src/ui/components/common/index.ts +6 -0
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +208 -0
- package/src/ui/components/dialog/InteractiveSelector.tsx +149 -0
- package/src/ui/components/dialog/SetupWizard.tsx +297 -0
- package/src/ui/components/dialog/UpdatePrompt.tsx +155 -0
- package/src/ui/components/dialog/index.ts +8 -0
- package/src/ui/components/index.ts +28 -0
- package/src/ui/components/input/CommandSuggestions.tsx +139 -0
- package/src/ui/components/input/CustomTextInput.tsx +220 -0
- package/src/ui/components/input/InputArea.tsx +361 -0
- package/src/ui/components/input/PromptSuggestions.tsx +66 -0
- package/src/ui/components/input/index.ts +6 -0
- package/src/ui/components/layout/ChatStatusBar.tsx +90 -0
- package/src/ui/components/layout/ContextBar.tsx +79 -0
- package/src/ui/components/layout/MessageArea.tsx +96 -0
- package/src/ui/components/layout/MessageList.tsx +647 -0
- package/src/ui/components/layout/MessageSeparator.tsx +26 -0
- package/src/ui/components/layout/WelcomeMessage.tsx +93 -0
- package/src/ui/components/layout/index.ts +7 -0
- package/src/ui/components/markdown/CodeHighlighter.tsx +292 -0
- package/src/ui/components/markdown/MessageRenderer.tsx +1211 -0
- package/src/ui/components/markdown/index.ts +8 -0
- package/src/ui/components/markdown/parser.ts +336 -0
- package/src/ui/components/markdown/types.ts +66 -0
- package/src/ui/focus/FocusManager.ts +137 -0
- package/src/ui/focus/index.ts +13 -0
- package/src/ui/focus/types.ts +54 -0
- package/src/ui/focus/useFocus.ts +75 -0
- package/src/ui/hooks/index.ts +11 -0
- package/src/ui/hooks/useAgent.ts +284 -0
- package/src/ui/hooks/useCommandHistory.ts +87 -0
- package/src/ui/hooks/useCommandProcessor.ts +443 -0
- package/src/ui/hooks/useConfirmation.ts +99 -0
- package/src/ui/hooks/useCtrlCHandler.ts +100 -0
- package/src/ui/hooks/useInputBuffer.ts +122 -0
- package/src/ui/hooks/useTerminalSize.ts +68 -0
- package/src/ui/hooks/useTerminalWidth.ts +5 -0
- package/src/ui/hooks/useWindowedList.ts +118 -0
- package/src/ui/render-debugger.ts +621 -0
- package/src/ui/test.ts +189 -0
- package/src/ui/themes/ThemeManager.ts +332 -0
- package/src/ui/themes/aegisTheme.ts +87 -0
- package/src/ui/themes/darkTheme.ts +87 -0
- package/src/ui/themes/defaultTheme.ts +85 -0
- package/src/ui/themes/index.ts +10 -0
- package/src/ui/themes/lightTheme.ts +89 -0
- package/src/ui/themes/popularThemes.ts +187 -0
- package/src/ui/themes/types.ts +130 -0
- package/src/utils/clipboard.ts +48 -0
- package/src/utils/debug.ts +43 -0
- package/src/utils/environment.ts +68 -0
- package/src/utils/index.ts +10 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAgent - Agent lifecycle hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts agent initialization, context management, and model switching
|
|
5
|
+
* from AegisInterface into a focused, testable hook.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useRef, useCallback, useState } from 'react';
|
|
9
|
+
import { Agent } from '../../agent/Agent.js';
|
|
10
|
+
import { ContextManager } from '../../context/index.js';
|
|
11
|
+
import { sessionActions, appActions, configActions, getState, subscribe } from '../../store/index.js';
|
|
12
|
+
import type { ModelConfig } from '../../config/types.js';
|
|
13
|
+
|
|
14
|
+
export interface UseAgentOptions {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
resumeSessionId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface UseAgentResult {
|
|
23
|
+
agentRef: React.MutableRefObject<Agent | null>;
|
|
24
|
+
contextManagerRef: React.MutableRefObject<ContextManager | null>;
|
|
25
|
+
isInitializing: boolean;
|
|
26
|
+
initError: string | null;
|
|
27
|
+
currentModel: string | undefined;
|
|
28
|
+
setCurrentModel: React.Dispatch<React.SetStateAction<string | undefined>>;
|
|
29
|
+
handleSetupComplete: () => Promise<void>;
|
|
30
|
+
getAgent: () => Agent | null;
|
|
31
|
+
getContextManager: () => ContextManager | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function initHooks(sessionId: string): Promise<void> {
|
|
35
|
+
try {
|
|
36
|
+
const { ConfigManager } = await import('../../config/index.js');
|
|
37
|
+
const cm = ConfigManager.getInstance();
|
|
38
|
+
const { initializeHooks, onSessionStart } = await import('../../hooks/index.js');
|
|
39
|
+
initializeHooks(cm.getConfig().hooks || {});
|
|
40
|
+
await onSessionStart(sessionId, process.cwd());
|
|
41
|
+
} catch { /* non-fatal */ }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function useAgent(options: UseAgentOptions): UseAgentResult {
|
|
45
|
+
const { apiKey, baseURL, model, debug, resumeSessionId } = options;
|
|
46
|
+
|
|
47
|
+
const agentRef = useRef<Agent | null>(null);
|
|
48
|
+
const contextManagerRef = useRef<ContextManager | null>(null);
|
|
49
|
+
const [isInitializing, setIsInitializing] = useState(true);
|
|
50
|
+
const [initError, setInitError] = useState<string | null>(null);
|
|
51
|
+
|
|
52
|
+
const currentModelIdRef = useRef(model);
|
|
53
|
+
const [currentModel, setCurrentModel] = useState(model);
|
|
54
|
+
|
|
55
|
+
const getAgent = useCallback(() => agentRef.current, []);
|
|
56
|
+
const getContextManager = useCallback(() => contextManagerRef.current, []);
|
|
57
|
+
|
|
58
|
+
// Stable option refs (prevents recreation of callbacks)
|
|
59
|
+
const debugRef = useRef(debug);
|
|
60
|
+
debugRef.current = debug;
|
|
61
|
+
const modelRef = useRef(model);
|
|
62
|
+
modelRef.current = model;
|
|
63
|
+
|
|
64
|
+
// ==================== Setup Completion ====================
|
|
65
|
+
const handleSetupComplete = useCallback(async () => {
|
|
66
|
+
setInitError(null);
|
|
67
|
+
setIsInitializing(true);
|
|
68
|
+
try {
|
|
69
|
+
const { ConfigManager } = await import('../../config/index.js');
|
|
70
|
+
const cm = ConfigManager.getInstance();
|
|
71
|
+
await cm.initialize();
|
|
72
|
+
const newModel = cm.getDefaultModel();
|
|
73
|
+
|
|
74
|
+
// If we already have a ContextManager (e.g. session was loaded earlier),
|
|
75
|
+
// preserve it instead of creating a new one
|
|
76
|
+
if (!contextManagerRef.current) {
|
|
77
|
+
contextManagerRef.current = new ContextManager({ compressionThreshold: 100000 });
|
|
78
|
+
}
|
|
79
|
+
const ctxManager = contextManagerRef.current;
|
|
80
|
+
|
|
81
|
+
// Only create a new session if none exists
|
|
82
|
+
let sid = ctxManager.getCurrentSessionId();
|
|
83
|
+
if (!sid) {
|
|
84
|
+
sid = await ctxManager.createSession();
|
|
85
|
+
}
|
|
86
|
+
sessionActions().setSessionId(sid);
|
|
87
|
+
|
|
88
|
+
agentRef.current = await Agent.create({
|
|
89
|
+
apiKey: newModel.apiKey!,
|
|
90
|
+
baseURL: newModel.baseURL,
|
|
91
|
+
model: newModel.model!,
|
|
92
|
+
requireConfirmation: newModel.requireConfirmation,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const { initializeCustomCommands } = await import('../../slash-commands/index.js');
|
|
96
|
+
await initializeCustomCommands(process.cwd());
|
|
97
|
+
|
|
98
|
+
import('../../skills/index.js').then(({ initializeSkills }) => {
|
|
99
|
+
initializeSkills(process.cwd()).catch(() => {});
|
|
100
|
+
}).catch(() => {});
|
|
101
|
+
|
|
102
|
+
await initHooks(sid);
|
|
103
|
+
|
|
104
|
+
setIsInitializing(false);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
setInitError(err instanceof Error ? err.message : String(err));
|
|
107
|
+
setIsInitializing(false);
|
|
108
|
+
}
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
// ==================== Initialization ====================
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const initAgent = async () => {
|
|
114
|
+
if (getState().app.initializationStatus === 'needsSetup') return;
|
|
115
|
+
try {
|
|
116
|
+
if (debugRef.current) {
|
|
117
|
+
console.log('[DEBUG] Initializing Agent and ContextManager...');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const ctxManager = new ContextManager({ compressionThreshold: 100000 });
|
|
121
|
+
contextManagerRef.current = ctxManager;
|
|
122
|
+
|
|
123
|
+
let currentSessionId: string;
|
|
124
|
+
|
|
125
|
+
if (resumeSessionId) {
|
|
126
|
+
const loaded = await ctxManager.loadSession(resumeSessionId);
|
|
127
|
+
if (loaded) {
|
|
128
|
+
currentSessionId = resumeSessionId;
|
|
129
|
+
const contextMessages = ctxManager.getMessages();
|
|
130
|
+
// Batch-restore all messages in a single store update so MessageList's
|
|
131
|
+
// scrollLineOffset initializes correctly (showing the bottom of history).
|
|
132
|
+
// Adding messages one-by-one via addUserMessage/addAssistantMessage skips
|
|
133
|
+
// auto-scroll for assistant messages, leaving scrollLineOffset at 0 (top).
|
|
134
|
+
const restoredMessages: import('../../store/types.js').SessionMessage[] =
|
|
135
|
+
contextMessages
|
|
136
|
+
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
137
|
+
.map((m, i) => ({
|
|
138
|
+
id: `restored-${i}-${Date.now()}`,
|
|
139
|
+
role: m.role as 'user' | 'assistant',
|
|
140
|
+
content: m.content,
|
|
141
|
+
timestamp: m.timestamp || Date.now(),
|
|
142
|
+
}));
|
|
143
|
+
sessionActions().restoreSession(currentSessionId, restoredMessages);
|
|
144
|
+
if (debugRef.current) {
|
|
145
|
+
console.log('[DEBUG] Loaded session with', contextMessages.length, 'messages');
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
if (debugRef.current) {
|
|
149
|
+
console.log('[DEBUG] Failed to load session, creating new one');
|
|
150
|
+
}
|
|
151
|
+
currentSessionId = await ctxManager.createSession();
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
currentSessionId = await ctxManager.createSession();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
sessionActions().setSessionId(currentSessionId);
|
|
158
|
+
|
|
159
|
+
const { ConfigManager } = await import('../../config/index.js');
|
|
160
|
+
const cm = ConfigManager.getInstance();
|
|
161
|
+
await cm.initialize();
|
|
162
|
+
const requireConfirmation = cm.getDefaultModel().requireConfirmation;
|
|
163
|
+
|
|
164
|
+
agentRef.current = await Agent.create({ apiKey, baseURL, model, requireConfirmation });
|
|
165
|
+
|
|
166
|
+
const { initializeCustomCommands } = await import('../../slash-commands/index.js');
|
|
167
|
+
const customCmdResult = await initializeCustomCommands(process.cwd());
|
|
168
|
+
if (debugRef.current && customCmdResult.count > 0) {
|
|
169
|
+
console.log('[DEBUG] Loaded', customCmdResult.count, 'custom commands');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
import('../../skills/index.js').then(({ initializeSkills }) => {
|
|
173
|
+
initializeSkills(process.cwd()).catch(() => {});
|
|
174
|
+
}).catch(() => {});
|
|
175
|
+
|
|
176
|
+
await initHooks(currentSessionId);
|
|
177
|
+
|
|
178
|
+
setIsInitializing(false);
|
|
179
|
+
|
|
180
|
+
if (debugRef.current) {
|
|
181
|
+
console.log('[DEBUG] Agent initialized successfully, sessionId:', currentSessionId);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
setInitError(error instanceof Error ? error.message : '');
|
|
185
|
+
setIsInitializing(false);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
initAgent();
|
|
190
|
+
|
|
191
|
+
return () => {
|
|
192
|
+
contextManagerRef.current?.cleanup().catch(() => {});
|
|
193
|
+
};
|
|
194
|
+
}, [apiKey, baseURL, model, debug, resumeSessionId]);
|
|
195
|
+
|
|
196
|
+
// ==================== Model Switch Subscription ====================
|
|
197
|
+
// Tracks both which model is active AND that model's own settings
|
|
198
|
+
// (requireConfirmation/allowedTools/disallowedTools), since the running
|
|
199
|
+
// Agent instance freezes these at creation time. Without watching the
|
|
200
|
+
// settings too, toggling e.g. `/confirm off` for the *currently active*
|
|
201
|
+
// model would silently have no effect until the user switched models away
|
|
202
|
+
// and back.
|
|
203
|
+
const currentModelSettingsRef = useRef<string>('');
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
// Seed the snapshot from current state so the first store change after
|
|
206
|
+
// mount doesn't look like a settings change and trigger a spurious rebuild.
|
|
207
|
+
const initialModels = getState().config.config?.models || [];
|
|
208
|
+
const initialFound = currentModelIdRef.current
|
|
209
|
+
? initialModels.find((m: ModelConfig) => m.id === currentModelIdRef.current)
|
|
210
|
+
: undefined;
|
|
211
|
+
if (initialFound) {
|
|
212
|
+
currentModelSettingsRef.current = JSON.stringify({
|
|
213
|
+
requireConfirmation: initialFound.requireConfirmation,
|
|
214
|
+
allowedTools: initialFound.allowedTools,
|
|
215
|
+
disallowedTools: initialFound.disallowedTools,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const rebuildAgent = (found: ModelConfig, displayName: string) => {
|
|
220
|
+
if (!agentRef.current) return;
|
|
221
|
+
import('../../agent/Agent.js').then(({ Agent }) => {
|
|
222
|
+
const apiKey = found.apiKey || process.env.OPENAI_API_KEY || '';
|
|
223
|
+
if (!apiKey) return;
|
|
224
|
+
Agent.create({
|
|
225
|
+
apiKey,
|
|
226
|
+
baseURL: found.baseURL || (found as ModelConfig & { baseUrl?: string }).baseUrl,
|
|
227
|
+
model: displayName,
|
|
228
|
+
requireConfirmation: found.requireConfirmation,
|
|
229
|
+
}).then(agent => {
|
|
230
|
+
agentRef.current = agent;
|
|
231
|
+
}).catch(() => {});
|
|
232
|
+
}).catch(() => {});
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const unsubscribe = subscribe((state) => {
|
|
236
|
+
const newModelId = state.config.config?.currentModelId;
|
|
237
|
+
if (!newModelId) return;
|
|
238
|
+
const models = state.config.config?.models || [];
|
|
239
|
+
const found = models.find((m: ModelConfig) => m.id === newModelId);
|
|
240
|
+
if (!found) return;
|
|
241
|
+
|
|
242
|
+
const settingsSnapshot = JSON.stringify({
|
|
243
|
+
requireConfirmation: found.requireConfirmation,
|
|
244
|
+
allowedTools: found.allowedTools,
|
|
245
|
+
disallowedTools: found.disallowedTools,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const modelChanged = newModelId !== currentModelIdRef.current;
|
|
249
|
+
const settingsChanged = settingsSnapshot !== currentModelSettingsRef.current;
|
|
250
|
+
|
|
251
|
+
if (!modelChanged && !settingsChanged) return;
|
|
252
|
+
|
|
253
|
+
currentModelIdRef.current = newModelId;
|
|
254
|
+
currentModelSettingsRef.current = settingsSnapshot;
|
|
255
|
+
|
|
256
|
+
const displayName = found.model || found.id || newModelId;
|
|
257
|
+
setCurrentModel(displayName);
|
|
258
|
+
modelRef.current = displayName;
|
|
259
|
+
|
|
260
|
+
// Apply requireConfirmation synchronously on the live Agent instance so
|
|
261
|
+
// toggling /confirm takes effect immediately — the full rebuild below
|
|
262
|
+
// is async and would otherwise leave a window where agentRef.current
|
|
263
|
+
// still has the stale value if a message is sent before it resolves.
|
|
264
|
+
if (!modelChanged && agentRef.current) {
|
|
265
|
+
agentRef.current.setRequireConfirmation(found.requireConfirmation);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
rebuildAgent(found, displayName);
|
|
269
|
+
});
|
|
270
|
+
return unsubscribe;
|
|
271
|
+
}, []);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
agentRef,
|
|
275
|
+
contextManagerRef,
|
|
276
|
+
isInitializing,
|
|
277
|
+
initError,
|
|
278
|
+
currentModel,
|
|
279
|
+
setCurrentModel,
|
|
280
|
+
handleSetupComplete,
|
|
281
|
+
getAgent,
|
|
282
|
+
getContextManager,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useCommandHistory - 命令历史管理
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useState, useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
interface CommandHistoryResult {
|
|
8
|
+
/** 添加命令到历史 */
|
|
9
|
+
addToHistory: (command: string) => void;
|
|
10
|
+
/** 获取上一条命令 */
|
|
11
|
+
getPreviousCommand: () => string | null;
|
|
12
|
+
/** 获取下一条命令 */
|
|
13
|
+
getNextCommand: () => string | null;
|
|
14
|
+
/** 重置历史索引 */
|
|
15
|
+
resetIndex: () => void;
|
|
16
|
+
/** 历史记录 */
|
|
17
|
+
history: string[];
|
|
18
|
+
/** 当前索引 */
|
|
19
|
+
historyIndex: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
export const useCommandHistory = (maxHistory = 100): CommandHistoryResult => {
|
|
27
|
+
const [history, setHistory] = useState<string[]>([]);
|
|
28
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
29
|
+
|
|
30
|
+
const addToHistory = useCallback((command: string) => {
|
|
31
|
+
if (command.trim()) {
|
|
32
|
+
setHistory(prev => {
|
|
33
|
+
// 避免重复添加相同命
|
|
34
|
+
if (prev[prev.length - 1] === command) {
|
|
35
|
+
return prev;
|
|
36
|
+
}
|
|
37
|
+
// 限制历史记录数
|
|
38
|
+
const newHistory = [...prev, command];
|
|
39
|
+
if (newHistory.length > maxHistory) {
|
|
40
|
+
newHistory.shift();
|
|
41
|
+
}
|
|
42
|
+
return newHistory;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
setHistoryIndex(-1);
|
|
46
|
+
}, [maxHistory]);
|
|
47
|
+
|
|
48
|
+
const getPreviousCommand = useCallback(() => {
|
|
49
|
+
if (history.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (historyIndex < history.length - 1) {
|
|
54
|
+
const newIndex = historyIndex + 1;
|
|
55
|
+
setHistoryIndex(newIndex);
|
|
56
|
+
return history[history.length - 1 - newIndex];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 已经到达最早的命
|
|
60
|
+
return history[0];
|
|
61
|
+
}, [history, historyIndex]);
|
|
62
|
+
|
|
63
|
+
const getNextCommand = useCallback(() => {
|
|
64
|
+
if (historyIndex > 0) {
|
|
65
|
+
const newIndex = historyIndex - 1;
|
|
66
|
+
setHistoryIndex(newIndex);
|
|
67
|
+
return history[history.length - 1 - newIndex];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 返回到最新(清空输
|
|
71
|
+
setHistoryIndex(-1);
|
|
72
|
+
return '';
|
|
73
|
+
}, [history, historyIndex]);
|
|
74
|
+
|
|
75
|
+
const resetIndex = useCallback(() => {
|
|
76
|
+
setHistoryIndex(-1);
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
addToHistory,
|
|
81
|
+
getPreviousCommand,
|
|
82
|
+
getNextCommand,
|
|
83
|
+
resetIndex,
|
|
84
|
+
history,
|
|
85
|
+
historyIndex,
|
|
86
|
+
};
|
|
87
|
+
};
|