@yu_robotics/remote-cli 1.6.149 ā 1.6.152
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 +13 -3
- 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 +2 -1
- package/dist/client/WebSocketClient.js.map +1 -1
- package/dist/delegation/GitCheckpoint.d.ts.map +1 -1
- package/dist/delegation/GitCheckpoint.js +39 -22
- 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 +9 -1
- package/dist/executor/AgyExecutor.d.ts.map +1 -1
- package/dist/executor/AgyExecutor.js +66 -0
- 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 +2 -1
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +39 -4
- package/dist/executor/CodexAppServerExecutor.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/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/BankedResetReminders.d.ts +24 -0
- package/dist/maintenance/BankedResetReminders.d.ts.map +1 -0
- package/dist/maintenance/BankedResetReminders.js +79 -0
- package/dist/maintenance/BankedResetReminders.js.map +1 -0
- package/dist/maintenance/CodexQuota.d.ts +12 -0
- package/dist/maintenance/CodexQuota.d.ts.map +1 -0
- package/dist/maintenance/CodexQuota.js +21 -0
- package/dist/maintenance/CodexQuota.js.map +1 -0
- package/dist/maintenance/CodexStatusProbe.d.ts +2 -2
- package/dist/maintenance/CodexStatusProbe.d.ts.map +1 -1
- package/dist/maintenance/CodexStatusProbe.js +2 -2
- package/dist/maintenance/CodexStatusProbe.js.map +1 -1
- package/dist/maintenance/MaintenanceClient.d.ts.map +1 -1
- package/dist/maintenance/MaintenanceClient.js +1 -1
- package/dist/maintenance/MaintenanceClient.js.map +1 -1
- package/dist/maintenance/SubscriptionInspection.d.ts +6 -4
- package/dist/maintenance/SubscriptionInspection.d.ts.map +1 -1
- package/dist/maintenance/SubscriptionInspection.js +28 -3
- package/dist/maintenance/SubscriptionInspection.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
|
@@ -56,6 +56,8 @@ From CLI 1.6.126, acknowledged quota-reminder suppression survives upgrades and
|
|
|
56
56
|
|
|
57
57
|
Deduplication is not exactly once: a crash between card delivery and local acknowledgement persistence can still produce a duplicate. Earlier clients retain process-only suppression, and their delivery history cannot be recovered on first adoption of the ledger; one additional qualifying reminder is possible. The Router presentation change alone does not give an older CLI restart-safe suppression.
|
|
58
58
|
|
|
59
|
+
From CLI and Router 1.6.150, the same startup/hourly status read also checks native `rateLimitResetCredits.availableCount`. A **Codex Banked Reset** card is sent only when the current valid count exceeds the previous valid count for the same account. The first sample, account switches, and service restarts establish silent in-memory baselines. Equal counts and decreases do not create notices; decreases update the baseline (for example, 5 ā 3 is silent, then 3 ā 4 notifies). Missing or invalid counts are unknown, not zero, and retain the last valid count for that account. Credit details may be absent or capped and are not used to infer the count. Pending increases are replaced by newer increases and canceled after an observed decrease or account switch; ACKs stop retries for that event only, never suppress later growth. Banked delivery receipts stay in Router memory until notice expiry, bounded to 1,000 including in-flight creations. Lost-ACK retries after a CLI reconnect acknowledge the existing card after revalidating ownership instead of creating a duplicate; socket/action bindings remain invalidated. Router restarts or ambiguous creation outcomes can still duplicate delivery. Delivery is best effort, and the card shows the previous/current counts and increase at the latest check, not a live balance. The additive `bankedResetReminder` capability requires both peers to opt in, so older Routers never render the new notice as a weekly reset. The existing `maintenance.subscriptionInspection` setting controls both quota notices. No extra status request, model prompt, automatic reset-credit consumption, or expiry notice is added.
|
|
60
|
+
|
|
59
61
|
Token handoff uses an experimental, unstable app-server login API that OpenAI may change or remove. Unsupported versions make inspection unavailable without affecting messaging or falling back to a model request. Reverify this API when updating the supported Codex version.
|
|
60
62
|
|
|
61
63
|
Both features default to enabled. Set `maintenance.updateNotice` or `maintenance.subscriptionInspection` to `false` using `remote-cli config set` and restart to opt out. See [Maintenance Notices](../../README.md#maintenance-notices) for delivery, privacy, retention, and compatibility limits.
|
|
@@ -169,10 +171,10 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
|
|
|
169
171
|
| `/new` | Alias for `/clear`; start a fresh conversation in this thread |
|
|
170
172
|
| `/compact` | Compress history to save tokens |
|
|
171
173
|
| `/cd <dir>` | Change directory; a different directory starts fresh conversations for this thread |
|
|
172
|
-
| `/model [name]` |
|
|
173
|
-
| `/effort [auto|level]` |
|
|
174
|
+
| `/model [name]` | Open native model settings, or set the active backend model with a text argument |
|
|
175
|
+
| `/effort [auto|level]` | Open native effort settings, or set/clear the active backend effort with a text argument |
|
|
174
176
|
| `/sandbox [on/off/read-only/default]` | Configure the current Codex or Claude Code thread sandbox, directory grants, and networking |
|
|
175
|
-
| `/backend` |
|
|
177
|
+
| `/backend` | Open backend settings; Current thread is the default and Confirm applies the selection |
|
|
176
178
|
| `/delegation [on|off]` | Inspect or enable delegation between installed agent backends in this thread |
|
|
177
179
|
| `/backend <index>` | Switch all threads and clear per-thread backend overrides |
|
|
178
180
|
| `/backend <index> @` | Switch only the current thread |
|
|
@@ -181,6 +183,8 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
|
|
|
181
183
|
| `/unbind` | Unbind all devices |
|
|
182
184
|
| `/device` | List and switch between bound devices |
|
|
183
185
|
|
|
186
|
+
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).
|
|
187
|
+
|
|
184
188
|
### Threads & Machines
|
|
185
189
|
|
|
186
190
|
From CLI 1.6.121, new and existing threads without a saved preference enable
|
|
@@ -289,6 +293,12 @@ A hard kill can leave a transfer quarantine requiring manual cleanup after its
|
|
|
289
293
|
processes exit. Tracked submodules also matched by outer ignore rules currently
|
|
290
294
|
fail admission; reconcile those rules before delegating.
|
|
291
295
|
|
|
296
|
+
From CLI 1.6.151, checkpoint index queries are combined and identical trees are
|
|
297
|
+
audited once within a capture. Both working-file passes and source HEAD/index
|
|
298
|
+
checks remain; no state is cached across tasks and Git deadlines are unchanged.
|
|
299
|
+
CLI tests run real Git fixture suites in one isolated process alongside at most
|
|
300
|
+
two lightweight test threads, avoiding CPU-count-driven Git subprocess contention.
|
|
301
|
+
|
|
292
302
|
From CLI 1.6.140, managed Git preparation, repository-lock waits, and artifact
|
|
293
303
|
collection are awaited separately from native startup/stop deadlines. Slots and
|
|
294
304
|
source reservations remain held until owned operations settle, even on cancellation;
|
|
@@ -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();
|