@yu_robotics/remote-cli 1.6.151 ā 1.6.154
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 +22 -5
- package/dist/client/MessageHandler.d.ts +14 -7
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +225 -96
- package/dist/client/MessageHandler.js.map +1 -1
- package/dist/client/WebSocketClient.d.ts.map +1 -1
- package/dist/client/WebSocketClient.js +1 -0
- package/dist/client/WebSocketClient.js.map +1 -1
- package/dist/delegation/BoundedFileChecks.d.ts +3 -0
- package/dist/delegation/BoundedFileChecks.d.ts.map +1 -0
- package/dist/delegation/BoundedFileChecks.js +13 -0
- package/dist/delegation/BoundedFileChecks.js.map +1 -0
- package/dist/delegation/DelegatedWorkspaceManager.d.ts.map +1 -1
- package/dist/delegation/DelegatedWorkspaceManager.js +14 -7
- package/dist/delegation/DelegatedWorkspaceManager.js.map +1 -1
- package/dist/delegation/GitCheckpoint.d.ts.map +1 -1
- package/dist/delegation/GitCheckpoint.js +49 -32
- package/dist/delegation/GitCheckpoint.js.map +1 -1
- package/dist/executor/AcpExecutor.d.ts +6 -1
- package/dist/executor/AcpExecutor.d.ts.map +1 -1
- package/dist/executor/AcpExecutor.js +96 -7
- package/dist/executor/AcpExecutor.js.map +1 -1
- package/dist/executor/AgyExecutor.d.ts +6 -1
- package/dist/executor/AgyExecutor.d.ts.map +1 -1
- package/dist/executor/AgyExecutor.js +43 -1
- package/dist/executor/AgyExecutor.js.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.d.ts +33 -6
- package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.js +308 -38
- package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
- package/dist/executor/CodexAppServerExecutor.d.ts +3 -1
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +65 -11
- package/dist/executor/CodexAppServerExecutor.js.map +1 -1
- package/dist/executor/CodexCommandActions.d.ts +2 -2
- package/dist/executor/CodexCommandActions.d.ts.map +1 -1
- package/dist/executor/CodexCommandActions.js +26 -5
- package/dist/executor/CodexCommandActions.js.map +1 -1
- package/dist/executor/DshExecutor.js +2 -2
- package/dist/executor/DshExecutor.js.map +1 -1
- package/dist/executor/IExecutor.d.ts +20 -0
- package/dist/executor/IExecutor.d.ts.map +1 -1
- package/dist/executor/PiExecutor.d.ts +2 -1
- package/dist/executor/PiExecutor.d.ts.map +1 -1
- package/dist/executor/PiExecutor.js +70 -53
- package/dist/executor/PiExecutor.js.map +1 -1
- package/dist/executor/acp/AcpClient.d.ts.map +1 -1
- package/dist/executor/acp/AcpClient.js +23 -3
- package/dist/executor/acp/AcpClient.js.map +1 -1
- package/dist/executor/acp/AcpTypes.d.ts +8 -1
- package/dist/executor/acp/AcpTypes.d.ts.map +1 -1
- package/dist/executor/agy/AgyModels.d.ts +32 -0
- package/dist/executor/agy/AgyModels.d.ts.map +1 -0
- package/dist/executor/agy/AgyModels.js +112 -0
- package/dist/executor/agy/AgyModels.js.map +1 -0
- package/dist/executor/claude/ClaudeSettings.d.ts +28 -0
- package/dist/executor/claude/ClaudeSettings.d.ts.map +1 -0
- package/dist/executor/claude/ClaudeSettings.js +121 -0
- package/dist/executor/claude/ClaudeSettings.js.map +1 -0
- package/dist/executor/index.d.ts +1 -1
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +3 -3
- package/dist/executor/index.js.map +1 -1
- package/dist/maintenance/release-notes.json +1 -1
- package/dist/settings/SettingsAdmission.d.ts +12 -0
- package/dist/settings/SettingsAdmission.d.ts.map +1 -0
- package/dist/settings/SettingsAdmission.js +55 -0
- package/dist/settings/SettingsAdmission.js.map +1 -0
- package/dist/settings/SettingsCatalog.d.ts +23 -0
- package/dist/settings/SettingsCatalog.d.ts.map +1 -0
- package/dist/settings/SettingsCatalog.js +137 -0
- package/dist/settings/SettingsCatalog.js.map +1 -0
- package/dist/settings/SettingsService.d.ts +48 -0
- package/dist/settings/SettingsService.d.ts.map +1 -0
- package/dist/settings/SettingsService.js +218 -0
- package/dist/settings/SettingsService.js.map +1 -0
- package/dist/thread/ThreadExecutorPool.js +1 -1
- package/dist/thread/ThreadExecutorPool.js.map +1 -1
- package/dist/thread/types.d.ts +1 -1
- package/dist/thread/types.d.ts.map +1 -1
- package/dist/types/Settings.d.ts +64 -0
- package/dist/types/Settings.d.ts.map +1 -0
- package/dist/types/Settings.js +3 -0
- package/dist/types/Settings.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Router 1.6.117 and newer explicitly label standalone background-task notificatio
|
|
|
20
20
|
|
|
21
21
|
## Public Activity
|
|
22
22
|
|
|
23
|
+
From CLI 1.6.153, Codex shell calls no longer lose their label when native command actions are absent or unknown. Native reads, searches, and directory listings remain preferred. Other calls use fixed operation labels such as `Run npm command` or `Run Git command` for simple allowlisted programs, and `Run shell command` for compound or unrecognized commands. These are deterministic operation labels, not model-authored purpose summaries: installed Codex 0.160.0 command items have no per-call description field. Arguments, full paths, output, and execution-wide reasoning are never copied into these fallback labels. Late native action metadata can enrich a pending row before its first result; repeated results retain one row. Existing description fields, heading limits, and card-node counts are unchanged; upgrade the CLI, with no new Router capability required.
|
|
24
|
+
|
|
23
25
|
Router 1.6.145 combines each foreground tool call and its matching result into one collapsed row, retaining input, output, and diff previews. Independent calls, workers, and approvals remain separate. This uses the existing CLI protocol; the client does not need a new capability. See [Code Change Previews](../../README.md#code-change-previews).
|
|
24
26
|
|
|
25
27
|
Router 1.6.146 gives Bash and other foreground tools one compact heading with a gray success dot, pending hourglass, or gray failure cross, plus the tool name and an optional public description or file basename. Full tool IDs and explicit status labels remain inside the expanded details. Raw commands are not fallback headings; the existing CLI protocol is unchanged.
|
|
@@ -171,10 +173,10 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
|
|
|
171
173
|
| `/new` | Alias for `/clear`; start a fresh conversation in this thread |
|
|
172
174
|
| `/compact` | Compress history to save tokens |
|
|
173
175
|
| `/cd <dir>` | Change directory; a different directory starts fresh conversations for this thread |
|
|
174
|
-
| `/model [name]` |
|
|
175
|
-
| `/effort [auto|level]` |
|
|
176
|
+
| `/model [name]` | Open native model settings, or set the active backend model with a text argument |
|
|
177
|
+
| `/effort [auto|level]` | Open native effort settings, or set/clear the active backend effort with a text argument |
|
|
176
178
|
| `/sandbox [on/off/read-only/default]` | Configure the current Codex or Claude Code thread sandbox, directory grants, and networking |
|
|
177
|
-
| `/backend` |
|
|
179
|
+
| `/backend` | Open backend settings; Current thread is the default and Confirm applies the selection |
|
|
178
180
|
| `/delegation [on|off]` | Inspect or enable delegation between installed agent backends in this thread |
|
|
179
181
|
| `/backend <index>` | Switch all threads and clear per-thread backend overrides |
|
|
180
182
|
| `/backend <index> @` | Switch only the current thread |
|
|
@@ -183,6 +185,10 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
|
|
|
183
185
|
| `/unbind` | Unbind all devices |
|
|
184
186
|
| `/device` | List and switch between bound devices |
|
|
185
187
|
|
|
188
|
+
CLI and Router 1.6.152 add standalone settings cards. `/backend` lists all eight stable backend choices, disables missing installations and applies only after Confirm; Current thread is the default and All threads on this device is opt-in. `/model` and `/effort` submit native options immediately, and show applied state only after a saved-setting acknowledgement. Their target-backend selector configures future worker launches without switching the coordinator. Busy work, expired cards and changed settings require refresh; older peers and parameterized text commands retain their existing behavior. See [Model and Reasoning Effort](../../README.md#model-and-reasoning-effort).
|
|
189
|
+
|
|
190
|
+
From 1.6.154, numbered sections separate backend selection, scope and Confirm, or target backend, model/effort choices and secondary actions. Selected drafts and known preferences carry explicit checks; changing the target still never switches the coordinator. AGY model catalogs accept tab-delimited and formatted multi-space native rows, with exact IDs and explicit failures for empty, unusable or oversized responses. No inference or additional protocol capability is needed.
|
|
191
|
+
|
|
186
192
|
### Threads & Machines
|
|
187
193
|
|
|
188
194
|
From CLI 1.6.121, new and existing threads without a saved preference enable
|
|
@@ -294,8 +300,19 @@ fail admission; reconcile those rules before delegating.
|
|
|
294
300
|
From CLI 1.6.151, checkpoint index queries are combined and identical trees are
|
|
295
301
|
audited once within a capture. Both working-file passes and source HEAD/index
|
|
296
302
|
checks remain; no state is cached across tasks and Git deadlines are unchanged.
|
|
297
|
-
CLI
|
|
298
|
-
|
|
303
|
+
From CLI 1.6.153, real Git fixture suites use at most two isolated processes
|
|
304
|
+
(one on hosts with fewer than four available CPUs), alongside one to four
|
|
305
|
+
lightweight test threads. Pristine test repositories are copied into independently
|
|
306
|
+
owned directories instead of rebuilt for every case. Publish still runs the full
|
|
307
|
+
suite; ordinary test deadlines and production Git safety checks are unchanged.
|
|
308
|
+
From 1.6.154, filesystem checks run in batches of at most eight and drain before
|
|
309
|
+
failure; checkout readers still hash every byte with independent buffers up to
|
|
310
|
+
1 MiB each. Test-only clocks remove mocked startup/stop and transfer-deadline
|
|
311
|
+
waits after readiness gates; real Git streams, partial packs, cleanup assertions,
|
|
312
|
+
129 MiB files and 10,001-file stress coverage remain. ZIP fixtures use faster
|
|
313
|
+
CRC generation without changing archive bytes. Hosts with at least eight
|
|
314
|
+
available CPUs use at most four isolated Git processes plus at most four
|
|
315
|
+
lightweight threads; lower-core pool limits and ordinary deadlines are unchanged.
|
|
299
316
|
|
|
300
317
|
From CLI 1.6.140, managed Git preparation, repository-lock waits, and artifact
|
|
301
318
|
collection are awaited separately from native startup/stop deadlines. Slots and
|
|
@@ -42,6 +42,10 @@ export declare class MessageHandler {
|
|
|
42
42
|
private delegationProgressTextSupported;
|
|
43
43
|
private activityProgressSupported;
|
|
44
44
|
private workerContextResetSupported;
|
|
45
|
+
private settingsCardsSupported;
|
|
46
|
+
private readonly settingsAdmission;
|
|
47
|
+
private readonly settingsCatalogReader;
|
|
48
|
+
private readonly settingsService;
|
|
45
49
|
private readonly pendingApprovalCards;
|
|
46
50
|
private readonly delegation;
|
|
47
51
|
private readonly delegationBridges;
|
|
@@ -58,6 +62,7 @@ export declare class MessageHandler {
|
|
|
58
62
|
* Handle command message ā route to the correct thread executor.
|
|
59
63
|
*/
|
|
60
64
|
private handleCommandMessage;
|
|
65
|
+
private handleAdmittedCommandMessage;
|
|
61
66
|
/**
|
|
62
67
|
* Handle /abort command for a specific thread executor
|
|
63
68
|
*/
|
|
@@ -85,8 +90,8 @@ export declare class MessageHandler {
|
|
|
85
90
|
private hasThreadQueueState;
|
|
86
91
|
/**
|
|
87
92
|
* Bare /model: show the thread's current model on the active backend plus
|
|
88
|
-
* the available models
|
|
89
|
-
* - claude:
|
|
93
|
+
* the available models, using each adapter's structured catalog:
|
|
94
|
+
* - claude: native control initialization exposes model aliases and capabilities
|
|
90
95
|
* - agy: `agy models` prints "slug<TAB>Display Name" lines
|
|
91
96
|
* - codex: app-server `model/list` returns the authenticated catalog
|
|
92
97
|
* - opencode/kimi ACP: session config options expose enabled models
|
|
@@ -94,11 +99,6 @@ export declare class MessageHandler {
|
|
|
94
99
|
* - pi: RPC `get_available_models` returns the configured Pi catalog
|
|
95
100
|
*/
|
|
96
101
|
private handleModelList;
|
|
97
|
-
/**
|
|
98
|
-
* Run a short-lived CLI listing command with a 10s timeout.
|
|
99
|
-
* Returns null on failure/timeout ā callers degrade gracefully.
|
|
100
|
-
*/
|
|
101
|
-
private runListingCommand;
|
|
102
102
|
/**
|
|
103
103
|
* Handle /thread subcommands
|
|
104
104
|
*/
|
|
@@ -182,6 +182,13 @@ export declare class MessageHandler {
|
|
|
182
182
|
private sendImage;
|
|
183
183
|
private sendStructuredContent;
|
|
184
184
|
private sendResponse;
|
|
185
|
+
private executorConfiguration;
|
|
186
|
+
private settingsState;
|
|
187
|
+
private settingsBusy;
|
|
188
|
+
private settingsFallback;
|
|
189
|
+
/** Shared native/persistent mutation for cards and parameterized text commands. */
|
|
190
|
+
private applyPreference;
|
|
191
|
+
private applyBackendSetting;
|
|
185
192
|
private checkCommand;
|
|
186
193
|
private detectBackends;
|
|
187
194
|
private handleBackendCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMpD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAgK,MAAM,UAAU,CAAC;AACzM,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,8 +32,10 @@ const BackendCommand_1 = require("../utils/BackendCommand");
|
|
|
32
32
|
const ClaudeStderrFilter_1 = require("../executor/claude/ClaudeStderrFilter");
|
|
33
33
|
const ExecutionMetadata_1 = require("../executor/ExecutionMetadata");
|
|
34
34
|
const ActivityProgress_1 = require("../utils/ActivityProgress");
|
|
35
|
-
const
|
|
36
|
-
const
|
|
35
|
+
const SettingsAdmission_1 = require("../settings/SettingsAdmission");
|
|
36
|
+
const SettingsCatalog_1 = require("../settings/SettingsCatalog");
|
|
37
|
+
const SettingsService_1 = require("../settings/SettingsService");
|
|
38
|
+
const EFFORT_BACKENDS = new Set(['claude', 'codex', 'agy', 'opencode', 'kimi', 'zcode', 'pi', 'dsh']);
|
|
37
39
|
const SLASH_SESSION_BACKENDS = new Set(['opencode', 'kimi', 'zcode', 'pi']);
|
|
38
40
|
const NATIVE_SKILLS_BACKENDS = new Set(['claude', 'agy', 'opencode', 'kimi', 'zcode', 'pi']);
|
|
39
41
|
function backendDisplayName(key) {
|
|
@@ -114,6 +116,10 @@ class MessageHandler {
|
|
|
114
116
|
delegationProgressTextSupported = false;
|
|
115
117
|
activityProgressSupported = false;
|
|
116
118
|
workerContextResetSupported = false;
|
|
119
|
+
settingsCardsSupported = false;
|
|
120
|
+
settingsAdmission = new SettingsAdmission_1.SettingsAdmission();
|
|
121
|
+
settingsCatalogReader;
|
|
122
|
+
settingsService;
|
|
117
123
|
pendingApprovalCards = new Map();
|
|
118
124
|
delegation;
|
|
119
125
|
delegationBridges = new Map();
|
|
@@ -126,6 +132,33 @@ class MessageHandler {
|
|
|
126
132
|
this.config = config;
|
|
127
133
|
this.wsClient.onClose?.(() => this.fileInbox?.disconnect());
|
|
128
134
|
this.delegation = new DelegationManager_1.DelegationManager(directoryGuard);
|
|
135
|
+
this.settingsCatalogReader = new SettingsCatalog_1.SettingsCatalogReader(directoryGuard);
|
|
136
|
+
this.settingsService = new SettingsService_1.SettingsService({
|
|
137
|
+
state: id => this.settingsState(id),
|
|
138
|
+
backends: async () => (await this.detectBackends()).map(entry => ({ ...entry, value: (0, config_1.backendKeyOf)(entry.id) })),
|
|
139
|
+
catalog: async (id, target, kind) => {
|
|
140
|
+
const thread = this.threadManager.getThread(id);
|
|
141
|
+
if (!thread)
|
|
142
|
+
throw new Error('This thread no longer exists.');
|
|
143
|
+
const config = this.executorConfiguration();
|
|
144
|
+
return this.settingsCatalogReader.read(config, thread, this.threadPool.getBackendKey(id), target, kind);
|
|
145
|
+
},
|
|
146
|
+
busy: (id, except, queue = true) => this.settingsBusy(id, except, queue),
|
|
147
|
+
threads: () => this.threadManager.listThreads().map(thread => thread.id),
|
|
148
|
+
exclusive: async (target, body) => {
|
|
149
|
+
try {
|
|
150
|
+
return await this.settingsAdmission.mutation(target, body);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
for (const id of target === '*' ? this.threadManager.listThreads().map(thread => thread.id) : [target]) {
|
|
154
|
+
void this.startNextQueuedCommand(id);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
backend: (id, target, scope, follow) => this.applyBackendSetting(id, target, scope, follow),
|
|
159
|
+
preference: (id, target, kind, value) => this.applyPreference(id, target, kind, value),
|
|
160
|
+
});
|
|
161
|
+
this.wsClient.onClose?.(() => this.settingsService.invalidate());
|
|
129
162
|
this.notificationAdapter = new hooks_1.FeishuNotificationAdapter(wsClient);
|
|
130
163
|
this.notificationAdapter.setThreadNameResolver((threadId) => this.threadManager.getThread(threadId)?.name);
|
|
131
164
|
this.notificationAdapter.register();
|
|
@@ -162,6 +195,12 @@ class MessageHandler {
|
|
|
162
195
|
case 'command':
|
|
163
196
|
await this.handleCommandMessage(message, this.fileInbox?.mark());
|
|
164
197
|
return;
|
|
198
|
+
case 'settings_action':
|
|
199
|
+
if (this.settingsCardsSupported) {
|
|
200
|
+
const result = await this.settingsService.action(message);
|
|
201
|
+
this.wsClient.send(result);
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
165
204
|
case 'file_pending':
|
|
166
205
|
if (this.fileInbox)
|
|
167
206
|
await this.fileInbox.accept(message);
|
|
@@ -202,6 +241,7 @@ class MessageHandler {
|
|
|
202
241
|
this.activityProgressSupported = data.capabilities?.activityProgress === true;
|
|
203
242
|
this.delegationProgressSupported = data.capabilities?.delegationProgress === true;
|
|
204
243
|
this.workerContextResetSupported = this.delegationProgressSupported && data.capabilities?.workerContextReset === true;
|
|
244
|
+
this.settingsCardsSupported = data.capabilities?.settingsCards === true;
|
|
205
245
|
this.delegationProgressTextSupported = this.delegationProgressSupported
|
|
206
246
|
&& data.capabilities?.delegationProgressText === true;
|
|
207
247
|
for (const pending of this.pendingApprovalCards.values()) {
|
|
@@ -252,6 +292,34 @@ class MessageHandler {
|
|
|
252
292
|
* Handle command message ā route to the correct thread executor.
|
|
253
293
|
*/
|
|
254
294
|
async handleCommandMessage(message, fileOrder) {
|
|
295
|
+
const threadId = message.threadId ?? this.threadManager.getDefaultThread().id;
|
|
296
|
+
const content = message.content?.trim() ?? '';
|
|
297
|
+
const body = () => this.handleAdmittedCommandMessage(message, fileOrder);
|
|
298
|
+
this.messageOpenIds.set(message.messageId, message.openId);
|
|
299
|
+
try {
|
|
300
|
+
// Abort and addressed human replies must remain available during native work.
|
|
301
|
+
if (content === '/abort' || /^(?:yes|no)(?:\s|$)/.test(content) || content.startsWith('/input '))
|
|
302
|
+
return await body();
|
|
303
|
+
if (/^\/(?:backend|model|effort)\s+\S/.test(content)) {
|
|
304
|
+
const global = /^\/backend\s/.test(content) && !/\s@$/.test(content);
|
|
305
|
+
await this.settingsAdmission.mutation(global ? '*' : threadId, async () => {
|
|
306
|
+
this.settingsService.invalidate(global ? undefined : threadId);
|
|
307
|
+
await body();
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
await this.settingsAdmission.command(threadId, message.messageId, async () => {
|
|
312
|
+
if (/^\/(?:clear|new|cd|sandbox)(?:\s|$)/.test(content) || /^\/thread\s+delete(?:\s|$)/.test(content))
|
|
313
|
+
this.settingsService.invalidate(threadId);
|
|
314
|
+
await body();
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
this.sendResponse(message.messageId, threadId, { success: false, error: error instanceof Error ? error.message : 'Command admission failed.' });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async handleAdmittedCommandMessage(message, fileOrder) {
|
|
255
323
|
const { messageId, content, attachments, workingDirectory, openId, isSlashCommand, threadId } = message;
|
|
256
324
|
this.currentOpenId = openId;
|
|
257
325
|
this.messageOpenIds.set(message.messageId, openId);
|
|
@@ -275,6 +343,17 @@ class MessageHandler {
|
|
|
275
343
|
});
|
|
276
344
|
return;
|
|
277
345
|
}
|
|
346
|
+
if (this.settingsCardsSupported && openId && /^\/(?:backend|model|effort)$/.test(content?.trim() ?? '')) {
|
|
347
|
+
const kind = content.trim().slice(1);
|
|
348
|
+
try {
|
|
349
|
+
const menu = await this.settingsService.open(kind, resolvedThreadId, openId, undefined, messageId);
|
|
350
|
+
this.sendResponse(messageId, resolvedThreadId, { success: true, output: this.settingsFallback(menu), settingsMenu: menu });
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false, error: 'The settings menu could not be opened. Retry or use the parameterized text command.' });
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
278
357
|
const executor = this.threadPool.getExecutor(resolvedThreadId);
|
|
279
358
|
this.sendStreamContext(messageId, resolvedThreadId, executor);
|
|
280
359
|
// Handle /abort for this specific thread (bypasses busy check)
|
|
@@ -810,11 +889,11 @@ Use /compact to reduce conversation context or /clear to start a fresh context.`
|
|
|
810
889
|
- /clear, /new - Start a fresh conversation in this thread
|
|
811
890
|
- /compact - Compress conversation history to reduce context size
|
|
812
891
|
- /cd <directory> - Change working directory for this thread
|
|
813
|
-
- /model [name] -
|
|
814
|
-
- /effort [auto|level] -
|
|
892
|
+
- /model [name] - Open model settings, or set the active backend model with a text argument
|
|
893
|
+
- /effort [auto|level] - Open effort settings, or set a native supported level with a text argument
|
|
815
894
|
- /sandbox [on|off|read-only|default|allow <directory>|remove <directory>|network on|off] - Configure this thread's sandbox (Codex, Claude Code)
|
|
816
895
|
- /delegation [on|off|reset [backend]] - Delegate tasks between installed agent backends; reset saved worker context when needed
|
|
817
|
-
- /backend -
|
|
896
|
+
- /backend - Open backend settings (Current thread by default; Confirm applies)
|
|
818
897
|
- /backend <index> - Switch all threads and clear per-thread backend overrides
|
|
819
898
|
- /backend <index> @ - Switch only the current thread
|
|
820
899
|
- /backend default @ - Clear the current thread override and follow the global backend
|
|
@@ -943,9 +1022,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
943
1022
|
if ('listModels' in executor && typeof executor.listModels === 'function') {
|
|
944
1023
|
try {
|
|
945
1024
|
const models = await executor.listModels();
|
|
946
|
-
const activeModel = models.find((entry) => entry.
|
|
947
|
-
?? models.find((entry) => entry.
|
|
948
|
-
?? models
|
|
1025
|
+
const activeModel = models.find((entry) => entry.isCurrent)
|
|
1026
|
+
?? models.find((entry) => entry.id === configuredModel)
|
|
1027
|
+
?? models.find((entry) => entry.isDefault);
|
|
949
1028
|
if (activeModel) {
|
|
950
1029
|
lines.push(`Model: ${activeModel.id}`);
|
|
951
1030
|
lines.push(`Model default: ${activeModel.defaultReasoningEffort ?? 'unavailable'}`);
|
|
@@ -969,18 +1048,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
969
1048
|
return true;
|
|
970
1049
|
}
|
|
971
1050
|
const effortArg = parts.slice(1).join(' ').toLowerCase();
|
|
972
|
-
const result = await
|
|
973
|
-
if (result.success) {
|
|
974
|
-
const current = this.threadManager.getThread(threadId);
|
|
975
|
-
const efforts = { ...current?.efforts };
|
|
976
|
-
if (effortArg === 'auto')
|
|
977
|
-
delete efforts[key];
|
|
978
|
-
else
|
|
979
|
-
efforts[key] = effortArg;
|
|
980
|
-
await this.threadManager.updateThread(threadId, {
|
|
981
|
-
efforts: Object.keys(efforts).length > 0 ? efforts : undefined,
|
|
982
|
-
});
|
|
983
|
-
}
|
|
1051
|
+
const result = await this.applyPreference(threadId, key, 'effort', effortArg === 'auto' ? undefined : effortArg);
|
|
984
1052
|
this.sendResponse(messageId, threadId, result.success
|
|
985
1053
|
? { success: true, output: result.output || `Reasoning effort set to ${effortArg}.` }
|
|
986
1054
|
: { success: false, error: result.error || 'Failed to set reasoning effort' });
|
|
@@ -1000,20 +1068,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1000
1068
|
});
|
|
1001
1069
|
return true;
|
|
1002
1070
|
}
|
|
1003
|
-
const result = await
|
|
1004
|
-
this.sendStreamChunk(messageId, threadId, chunk);
|
|
1005
|
-
});
|
|
1006
|
-
if (result.success) {
|
|
1007
|
-
// Persist per-backend: model names are backend-specific (Claude's
|
|
1008
|
-
// "opus" is rejected by agy), so selections live under
|
|
1009
|
-
// thread.models[backendKey]. The legacy `model` field is kept in
|
|
1010
|
-
// sync for the Claude backend only.
|
|
1011
|
-
const executorConfig = this.config.get('executor') ?? { type: 'auto' };
|
|
1012
|
-
const key = this.threadPool.getBackendKey(threadId);
|
|
1013
|
-
const current = this.threadManager.getThread(threadId);
|
|
1014
|
-
const models = { ...current?.models, [key]: modelArg };
|
|
1015
|
-
await this.threadManager.updateThread(threadId, key === 'claude' ? { models, model: modelArg } : { models });
|
|
1016
|
-
}
|
|
1071
|
+
const result = await this.applyPreference(threadId, this.threadPool.getBackendKey(threadId), 'model', modelArg, chunk => this.sendStreamChunk(messageId, threadId, chunk));
|
|
1017
1072
|
this.sendResponse(messageId, threadId, result.success
|
|
1018
1073
|
? { success: true, output: result.output || `ā
Model set to ${modelArg}` }
|
|
1019
1074
|
: { success: false, error: result.error || 'Failed to set model' });
|
|
@@ -1162,6 +1217,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1162
1217
|
this.sendResponse(messageId, threadId, { success: true, output: lines.join('\n') });
|
|
1163
1218
|
}
|
|
1164
1219
|
async startNextQueuedCommand(threadId) {
|
|
1220
|
+
if (this.settingsAdmission.isBlocked(threadId))
|
|
1221
|
+
return;
|
|
1165
1222
|
if (this.threadPool.isThreadBusy(threadId) || this.pausedQueues.has(threadId) || this.abortOperations.has(threadId))
|
|
1166
1223
|
return;
|
|
1167
1224
|
const queue = this.threadQueues.get(threadId);
|
|
@@ -1248,8 +1305,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1248
1305
|
}
|
|
1249
1306
|
/**
|
|
1250
1307
|
* Bare /model: show the thread's current model on the active backend plus
|
|
1251
|
-
* the available models
|
|
1252
|
-
* - claude:
|
|
1308
|
+
* the available models, using each adapter's structured catalog:
|
|
1309
|
+
* - claude: native control initialization exposes model aliases and capabilities
|
|
1253
1310
|
* - agy: `agy models` prints "slug<TAB>Display Name" lines
|
|
1254
1311
|
* - codex: app-server `model/list` returns the authenticated catalog
|
|
1255
1312
|
* - opencode/kimi ACP: session config options expose enabled models
|
|
@@ -1257,7 +1314,6 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1257
1314
|
* - pi: RPC `get_available_models` returns the configured Pi catalog
|
|
1258
1315
|
*/
|
|
1259
1316
|
async handleModelList(messageId, threadId) {
|
|
1260
|
-
const executorConfig = this.config.get('executor') ?? { type: 'auto' };
|
|
1261
1317
|
const key = this.threadPool.getBackendKey(threadId);
|
|
1262
1318
|
const thread = this.threadManager.getThread(threadId);
|
|
1263
1319
|
const current = thread?.models?.[key] ?? (key === 'claude' ? thread?.model : undefined);
|
|
@@ -1266,7 +1322,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1266
1322
|
`šÆ Backend: ${backendLabel}`,
|
|
1267
1323
|
`Current model: ${current ?? 'backend default'}`,
|
|
1268
1324
|
];
|
|
1269
|
-
|
|
1325
|
+
{
|
|
1270
1326
|
const executor = this.threadPool.getExecutor(threadId);
|
|
1271
1327
|
if ('listModels' in executor && typeof executor.listModels === 'function') {
|
|
1272
1328
|
try {
|
|
@@ -1290,57 +1346,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
1290
1346
|
lines.push('', `Model listing is unavailable in this ${backendLabel} transport.`);
|
|
1291
1347
|
}
|
|
1292
1348
|
}
|
|
1293
|
-
else {
|
|
1294
|
-
const bin = (0, BackendCommand_1.getBackendCommand)(key === 'agy' ? 'agy' : 'claude', executorConfig);
|
|
1295
|
-
const args = key === 'agy' ? ['models'] : ['/model', '--print'];
|
|
1296
|
-
const listing = await this.runListingCommand(bin, args);
|
|
1297
|
-
if (listing) {
|
|
1298
|
-
lines.push('', 'Available models:', listing.trim());
|
|
1299
|
-
}
|
|
1300
|
-
else {
|
|
1301
|
-
lines.push('', 'ā ļø Could not fetch the model list from the backend CLI.');
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
1349
|
lines.push('', 'Set with: /model <name>');
|
|
1305
1350
|
this.sendResponse(messageId, threadId, { success: true, output: lines.join('\n') });
|
|
1306
1351
|
}
|
|
1307
|
-
/**
|
|
1308
|
-
* Run a short-lived CLI listing command with a 10s timeout.
|
|
1309
|
-
* Returns null on failure/timeout ā callers degrade gracefully.
|
|
1310
|
-
*/
|
|
1311
|
-
runListingCommand(bin, args) {
|
|
1312
|
-
return new Promise((resolve) => {
|
|
1313
|
-
let settled = false;
|
|
1314
|
-
let timer;
|
|
1315
|
-
const finish = (value) => {
|
|
1316
|
-
if (settled)
|
|
1317
|
-
return;
|
|
1318
|
-
settled = true;
|
|
1319
|
-
if (timer)
|
|
1320
|
-
clearTimeout(timer);
|
|
1321
|
-
resolve(value);
|
|
1322
|
-
};
|
|
1323
|
-
let child;
|
|
1324
|
-
try {
|
|
1325
|
-
child = (0, child_process_1.spawn)(bin, args, {
|
|
1326
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1327
|
-
env: { ...process.env, CLAUDECODE: '' },
|
|
1328
|
-
});
|
|
1329
|
-
}
|
|
1330
|
-
catch {
|
|
1331
|
-
finish(null);
|
|
1332
|
-
return;
|
|
1333
|
-
}
|
|
1334
|
-
timer = setTimeout(() => {
|
|
1335
|
-
child.kill();
|
|
1336
|
-
finish(null);
|
|
1337
|
-
}, 10000);
|
|
1338
|
-
const chunks = [];
|
|
1339
|
-
child.stdout?.on('data', (d) => chunks.push(d.toString()));
|
|
1340
|
-
child.on('exit', (code) => finish(code === 0 ? chunks.join('') : null));
|
|
1341
|
-
child.on('error', () => finish(null));
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
1352
|
/**
|
|
1345
1353
|
* Handle /thread subcommands
|
|
1346
1354
|
*/
|
|
@@ -2515,6 +2523,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2515
2523
|
threads: result.threads,
|
|
2516
2524
|
queueConfirmation: result.queueConfirmation,
|
|
2517
2525
|
...(result.executionMetadata ? { executionMetadata: result.executionMetadata } : {}),
|
|
2526
|
+
...(this.settingsCardsSupported && result.settingsMenu ? { settingsMenu: result.settingsMenu } : {}),
|
|
2518
2527
|
cwd,
|
|
2519
2528
|
timestamp: Date.now(),
|
|
2520
2529
|
});
|
|
@@ -2525,6 +2534,127 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2525
2534
|
}
|
|
2526
2535
|
}
|
|
2527
2536
|
// āā Backend switching āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
2537
|
+
executorConfiguration() {
|
|
2538
|
+
return this.config.get('executor') ?? { type: 'auto' };
|
|
2539
|
+
}
|
|
2540
|
+
settingsState(threadId) {
|
|
2541
|
+
const thread = this.threadManager.getThread(threadId);
|
|
2542
|
+
if (!thread)
|
|
2543
|
+
return undefined;
|
|
2544
|
+
const projection = (entry) => ({ id: entry.id, backend: entry.backend,
|
|
2545
|
+
cwd: entry.workingDirectory, generation: entry.delegationWorkspaceGeneration, model: entry.model,
|
|
2546
|
+
models: entry.models, efforts: entry.efforts });
|
|
2547
|
+
const all = this.threadManager.listThreads().map(projection).sort((a, b) => a.id.localeCompare(b.id));
|
|
2548
|
+
const configuredModels = { ...thread.models };
|
|
2549
|
+
if (configuredModels.claude === undefined && thread.model)
|
|
2550
|
+
configuredModels.claude = thread.model;
|
|
2551
|
+
return { threadId, threadName: thread.name, coordinatorBackend: this.threadPool.getBackendKey(threadId),
|
|
2552
|
+
followsGlobal: !thread.backend, configuredModels, configuredEfforts: { ...thread.efforts },
|
|
2553
|
+
revision: (0, crypto_1.createHash)('sha256').update(JSON.stringify({ thread: projection(thread), all, config: this.executorConfiguration() })).digest('hex') };
|
|
2554
|
+
}
|
|
2555
|
+
settingsBusy(threadId, exceptRequest, includeQueue = true) {
|
|
2556
|
+
return this.isDestroyed || this.automaticUpdateInProgress || this.threadPool.isThreadBusy(threadId)
|
|
2557
|
+
|| this.delegation.hasActiveTasks(threadId) || this.abortOperations.has(threadId)
|
|
2558
|
+
|| this.settingsAdmission.hasCommand(threadId, exceptRequest) || (includeQueue && this.hasThreadQueueState(threadId));
|
|
2559
|
+
}
|
|
2560
|
+
settingsFallback(menu) {
|
|
2561
|
+
const lines = [`${menu.kind === 'backend' ? 'Backend' : menu.kind === 'model' ? 'Model' : 'Reasoning effort'} settings`,
|
|
2562
|
+
`Thread: ${menu.threadName}`, `Target backend: ${menu.targetBackend}`,
|
|
2563
|
+
`Configured: ${menu.configuredValue ?? 'default'}`, `Effective: ${menu.effectiveValue ?? 'not reported'} (${menu.effectiveSource})`];
|
|
2564
|
+
if (menu.unavailableReason)
|
|
2565
|
+
lines.push(menu.unavailableReason);
|
|
2566
|
+
for (const choice of menu.choices)
|
|
2567
|
+
lines.push(`${choice.label}${choice.disabled ? ' (unavailable)' : ''}`);
|
|
2568
|
+
if (menu.kind === 'backend')
|
|
2569
|
+
lines.push('Card scope defaults to Current thread. Confirm to apply. Text commands: /backend <index> @ (current), /backend <index> (all).');
|
|
2570
|
+
else
|
|
2571
|
+
lines.push(`Use the card buttons, or /${menu.kind} <value> for the active backend.`);
|
|
2572
|
+
return lines.join('\n');
|
|
2573
|
+
}
|
|
2574
|
+
/** Shared native/persistent mutation for cards and parameterized text commands. */
|
|
2575
|
+
async applyPreference(threadId, backend, kind, value, onStream) {
|
|
2576
|
+
const thread = this.threadManager.getThread(threadId);
|
|
2577
|
+
if (!thread)
|
|
2578
|
+
return { success: false, error: 'This thread no longer exists.' };
|
|
2579
|
+
if (value !== undefined && (!value || value.length > 512 || /[\x00-\x1f\x7f]/.test(value)))
|
|
2580
|
+
return { success: false, error: 'Invalid settings value.' };
|
|
2581
|
+
const config = this.executorConfiguration();
|
|
2582
|
+
const coordinator = this.threadPool.getBackendKey(threadId);
|
|
2583
|
+
const apply = async (executor) => {
|
|
2584
|
+
if (kind === 'effort') {
|
|
2585
|
+
if (!executor.setEffort)
|
|
2586
|
+
return { success: false, error: 'This backend does not expose effort controls.' };
|
|
2587
|
+
return executor.setEffort(value ?? 'auto');
|
|
2588
|
+
}
|
|
2589
|
+
if (value === undefined) {
|
|
2590
|
+
const fallback = (0, SettingsCatalog_1.configuredBackendModel)(config, backend);
|
|
2591
|
+
if (fallback && executor.setModel)
|
|
2592
|
+
return executor.setModel(fallback, onStream);
|
|
2593
|
+
if (!executor.clearModel)
|
|
2594
|
+
return { success: false, error: 'This backend cannot safely clear its model override.' };
|
|
2595
|
+
await executor.clearModel();
|
|
2596
|
+
return { success: true, output: 'Model override cleared.' };
|
|
2597
|
+
}
|
|
2598
|
+
if (!executor.setModel)
|
|
2599
|
+
return { success: false, error: 'This backend does not expose model controls.' };
|
|
2600
|
+
return executor.setModel(value, onStream);
|
|
2601
|
+
};
|
|
2602
|
+
try {
|
|
2603
|
+
const result = backend === coordinator ? await apply(this.threadPool.getExecutor(threadId))
|
|
2604
|
+
: await this.settingsCatalogReader.withExecutor(config, thread, coordinator, backend, apply, value === undefined && kind === 'model');
|
|
2605
|
+
if (!result.success)
|
|
2606
|
+
return result;
|
|
2607
|
+
// Persistence uses the latest thread object so unrelated backend choices survive.
|
|
2608
|
+
const current = this.threadManager.getThread(threadId);
|
|
2609
|
+
if (!current)
|
|
2610
|
+
throw new Error('The thread was deleted during the settings operation.');
|
|
2611
|
+
if (kind === 'model') {
|
|
2612
|
+
const models = { ...current.models };
|
|
2613
|
+
if (value === undefined)
|
|
2614
|
+
delete models[backend];
|
|
2615
|
+
else
|
|
2616
|
+
models[backend] = value;
|
|
2617
|
+
await this.threadManager.updateThread(threadId, { models: Object.keys(models).length ? models : undefined,
|
|
2618
|
+
...(backend === 'claude' ? { model: value } : {}) });
|
|
2619
|
+
}
|
|
2620
|
+
else {
|
|
2621
|
+
const efforts = { ...current.efforts };
|
|
2622
|
+
if (value === undefined)
|
|
2623
|
+
delete efforts[backend];
|
|
2624
|
+
else
|
|
2625
|
+
efforts[backend] = value;
|
|
2626
|
+
await this.threadManager.updateThread(threadId, { efforts: Object.keys(efforts).length ? efforts : undefined });
|
|
2627
|
+
}
|
|
2628
|
+
return result;
|
|
2629
|
+
}
|
|
2630
|
+
finally {
|
|
2631
|
+
this.settingsService.invalidate(threadId);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
async applyBackendSetting(threadId, backend, scope, followGlobal = false) {
|
|
2635
|
+
try {
|
|
2636
|
+
if (followGlobal) {
|
|
2637
|
+
await this.threadPool.destroyThread(threadId, { deleteData: false });
|
|
2638
|
+
await this.threadManager.updateThread(threadId, { backend: undefined });
|
|
2639
|
+
return { success: true, output: 'This thread now follows the global backend.', clearedCount: this.clearThreadQueue(threadId) };
|
|
2640
|
+
}
|
|
2641
|
+
if (scope === 'thread') {
|
|
2642
|
+
await this.threadPool.switchThreadBackend(threadId, backend);
|
|
2643
|
+
return { success: true, output: `Thread backend set to ${backend}.`, clearedCount: this.clearThreadQueue(threadId) };
|
|
2644
|
+
}
|
|
2645
|
+
if (this.threadPool.getSummaries().some(thread => thread.status === 'running') || this.delegation.hasActiveTasks(threadId)) {
|
|
2646
|
+
return { success: false, error: 'Cannot switch all backends while a thread is running. Send /abort first.', clearedCount: 0 };
|
|
2647
|
+
}
|
|
2648
|
+
const config = { ...this.executorConfiguration(), type: backend === 'claude' ? 'auto' : backend };
|
|
2649
|
+
await this.config.set('executor', config);
|
|
2650
|
+
await this.threadPool.switchBackend(config);
|
|
2651
|
+
await this.threadManager.clearBackendOverrides();
|
|
2652
|
+
return { success: true, output: `All threads will use ${backend} for future commands.`, clearedCount: this.clearAllQueues() };
|
|
2653
|
+
}
|
|
2654
|
+
finally {
|
|
2655
|
+
this.settingsService.invalidate(scope === 'all' ? undefined : threadId);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2528
2658
|
checkCommand(cmd, args) {
|
|
2529
2659
|
return new Promise((resolve) => {
|
|
2530
2660
|
try {
|
|
@@ -2596,9 +2726,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2596
2726
|
});
|
|
2597
2727
|
return;
|
|
2598
2728
|
}
|
|
2599
|
-
await this.
|
|
2600
|
-
await this.threadManager.updateThread(threadId, { backend: undefined });
|
|
2601
|
-
const clearedCount = this.clearThreadQueue(threadId);
|
|
2729
|
+
const { clearedCount } = await this.applyBackendSetting(threadId, (0, config_1.backendKeyOf)(currentType), 'thread', true);
|
|
2602
2730
|
this.sendResponse(messageId, threadId, {
|
|
2603
2731
|
success: true,
|
|
2604
2732
|
output: `ā
Thread backend reset to the global backend (${(0, config_1.backendKeyOf)(currentType)}).${clearedCount ? `\nšļø Cleared ${clearedCount} queued message${clearedCount === 1 ? '' : 's'}.` : ''}`,
|
|
@@ -2621,11 +2749,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2621
2749
|
});
|
|
2622
2750
|
return;
|
|
2623
2751
|
}
|
|
2624
|
-
const newConfig = { ...currentConfig, type: target.id };
|
|
2625
2752
|
if (isThreadOverride) {
|
|
2626
2753
|
try {
|
|
2627
|
-
await this.
|
|
2628
|
-
const clearedCount = this.clearThreadQueue(threadId);
|
|
2754
|
+
const { clearedCount } = await this.applyBackendSetting(threadId, (0, config_1.backendKeyOf)(target.id), 'thread');
|
|
2629
2755
|
this.sendResponse(messageId, threadId, {
|
|
2630
2756
|
success: true,
|
|
2631
2757
|
output: `ā
This thread switched to: ${target.label}${clearedCount ? `\nšļø Cleared ${clearedCount} queued message${clearedCount === 1 ? '' : 's'}.` : ''}\n\nUse /backend ${installed.indexOf(target) + 1} to switch all threads, or /backend default @ to follow the global backend again.`,
|
|
@@ -2647,10 +2773,12 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2647
2773
|
});
|
|
2648
2774
|
return;
|
|
2649
2775
|
}
|
|
2650
|
-
await this.
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2776
|
+
const applied = await this.applyBackendSetting(threadId, (0, config_1.backendKeyOf)(target.id), 'all');
|
|
2777
|
+
if (!applied.success) {
|
|
2778
|
+
this.sendResponse(messageId, threadId, applied);
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
const { clearedCount } = applied;
|
|
2654
2782
|
this.sendResponse(messageId, threadId, {
|
|
2655
2783
|
success: true,
|
|
2656
2784
|
output: `ā
Backend switched to: ${target.label}${clearedCount ? `\nšļø Cleared ${clearedCount} queued message${clearedCount === 1 ? '' : 's'}.` : ''}\n\nAll threads will use the new backend for future commands. Conversations on the previous backend are preserved ā switch back to resume them.`,
|
|
@@ -2661,6 +2789,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2661
2789
|
* Destroy handler and all executors
|
|
2662
2790
|
*/
|
|
2663
2791
|
async destroy() {
|
|
2792
|
+
this.settingsService.destroy();
|
|
2664
2793
|
this.isDestroyed = true;
|
|
2665
2794
|
await this.fileInbox?.destroy();
|
|
2666
2795
|
this.notificationAdapter.unregister();
|