@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.
Files changed (146) hide show
  1. package/.claude/hooks/gh-setup.sh +49 -0
  2. package/.claude/settings.json +15 -0
  3. package/.claude/skills/release-notes/SKILL.md +331 -0
  4. package/.editorconfig +10 -0
  5. package/.github/FUNDING.yml +2 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +59 -0
  9. package/.github/copilot-instructions.md +45 -0
  10. package/.github/pull_request_template.md +32 -0
  11. package/.github/workflows/ci.yaml +25 -0
  12. package/.github/workflows/docs.yml +58 -0
  13. package/.github/workflows/relay_to_openclaw.yml +59 -0
  14. package/.github/workflows/release.yaml +45 -0
  15. package/.prettierignore +10 -0
  16. package/.prettierrc +13 -0
  17. package/.vscode/extensions.json +7 -0
  18. package/.vscode/settings.json +37 -0
  19. package/.zed/settings.json +42 -0
  20. package/AGENTS.md +330 -0
  21. package/ARCHITECTURE.md +390 -0
  22. package/CONTRIBUTING.md +216 -0
  23. package/LICENSE +202 -0
  24. package/NOTICE +2 -0
  25. package/README.ja.md +121 -0
  26. package/README.md +125 -0
  27. package/docs/.vitepress/config.mts +124 -0
  28. package/docs/.vitepress/theme/custom.css +111 -0
  29. package/docs/.vitepress/theme/index.ts +4 -0
  30. package/docs/agent-setup/claude-code.md +84 -0
  31. package/docs/agent-setup/codex.md +76 -0
  32. package/docs/agent-setup/custom-agents.md +67 -0
  33. package/docs/agent-setup/gemini-cli.md +99 -0
  34. package/docs/agent-setup/index.md +34 -0
  35. package/docs/announcements/gemini-cli-deprecation.md +73 -0
  36. package/docs/getting-started/index.md +78 -0
  37. package/docs/getting-started/quick-start.md +38 -0
  38. package/docs/help/faq.md +181 -0
  39. package/docs/help/troubleshooting.md +221 -0
  40. package/docs/index.md +63 -0
  41. package/docs/public/apple-touch-icon.png +0 -0
  42. package/docs/public/demo.mp4 +0 -0
  43. package/docs/public/favicon-16x16.png +0 -0
  44. package/docs/public/favicon-32x32.png +0 -0
  45. package/docs/public/favicon.ico +0 -0
  46. package/docs/public/images/editing.webp +0 -0
  47. package/docs/public/images/export.webp +0 -0
  48. package/docs/public/images/floating-chat-button.webp +0 -0
  49. package/docs/public/images/floating-chat-instance-menu.webp +0 -0
  50. package/docs/public/images/floating-chat-view.webp +0 -0
  51. package/docs/public/images/mode-selection.webp +0 -0
  52. package/docs/public/images/model-selection.webp +0 -0
  53. package/docs/public/images/multi-session.webp +0 -0
  54. package/docs/public/images/remove-image.webp +0 -0
  55. package/docs/public/images/ribbon-icon.webp +0 -0
  56. package/docs/public/images/selection-context.gif +0 -0
  57. package/docs/public/images/sending-images.webp +0 -0
  58. package/docs/public/images/sending-messages.webp +0 -0
  59. package/docs/public/images/session-history-button.webp +0 -0
  60. package/docs/public/images/slash-commands-1.webp +0 -0
  61. package/docs/public/images/slash-commands-2.webp +0 -0
  62. package/docs/public/images/switch-agent.webp +0 -0
  63. package/docs/public/images/switch-default-agent.webp +0 -0
  64. package/docs/public/images/temporary-disable.gif +0 -0
  65. package/docs/reference/acp-support.md +110 -0
  66. package/docs/usage/chat-export.md +80 -0
  67. package/docs/usage/commands.md +51 -0
  68. package/docs/usage/context-files.md +57 -0
  69. package/docs/usage/editing.md +69 -0
  70. package/docs/usage/floating-chat.md +84 -0
  71. package/docs/usage/index.md +97 -0
  72. package/docs/usage/mcp-tools.md +33 -0
  73. package/docs/usage/mentions.md +70 -0
  74. package/docs/usage/mode-selection.md +28 -0
  75. package/docs/usage/model-selection.md +32 -0
  76. package/docs/usage/multi-session.md +68 -0
  77. package/docs/usage/sending-images.md +64 -0
  78. package/docs/usage/session-history.md +91 -0
  79. package/docs/usage/slash-commands.md +44 -0
  80. package/esbuild.config.mjs +49 -0
  81. package/eslint.config.mjs +25 -0
  82. package/main.js +228 -0
  83. package/manifest.json +11 -0
  84. package/package.json +52 -0
  85. package/src/acp/acp-client.ts +921 -0
  86. package/src/acp/acp-handler.ts +252 -0
  87. package/src/acp/permission-handler.ts +282 -0
  88. package/src/acp/terminal-handler.ts +264 -0
  89. package/src/acp/type-converter.ts +272 -0
  90. package/src/hooks/useAgent.ts +250 -0
  91. package/src/hooks/useAgentMessages.ts +470 -0
  92. package/src/hooks/useAgentSession.ts +544 -0
  93. package/src/hooks/useChatActions.ts +400 -0
  94. package/src/hooks/useHistoryModal.ts +219 -0
  95. package/src/hooks/useSessionHistory.ts +863 -0
  96. package/src/hooks/useSettings.ts +19 -0
  97. package/src/hooks/useSuggestions.ts +342 -0
  98. package/src/main.ts +9 -0
  99. package/src/plugin.ts +1126 -0
  100. package/src/services/chat-exporter.ts +552 -0
  101. package/src/services/message-sender.ts +755 -0
  102. package/src/services/message-state.ts +375 -0
  103. package/src/services/session-helpers.ts +211 -0
  104. package/src/services/session-state.ts +130 -0
  105. package/src/services/session-storage.ts +267 -0
  106. package/src/services/settings-normalizer.ts +255 -0
  107. package/src/services/settings-service.ts +285 -0
  108. package/src/services/update-checker.ts +128 -0
  109. package/src/services/vault-service.ts +558 -0
  110. package/src/services/view-registry.ts +345 -0
  111. package/src/types/agent.ts +92 -0
  112. package/src/types/chat.ts +351 -0
  113. package/src/types/errors.ts +136 -0
  114. package/src/types/obsidian-internals.d.ts +14 -0
  115. package/src/types/session.ts +731 -0
  116. package/src/ui/ChangeDirectoryModal.ts +137 -0
  117. package/src/ui/ChatContext.ts +25 -0
  118. package/src/ui/ChatHeader.tsx +295 -0
  119. package/src/ui/ChatPanel.tsx +1162 -0
  120. package/src/ui/ChatView.tsx +348 -0
  121. package/src/ui/ErrorBanner.tsx +104 -0
  122. package/src/ui/FloatingButton.tsx +351 -0
  123. package/src/ui/FloatingChatView.tsx +531 -0
  124. package/src/ui/InputArea.tsx +1107 -0
  125. package/src/ui/InputToolbar.tsx +371 -0
  126. package/src/ui/MessageBubble.tsx +442 -0
  127. package/src/ui/MessageList.tsx +265 -0
  128. package/src/ui/PermissionBanner.tsx +61 -0
  129. package/src/ui/SessionHistoryModal.tsx +821 -0
  130. package/src/ui/SettingsTab.ts +1337 -0
  131. package/src/ui/SuggestionPopup.tsx +138 -0
  132. package/src/ui/TerminalBlock.tsx +107 -0
  133. package/src/ui/ToolCallBlock.tsx +456 -0
  134. package/src/ui/shared/AttachmentStrip.tsx +57 -0
  135. package/src/ui/shared/IconButton.tsx +55 -0
  136. package/src/ui/shared/MarkdownRenderer.tsx +103 -0
  137. package/src/ui/view-host.ts +56 -0
  138. package/src/utils/error-utils.ts +274 -0
  139. package/src/utils/logger.ts +44 -0
  140. package/src/utils/mention-parser.ts +129 -0
  141. package/src/utils/paths.ts +246 -0
  142. package/src/utils/platform.ts +425 -0
  143. package/styles.css +2322 -0
  144. package/tsconfig.json +18 -0
  145. package/version-bump.mjs +18 -0
  146. 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
+ }