@mseep/obsidian-agent-client 0.10.6
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/.claude/hooks/gh-setup.sh +49 -0
- package/.claude/settings.json +15 -0
- package/.claude/skills/release-notes/SKILL.md +331 -0
- package/.editorconfig +10 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +59 -0
- package/.github/copilot-instructions.md +45 -0
- package/.github/pull_request_template.md +32 -0
- package/.github/workflows/ci.yaml +25 -0
- package/.github/workflows/docs.yml +58 -0
- package/.github/workflows/relay_to_openclaw.yml +59 -0
- package/.github/workflows/release.yaml +45 -0
- package/.prettierignore +10 -0
- package/.prettierrc +13 -0
- package/.vscode/extensions.json +7 -0
- package/.vscode/settings.json +37 -0
- package/.zed/settings.json +42 -0
- package/AGENTS.md +330 -0
- package/ARCHITECTURE.md +390 -0
- package/CONTRIBUTING.md +216 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.ja.md +121 -0
- package/README.md +125 -0
- package/docs/.vitepress/config.mts +124 -0
- package/docs/.vitepress/theme/custom.css +111 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/agent-setup/claude-code.md +84 -0
- package/docs/agent-setup/codex.md +76 -0
- package/docs/agent-setup/custom-agents.md +67 -0
- package/docs/agent-setup/gemini-cli.md +99 -0
- package/docs/agent-setup/index.md +34 -0
- package/docs/announcements/gemini-cli-deprecation.md +73 -0
- package/docs/getting-started/index.md +78 -0
- package/docs/getting-started/quick-start.md +38 -0
- package/docs/help/faq.md +181 -0
- package/docs/help/troubleshooting.md +221 -0
- package/docs/index.md +63 -0
- package/docs/public/apple-touch-icon.png +0 -0
- package/docs/public/demo.mp4 +0 -0
- package/docs/public/favicon-16x16.png +0 -0
- package/docs/public/favicon-32x32.png +0 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/public/images/editing.webp +0 -0
- package/docs/public/images/export.webp +0 -0
- package/docs/public/images/floating-chat-button.webp +0 -0
- package/docs/public/images/floating-chat-instance-menu.webp +0 -0
- package/docs/public/images/floating-chat-view.webp +0 -0
- package/docs/public/images/mode-selection.webp +0 -0
- package/docs/public/images/model-selection.webp +0 -0
- package/docs/public/images/multi-session.webp +0 -0
- package/docs/public/images/remove-image.webp +0 -0
- package/docs/public/images/ribbon-icon.webp +0 -0
- package/docs/public/images/selection-context.gif +0 -0
- package/docs/public/images/sending-images.webp +0 -0
- package/docs/public/images/sending-messages.webp +0 -0
- package/docs/public/images/session-history-button.webp +0 -0
- package/docs/public/images/slash-commands-1.webp +0 -0
- package/docs/public/images/slash-commands-2.webp +0 -0
- package/docs/public/images/switch-agent.webp +0 -0
- package/docs/public/images/switch-default-agent.webp +0 -0
- package/docs/public/images/temporary-disable.gif +0 -0
- package/docs/reference/acp-support.md +110 -0
- package/docs/usage/chat-export.md +80 -0
- package/docs/usage/commands.md +51 -0
- package/docs/usage/context-files.md +57 -0
- package/docs/usage/editing.md +69 -0
- package/docs/usage/floating-chat.md +84 -0
- package/docs/usage/index.md +97 -0
- package/docs/usage/mcp-tools.md +33 -0
- package/docs/usage/mentions.md +70 -0
- package/docs/usage/mode-selection.md +28 -0
- package/docs/usage/model-selection.md +32 -0
- package/docs/usage/multi-session.md +68 -0
- package/docs/usage/sending-images.md +64 -0
- package/docs/usage/session-history.md +91 -0
- package/docs/usage/slash-commands.md +44 -0
- package/esbuild.config.mjs +49 -0
- package/eslint.config.mjs +25 -0
- package/main.js +228 -0
- package/manifest.json +11 -0
- package/package.json +52 -0
- package/src/acp/acp-client.ts +921 -0
- package/src/acp/acp-handler.ts +252 -0
- package/src/acp/permission-handler.ts +282 -0
- package/src/acp/terminal-handler.ts +264 -0
- package/src/acp/type-converter.ts +272 -0
- package/src/hooks/useAgent.ts +250 -0
- package/src/hooks/useAgentMessages.ts +470 -0
- package/src/hooks/useAgentSession.ts +544 -0
- package/src/hooks/useChatActions.ts +400 -0
- package/src/hooks/useHistoryModal.ts +219 -0
- package/src/hooks/useSessionHistory.ts +863 -0
- package/src/hooks/useSettings.ts +19 -0
- package/src/hooks/useSuggestions.ts +342 -0
- package/src/main.ts +9 -0
- package/src/plugin.ts +1126 -0
- package/src/services/chat-exporter.ts +552 -0
- package/src/services/message-sender.ts +755 -0
- package/src/services/message-state.ts +375 -0
- package/src/services/session-helpers.ts +211 -0
- package/src/services/session-state.ts +130 -0
- package/src/services/session-storage.ts +267 -0
- package/src/services/settings-normalizer.ts +255 -0
- package/src/services/settings-service.ts +285 -0
- package/src/services/update-checker.ts +128 -0
- package/src/services/vault-service.ts +558 -0
- package/src/services/view-registry.ts +345 -0
- package/src/types/agent.ts +92 -0
- package/src/types/chat.ts +351 -0
- package/src/types/errors.ts +136 -0
- package/src/types/obsidian-internals.d.ts +14 -0
- package/src/types/session.ts +731 -0
- package/src/ui/ChangeDirectoryModal.ts +137 -0
- package/src/ui/ChatContext.ts +25 -0
- package/src/ui/ChatHeader.tsx +295 -0
- package/src/ui/ChatPanel.tsx +1162 -0
- package/src/ui/ChatView.tsx +348 -0
- package/src/ui/ErrorBanner.tsx +104 -0
- package/src/ui/FloatingButton.tsx +351 -0
- package/src/ui/FloatingChatView.tsx +531 -0
- package/src/ui/InputArea.tsx +1107 -0
- package/src/ui/InputToolbar.tsx +371 -0
- package/src/ui/MessageBubble.tsx +442 -0
- package/src/ui/MessageList.tsx +265 -0
- package/src/ui/PermissionBanner.tsx +61 -0
- package/src/ui/SessionHistoryModal.tsx +821 -0
- package/src/ui/SettingsTab.ts +1337 -0
- package/src/ui/SuggestionPopup.tsx +138 -0
- package/src/ui/TerminalBlock.tsx +107 -0
- package/src/ui/ToolCallBlock.tsx +456 -0
- package/src/ui/shared/AttachmentStrip.tsx +57 -0
- package/src/ui/shared/IconButton.tsx +55 -0
- package/src/ui/shared/MarkdownRenderer.tsx +103 -0
- package/src/ui/view-host.ts +56 -0
- package/src/utils/error-utils.ts +274 -0
- package/src/utils/logger.ts +44 -0
- package/src/utils/mention-parser.ts +129 -0
- package/src/utils/paths.ts +246 -0
- package/src/utils/platform.ts +425 -0
- package/styles.css +2322 -0
- package/tsconfig.json +18 -0
- package/version-bump.mjs +18 -0
- package/versions.json +3 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-hook for managing agent session lifecycle and configuration.
|
|
3
|
+
*
|
|
4
|
+
* Handles session creation, restart, close, config/mode/model management,
|
|
5
|
+
* and session-level update processing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
const { useState, useCallback, useRef } = React;
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ChatSession,
|
|
13
|
+
SessionModeState,
|
|
14
|
+
SessionModelState,
|
|
15
|
+
SessionUpdate,
|
|
16
|
+
SessionConfigOption,
|
|
17
|
+
} from "../types/session";
|
|
18
|
+
import type { AcpClient } from "../acp/acp-client";
|
|
19
|
+
import type { ISettingsAccess } from "../services/settings-service";
|
|
20
|
+
import type { ErrorInfo } from "../types/errors";
|
|
21
|
+
import { extractErrorMessage } from "../utils/error-utils";
|
|
22
|
+
import {
|
|
23
|
+
type AgentDisplayInfo,
|
|
24
|
+
getDefaultAgentId,
|
|
25
|
+
getAvailableAgentsFromSettings,
|
|
26
|
+
getCurrentAgent,
|
|
27
|
+
findAgentSettings,
|
|
28
|
+
buildAgentConfigWithApiKey,
|
|
29
|
+
createInitialSession,
|
|
30
|
+
} from "../services/session-helpers";
|
|
31
|
+
import {
|
|
32
|
+
applyLegacyValue,
|
|
33
|
+
tryRestoreConfigOption,
|
|
34
|
+
restoreLegacyConfig,
|
|
35
|
+
} from "../services/session-state";
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Types
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
export interface UseAgentSessionReturn {
|
|
42
|
+
session: ChatSession;
|
|
43
|
+
isReady: boolean;
|
|
44
|
+
|
|
45
|
+
// Session lifecycle
|
|
46
|
+
createSession: (
|
|
47
|
+
overrideAgentId?: string,
|
|
48
|
+
overrideCwd?: string,
|
|
49
|
+
) => Promise<void>;
|
|
50
|
+
restartSession: (
|
|
51
|
+
newAgentId?: string,
|
|
52
|
+
overrideCwd?: string,
|
|
53
|
+
) => Promise<void>;
|
|
54
|
+
closeSession: () => Promise<void>;
|
|
55
|
+
forceRestartAgent: () => Promise<void>;
|
|
56
|
+
cancelOperation: () => Promise<void>;
|
|
57
|
+
getAvailableAgents: () => AgentDisplayInfo[];
|
|
58
|
+
updateSessionFromLoad: (
|
|
59
|
+
sessionId: string,
|
|
60
|
+
modes?: SessionModeState,
|
|
61
|
+
models?: SessionModelState,
|
|
62
|
+
configOptions?: SessionConfigOption[],
|
|
63
|
+
) => Promise<void>;
|
|
64
|
+
|
|
65
|
+
// Config
|
|
66
|
+
setMode: (modeId: string) => Promise<void>;
|
|
67
|
+
setModel: (modelId: string) => Promise<void>;
|
|
68
|
+
setConfigOption: (configId: string, value: string) => Promise<void>;
|
|
69
|
+
|
|
70
|
+
/** Handle session-level updates (commands, mode, config, usage, error) */
|
|
71
|
+
handleSessionUpdate: (update: SessionUpdate) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Hook Implementation
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
export function useAgentSession(
|
|
79
|
+
agentClient: AcpClient,
|
|
80
|
+
settingsAccess: ISettingsAccess,
|
|
81
|
+
workingDirectory: string,
|
|
82
|
+
setErrorInfo: (error: ErrorInfo | null) => void,
|
|
83
|
+
initialAgentId?: string,
|
|
84
|
+
): UseAgentSessionReturn {
|
|
85
|
+
// ============================================================
|
|
86
|
+
// Session State
|
|
87
|
+
// ============================================================
|
|
88
|
+
|
|
89
|
+
const initialSettings = settingsAccess.getSnapshot();
|
|
90
|
+
const effectiveInitialAgentId =
|
|
91
|
+
initialAgentId || getDefaultAgentId(initialSettings);
|
|
92
|
+
const initialAgent = getCurrentAgent(
|
|
93
|
+
initialSettings,
|
|
94
|
+
effectiveInitialAgentId,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const [session, setSession] = useState<ChatSession>(() =>
|
|
98
|
+
createInitialSession(
|
|
99
|
+
effectiveInitialAgentId,
|
|
100
|
+
initialAgent.displayName,
|
|
101
|
+
workingDirectory,
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const isReady = session.state === "ready";
|
|
106
|
+
|
|
107
|
+
// Ref for accessing latest session in callbacks without deps
|
|
108
|
+
const sessionRef = useRef(session);
|
|
109
|
+
sessionRef.current = session;
|
|
110
|
+
|
|
111
|
+
// ============================================================
|
|
112
|
+
// Session Update Handler (session-level only)
|
|
113
|
+
// ============================================================
|
|
114
|
+
|
|
115
|
+
const handleSessionUpdate = useCallback(
|
|
116
|
+
(update: SessionUpdate) => {
|
|
117
|
+
switch (update.type) {
|
|
118
|
+
case "available_commands_update":
|
|
119
|
+
setSession((prev) => ({
|
|
120
|
+
...prev,
|
|
121
|
+
availableCommands: update.commands,
|
|
122
|
+
}));
|
|
123
|
+
break;
|
|
124
|
+
case "current_mode_update":
|
|
125
|
+
setSession((prev) => {
|
|
126
|
+
if (!prev.modes) return prev;
|
|
127
|
+
return {
|
|
128
|
+
...prev,
|
|
129
|
+
modes: {
|
|
130
|
+
...prev.modes,
|
|
131
|
+
currentModeId: update.currentModeId,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
break;
|
|
136
|
+
case "config_option_update":
|
|
137
|
+
setSession((prev) => ({
|
|
138
|
+
...prev,
|
|
139
|
+
configOptions: update.configOptions,
|
|
140
|
+
}));
|
|
141
|
+
break;
|
|
142
|
+
case "usage_update":
|
|
143
|
+
setSession((prev) => ({
|
|
144
|
+
...prev,
|
|
145
|
+
usage: {
|
|
146
|
+
used: update.used,
|
|
147
|
+
size: update.size,
|
|
148
|
+
cost: update.cost ?? undefined,
|
|
149
|
+
},
|
|
150
|
+
}));
|
|
151
|
+
break;
|
|
152
|
+
case "process_error":
|
|
153
|
+
setSession((prev) => ({ ...prev, state: "error" }));
|
|
154
|
+
setErrorInfo({
|
|
155
|
+
title: update.error.title || "Agent Error",
|
|
156
|
+
message: update.error.message || "An error occurred",
|
|
157
|
+
suggestion: update.error.suggestion,
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
[setErrorInfo],
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// ============================================================
|
|
166
|
+
// Session Lifecycle
|
|
167
|
+
// ============================================================
|
|
168
|
+
|
|
169
|
+
const createSession = useCallback(
|
|
170
|
+
async (overrideAgentId?: string, overrideCwd?: string) => {
|
|
171
|
+
const effectiveCwd = overrideCwd || workingDirectory;
|
|
172
|
+
const settings = settingsAccess.getSnapshot();
|
|
173
|
+
const agentId = overrideAgentId || getDefaultAgentId(settings);
|
|
174
|
+
const currentAgent = getCurrentAgent(settings, agentId);
|
|
175
|
+
|
|
176
|
+
setSession((prev) => ({
|
|
177
|
+
...prev,
|
|
178
|
+
sessionId: null,
|
|
179
|
+
state: "initializing",
|
|
180
|
+
agentId: agentId,
|
|
181
|
+
agentDisplayName: currentAgent.displayName,
|
|
182
|
+
authMethods: [],
|
|
183
|
+
availableCommands: undefined,
|
|
184
|
+
modes: undefined,
|
|
185
|
+
models: undefined,
|
|
186
|
+
configOptions: undefined,
|
|
187
|
+
usage: undefined,
|
|
188
|
+
promptCapabilities: prev.promptCapabilities,
|
|
189
|
+
agentCapabilities: prev.agentCapabilities,
|
|
190
|
+
agentInfo: prev.agentInfo,
|
|
191
|
+
createdAt: new Date(),
|
|
192
|
+
lastActivityAt: new Date(),
|
|
193
|
+
}));
|
|
194
|
+
setErrorInfo(null);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const agentSettings = findAgentSettings(settings, agentId);
|
|
198
|
+
|
|
199
|
+
if (!agentSettings) {
|
|
200
|
+
setSession((prev) => ({ ...prev, state: "error" }));
|
|
201
|
+
setErrorInfo({
|
|
202
|
+
title: "Agent Not Found",
|
|
203
|
+
message: `Agent with ID "${agentId}" not found in settings`,
|
|
204
|
+
suggestion:
|
|
205
|
+
"Please check your agent configuration in settings.",
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const agentConfig = buildAgentConfigWithApiKey(
|
|
211
|
+
settings,
|
|
212
|
+
agentSettings,
|
|
213
|
+
agentId,
|
|
214
|
+
effectiveCwd,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const initResult =
|
|
218
|
+
!agentClient.isInitialized() ||
|
|
219
|
+
agentClient.getCurrentAgentId() !== agentId
|
|
220
|
+
? await agentClient.initialize(agentConfig)
|
|
221
|
+
: null;
|
|
222
|
+
|
|
223
|
+
const sessionResult =
|
|
224
|
+
await agentClient.newSession(effectiveCwd);
|
|
225
|
+
|
|
226
|
+
setSession((prev) => ({
|
|
227
|
+
...prev,
|
|
228
|
+
sessionId: sessionResult.sessionId,
|
|
229
|
+
state: "ready",
|
|
230
|
+
authMethods: initResult?.authMethods ?? [],
|
|
231
|
+
modes: sessionResult.modes,
|
|
232
|
+
models: sessionResult.models,
|
|
233
|
+
configOptions: sessionResult.configOptions,
|
|
234
|
+
promptCapabilities: initResult
|
|
235
|
+
? initResult.promptCapabilities
|
|
236
|
+
: prev.promptCapabilities,
|
|
237
|
+
agentCapabilities: initResult
|
|
238
|
+
? initResult.agentCapabilities
|
|
239
|
+
: prev.agentCapabilities,
|
|
240
|
+
agentInfo: initResult
|
|
241
|
+
? initResult.agentInfo
|
|
242
|
+
: prev.agentInfo,
|
|
243
|
+
lastActivityAt: new Date(),
|
|
244
|
+
}));
|
|
245
|
+
|
|
246
|
+
// Restore last used config (model/mode)
|
|
247
|
+
if (sessionResult.configOptions && sessionResult.sessionId) {
|
|
248
|
+
let configOptions = sessionResult.configOptions;
|
|
249
|
+
configOptions = await tryRestoreConfigOption(
|
|
250
|
+
agentClient,
|
|
251
|
+
sessionResult.sessionId,
|
|
252
|
+
configOptions,
|
|
253
|
+
"model",
|
|
254
|
+
settings.lastUsedModels[agentId],
|
|
255
|
+
);
|
|
256
|
+
configOptions = await tryRestoreConfigOption(
|
|
257
|
+
agentClient,
|
|
258
|
+
sessionResult.sessionId,
|
|
259
|
+
configOptions,
|
|
260
|
+
"mode",
|
|
261
|
+
settings.lastUsedModes[agentId],
|
|
262
|
+
);
|
|
263
|
+
if (configOptions !== sessionResult.configOptions) {
|
|
264
|
+
setSession((prev) => ({
|
|
265
|
+
...prev,
|
|
266
|
+
configOptions,
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
} else if (sessionResult.sessionId) {
|
|
270
|
+
await restoreLegacyConfig(
|
|
271
|
+
agentClient,
|
|
272
|
+
sessionResult,
|
|
273
|
+
settings.lastUsedModels[agentId],
|
|
274
|
+
settings.lastUsedModes[agentId],
|
|
275
|
+
setSession,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
setSession((prev) => ({ ...prev, state: "error" }));
|
|
280
|
+
setErrorInfo({
|
|
281
|
+
title: "Session Creation Failed",
|
|
282
|
+
message: `Failed to create new session: ${extractErrorMessage(error)}`,
|
|
283
|
+
suggestion:
|
|
284
|
+
"Please check the agent configuration and try again.",
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
[agentClient, settingsAccess, workingDirectory, setErrorInfo],
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const restartSession = useCallback(
|
|
292
|
+
async (newAgentId?: string, overrideCwd?: string) => {
|
|
293
|
+
await createSession(newAgentId, overrideCwd);
|
|
294
|
+
},
|
|
295
|
+
[createSession],
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const closeSession = useCallback(async () => {
|
|
299
|
+
const s = sessionRef.current;
|
|
300
|
+
if (s.sessionId) {
|
|
301
|
+
try {
|
|
302
|
+
await agentClient.cancel(s.sessionId);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.warn("Failed to cancel session:", error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
await agentClient.disconnect();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.warn("Failed to disconnect:", error);
|
|
311
|
+
}
|
|
312
|
+
setSession((prev) => ({
|
|
313
|
+
...prev,
|
|
314
|
+
sessionId: null,
|
|
315
|
+
state: "disconnected",
|
|
316
|
+
}));
|
|
317
|
+
}, [agentClient]);
|
|
318
|
+
|
|
319
|
+
const forceRestartAgent = useCallback(async () => {
|
|
320
|
+
const currentAgentId = sessionRef.current.agentId;
|
|
321
|
+
await agentClient.disconnect();
|
|
322
|
+
await createSession(currentAgentId);
|
|
323
|
+
}, [agentClient, createSession]);
|
|
324
|
+
|
|
325
|
+
const cancelOperation = useCallback(async () => {
|
|
326
|
+
const s = sessionRef.current;
|
|
327
|
+
if (!s.sessionId) return;
|
|
328
|
+
try {
|
|
329
|
+
await agentClient.cancel(s.sessionId);
|
|
330
|
+
setSession((prev) => ({ ...prev, state: "ready" }));
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.warn("Failed to cancel operation:", error);
|
|
333
|
+
setSession((prev) => ({ ...prev, state: "ready" }));
|
|
334
|
+
}
|
|
335
|
+
}, [agentClient]);
|
|
336
|
+
|
|
337
|
+
const getAvailableAgents = useCallback(() => {
|
|
338
|
+
const settings = settingsAccess.getSnapshot();
|
|
339
|
+
return getAvailableAgentsFromSettings(settings);
|
|
340
|
+
}, [settingsAccess]);
|
|
341
|
+
|
|
342
|
+
const updateSessionFromLoad = useCallback(
|
|
343
|
+
async (
|
|
344
|
+
sessionId: string,
|
|
345
|
+
modes?: SessionModeState,
|
|
346
|
+
models?: SessionModelState,
|
|
347
|
+
configOptions?: SessionConfigOption[],
|
|
348
|
+
) => {
|
|
349
|
+
setSession((prev) => ({
|
|
350
|
+
...prev,
|
|
351
|
+
sessionId,
|
|
352
|
+
state: "ready",
|
|
353
|
+
modes: modes ?? prev.modes,
|
|
354
|
+
models: models ?? prev.models,
|
|
355
|
+
configOptions: configOptions ?? prev.configOptions,
|
|
356
|
+
lastActivityAt: new Date(),
|
|
357
|
+
}));
|
|
358
|
+
|
|
359
|
+
// Restore last used config (model/mode) — same logic as createSession
|
|
360
|
+
const s = sessionRef.current;
|
|
361
|
+
const settings = settingsAccess.getSnapshot();
|
|
362
|
+
const agentId = s.agentId;
|
|
363
|
+
|
|
364
|
+
if (configOptions && sessionId) {
|
|
365
|
+
let restored = configOptions;
|
|
366
|
+
restored = await tryRestoreConfigOption(
|
|
367
|
+
agentClient,
|
|
368
|
+
sessionId,
|
|
369
|
+
restored,
|
|
370
|
+
"model",
|
|
371
|
+
settings.lastUsedModels[agentId],
|
|
372
|
+
);
|
|
373
|
+
restored = await tryRestoreConfigOption(
|
|
374
|
+
agentClient,
|
|
375
|
+
sessionId,
|
|
376
|
+
restored,
|
|
377
|
+
"mode",
|
|
378
|
+
settings.lastUsedModes[agentId],
|
|
379
|
+
);
|
|
380
|
+
if (restored !== configOptions) {
|
|
381
|
+
setSession((prev) => ({
|
|
382
|
+
...prev,
|
|
383
|
+
configOptions: restored,
|
|
384
|
+
}));
|
|
385
|
+
}
|
|
386
|
+
} else if (sessionId && modes) {
|
|
387
|
+
await restoreLegacyConfig(
|
|
388
|
+
agentClient,
|
|
389
|
+
{ sessionId, modes, models, configOptions: undefined },
|
|
390
|
+
settings.lastUsedModels[agentId],
|
|
391
|
+
settings.lastUsedModes[agentId],
|
|
392
|
+
setSession,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
[agentClient, settingsAccess],
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// ============================================================
|
|
400
|
+
// Config (including legacy)
|
|
401
|
+
// ============================================================
|
|
402
|
+
|
|
403
|
+
const setLegacyConfigValue = useCallback(
|
|
404
|
+
async (kind: "mode" | "model", value: string) => {
|
|
405
|
+
const s = sessionRef.current;
|
|
406
|
+
if (!s.sessionId) {
|
|
407
|
+
console.warn(`Cannot set ${kind}: no active session`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const previousValue =
|
|
412
|
+
kind === "mode"
|
|
413
|
+
? s.modes?.currentModeId
|
|
414
|
+
: s.models?.currentModelId;
|
|
415
|
+
|
|
416
|
+
setSession((prev) => applyLegacyValue(prev, kind, value));
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
if (kind === "mode") {
|
|
420
|
+
await agentClient.setSessionMode(s.sessionId, value);
|
|
421
|
+
} else {
|
|
422
|
+
await agentClient.setSessionModel(s.sessionId, value);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (s.agentId) {
|
|
426
|
+
const persistKey =
|
|
427
|
+
kind === "mode" ? "lastUsedModes" : "lastUsedModels";
|
|
428
|
+
const currentSettings = settingsAccess.getSnapshot();
|
|
429
|
+
void settingsAccess.updateSettings({
|
|
430
|
+
[persistKey]: {
|
|
431
|
+
...currentSettings[persistKey],
|
|
432
|
+
[s.agentId]: value,
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error(`Failed to set ${kind}:`, error);
|
|
438
|
+
if (previousValue) {
|
|
439
|
+
setSession((prev) =>
|
|
440
|
+
applyLegacyValue(prev, kind, previousValue),
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
[agentClient, settingsAccess],
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const setMode = useCallback(
|
|
449
|
+
(modeId: string) => setLegacyConfigValue("mode", modeId),
|
|
450
|
+
[setLegacyConfigValue],
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
const setModel = useCallback(
|
|
454
|
+
(modelId: string) => setLegacyConfigValue("model", modelId),
|
|
455
|
+
[setLegacyConfigValue],
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
const setConfigOption = useCallback(
|
|
459
|
+
async (configId: string, value: string) => {
|
|
460
|
+
const s = sessionRef.current;
|
|
461
|
+
if (!s.sessionId) {
|
|
462
|
+
console.warn("Cannot set config option: no active session");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const previousConfigOptions = s.configOptions;
|
|
467
|
+
|
|
468
|
+
setSession((prev) => {
|
|
469
|
+
if (!prev.configOptions) return prev;
|
|
470
|
+
return {
|
|
471
|
+
...prev,
|
|
472
|
+
configOptions: prev.configOptions.map((opt) =>
|
|
473
|
+
opt.id === configId
|
|
474
|
+
? { ...opt, currentValue: value }
|
|
475
|
+
: opt,
|
|
476
|
+
),
|
|
477
|
+
};
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
const updatedOptions = await agentClient.setSessionConfigOption(
|
|
482
|
+
s.sessionId,
|
|
483
|
+
configId,
|
|
484
|
+
value,
|
|
485
|
+
);
|
|
486
|
+
setSession((prev) => ({
|
|
487
|
+
...prev,
|
|
488
|
+
configOptions: updatedOptions,
|
|
489
|
+
}));
|
|
490
|
+
|
|
491
|
+
const changedOption = updatedOptions.find(
|
|
492
|
+
(o) => o.id === configId,
|
|
493
|
+
);
|
|
494
|
+
if (changedOption?.category === "model" && s.agentId) {
|
|
495
|
+
const currentSettings = settingsAccess.getSnapshot();
|
|
496
|
+
void settingsAccess.updateSettings({
|
|
497
|
+
lastUsedModels: {
|
|
498
|
+
...currentSettings.lastUsedModels,
|
|
499
|
+
[s.agentId]: value,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
if (changedOption?.category === "mode" && s.agentId) {
|
|
504
|
+
const currentSettings = settingsAccess.getSnapshot();
|
|
505
|
+
void settingsAccess.updateSettings({
|
|
506
|
+
lastUsedModes: {
|
|
507
|
+
...currentSettings.lastUsedModes,
|
|
508
|
+
[s.agentId]: value,
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
} catch (error) {
|
|
513
|
+
console.error("Failed to set config option:", error);
|
|
514
|
+
if (previousConfigOptions) {
|
|
515
|
+
setSession((prev) => ({
|
|
516
|
+
...prev,
|
|
517
|
+
configOptions: previousConfigOptions,
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
[agentClient, settingsAccess],
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
// ============================================================
|
|
526
|
+
// Return
|
|
527
|
+
// ============================================================
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
session,
|
|
531
|
+
isReady,
|
|
532
|
+
createSession,
|
|
533
|
+
restartSession,
|
|
534
|
+
closeSession,
|
|
535
|
+
forceRestartAgent,
|
|
536
|
+
cancelOperation,
|
|
537
|
+
getAvailableAgents,
|
|
538
|
+
updateSessionFromLoad,
|
|
539
|
+
setMode,
|
|
540
|
+
setModel,
|
|
541
|
+
setConfigOption,
|
|
542
|
+
handleSessionUpdate,
|
|
543
|
+
};
|
|
544
|
+
}
|