@yu_robotics/remote-cli 1.6.170 → 1.6.176
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 +25 -0
- package/dist/client/GoalCommand.d.ts +15 -0
- package/dist/client/GoalCommand.d.ts.map +1 -0
- package/dist/client/GoalCommand.js +59 -0
- package/dist/client/GoalCommand.js.map +1 -0
- package/dist/client/MessageHandler.d.ts +4 -0
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +186 -46
- package/dist/client/MessageHandler.js.map +1 -1
- package/dist/client/TaskRecovery.d.ts +3 -1
- package/dist/client/TaskRecovery.d.ts.map +1 -1
- package/dist/client/TaskRecovery.js +9 -4
- package/dist/client/TaskRecovery.js.map +1 -1
- package/dist/client/WebSocketClient.d.ts +2 -1
- package/dist/client/WebSocketClient.d.ts.map +1 -1
- package/dist/client/WebSocketClient.js +9 -1
- package/dist/client/WebSocketClient.js.map +1 -1
- package/dist/delegation/DelegationManager.js +1 -1
- package/dist/delegation/DelegationManager.js.map +1 -1
- package/dist/executor/AcpExecutor.d.ts +6 -0
- package/dist/executor/AcpExecutor.d.ts.map +1 -1
- package/dist/executor/AcpExecutor.js +81 -10
- package/dist/executor/AcpExecutor.js.map +1 -1
- package/dist/executor/CodexAppServerClient.d.ts +1 -0
- package/dist/executor/CodexAppServerClient.d.ts.map +1 -1
- package/dist/executor/CodexAppServerClient.js +12 -4
- package/dist/executor/CodexAppServerClient.js.map +1 -1
- package/dist/executor/CodexAppServerExecutor.d.ts +39 -2
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +712 -32
- package/dist/executor/CodexAppServerExecutor.js.map +1 -1
- package/dist/executor/IExecutor.d.ts +43 -0
- package/dist/executor/IExecutor.d.ts.map +1 -1
- package/dist/executor/KimiExecutor.d.ts +9 -0
- package/dist/executor/KimiExecutor.d.ts.map +1 -1
- package/dist/executor/KimiExecutor.js +45 -0
- package/dist/executor/KimiExecutor.js.map +1 -1
- package/dist/executor/codex/CodexGoals.d.ts +6 -0
- package/dist/executor/codex/CodexGoals.d.ts.map +1 -0
- package/dist/executor/codex/CodexGoals.js +49 -0
- package/dist/executor/codex/CodexGoals.js.map +1 -0
- package/dist/executor/codex/CodexImages.d.ts +22 -0
- package/dist/executor/codex/CodexImages.d.ts.map +1 -0
- package/dist/executor/codex/CodexImages.js +201 -0
- package/dist/executor/codex/CodexImages.js.map +1 -0
- package/dist/executor/index.d.ts +2 -1
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +1 -0
- package/dist/executor/index.js.map +1 -1
- package/dist/executor/kimi/KimiAccountUsage.d.ts +2 -6
- package/dist/executor/kimi/KimiAccountUsage.d.ts.map +1 -1
- package/dist/executor/kimi/KimiAccountUsage.js +11 -71
- package/dist/executor/kimi/KimiAccountUsage.js.map +1 -1
- package/dist/executor/kimi/KimiLocalServer.d.ts +28 -0
- package/dist/executor/kimi/KimiLocalServer.d.ts.map +1 -0
- package/dist/executor/kimi/KimiLocalServer.js +192 -0
- package/dist/executor/kimi/KimiLocalServer.js.map +1 -0
- package/dist/executor/kimi/KimiModelCatalog.d.ts +6 -0
- package/dist/executor/kimi/KimiModelCatalog.d.ts.map +1 -0
- package/dist/executor/kimi/KimiModelCatalog.js +86 -0
- package/dist/executor/kimi/KimiModelCatalog.js.map +1 -0
- package/dist/maintenance/release-notes.json +1 -1
- package/dist/settings/SettingsCatalog.d.ts +1 -1
- package/dist/settings/SettingsCatalog.d.ts.map +1 -1
- package/dist/settings/SettingsCatalog.js +6 -2
- package/dist/settings/SettingsCatalog.js.map +1 -1
- package/dist/settings/SettingsService.d.ts +3 -1
- package/dist/settings/SettingsService.d.ts.map +1 -1
- package/dist/settings/SettingsService.js +17 -5
- package/dist/settings/SettingsService.js.map +1 -1
- package/dist/thread/ThreadExecutorPool.d.ts +3 -1
- package/dist/thread/ThreadExecutorPool.d.ts.map +1 -1
- package/dist/thread/ThreadExecutorPool.js +13 -1
- package/dist/thread/ThreadExecutorPool.js.map +1 -1
- package/dist/thread/ThreadManager.d.ts +1 -1
- package/dist/thread/ThreadManager.d.ts.map +1 -1
- package/dist/thread/ThreadManager.js.map +1 -1
- package/dist/thread/types.d.ts +2 -0
- package/dist/thread/types.d.ts.map +1 -1
- package/dist/types/Settings.d.ts +8 -0
- package/dist/types/Settings.d.ts.map +1 -1
- package/dist/utils/ImageDelivery.d.ts +67 -0
- package/dist/utils/ImageDelivery.d.ts.map +1 -0
- package/dist/utils/ImageDelivery.js +270 -0
- package/dist/utils/ImageDelivery.js.map +1 -0
- package/dist/utils/ImageValidation.d.ts +11 -0
- package/dist/utils/ImageValidation.d.ts.map +1 -0
- package/dist/utils/ImageValidation.js +62 -0
- package/dist/utils/ImageValidation.js.map +1 -0
- package/dist/utils/LocalImageDetector.d.ts +12 -3
- package/dist/utils/LocalImageDetector.d.ts.map +1 -1
- package/dist/utils/LocalImageDetector.js +153 -55
- package/dist/utils/LocalImageDetector.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,9 +12,11 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
12
12
|
const os_1 = __importDefault(require("os"));
|
|
13
13
|
const path_1 = __importDefault(require("path"));
|
|
14
14
|
const DelegationSettings_1 = require("../thread/DelegationSettings");
|
|
15
|
+
const GoalCommand_1 = require("./GoalCommand");
|
|
15
16
|
const hooks_1 = require("../hooks");
|
|
16
17
|
const FileReadDetector_1 = require("../utils/FileReadDetector");
|
|
17
18
|
const LocalImageDetector_1 = require("../utils/LocalImageDetector");
|
|
19
|
+
const ImageDelivery_1 = require("../utils/ImageDelivery");
|
|
18
20
|
const WorkingDirectory_1 = require("../utils/WorkingDirectory");
|
|
19
21
|
const MachineCommands_1 = require("../machines/MachineCommands");
|
|
20
22
|
const child_process_1 = require("child_process");
|
|
@@ -167,6 +169,7 @@ class MessageHandler {
|
|
|
167
169
|
});
|
|
168
170
|
activeThreadOperations = new Map();
|
|
169
171
|
abortOperations = new Map();
|
|
172
|
+
pendingGoalActivations = new Map();
|
|
170
173
|
automaticUpdateInProgress = false;
|
|
171
174
|
approvalCardsSupported = false;
|
|
172
175
|
streamingContextSupported = false;
|
|
@@ -176,6 +179,7 @@ class MessageHandler {
|
|
|
176
179
|
activityProgressSupported = false;
|
|
177
180
|
workerContextResetSupported = false;
|
|
178
181
|
settingsCardsSupported = false;
|
|
182
|
+
modelSpeedSettingsSupported = false;
|
|
179
183
|
directoryCardsSupported = false;
|
|
180
184
|
directoryBrowser;
|
|
181
185
|
delegationCardsSupported = false;
|
|
@@ -186,6 +190,8 @@ class MessageHandler {
|
|
|
186
190
|
delegation;
|
|
187
191
|
delegationBridges = new Map();
|
|
188
192
|
fileInbox;
|
|
193
|
+
imageDeliveries = new Map();
|
|
194
|
+
imageConnectionEpoch = 0;
|
|
189
195
|
constructor(wsClient, threadPool, threadManager, directoryGuard, config) {
|
|
190
196
|
this.wsClient = wsClient;
|
|
191
197
|
this.threadPool = threadPool;
|
|
@@ -193,8 +199,11 @@ class MessageHandler {
|
|
|
193
199
|
this.directoryGuard = directoryGuard;
|
|
194
200
|
this.config = config;
|
|
195
201
|
this.wsClient.onClose?.(() => {
|
|
202
|
+
this.imageConnectionEpoch++;
|
|
196
203
|
this.routerVersion = undefined;
|
|
197
204
|
this.fileInbox?.disconnect();
|
|
205
|
+
for (const delivery of this.imageDeliveries.values())
|
|
206
|
+
delivery.invalidatePending();
|
|
198
207
|
});
|
|
199
208
|
this.delegation = new DelegationManager_1.DelegationManager(directoryGuard);
|
|
200
209
|
this.settingsCatalogReader = new SettingsCatalog_1.SettingsCatalogReader(directoryGuard);
|
|
@@ -216,7 +225,7 @@ class MessageHandler {
|
|
|
216
225
|
if (!thread)
|
|
217
226
|
throw new Error('This thread no longer exists.');
|
|
218
227
|
const config = this.executorConfiguration();
|
|
219
|
-
return this.settingsCatalogReader.read(config, thread, this.threadPool.getBackendKey(id), target, kind);
|
|
228
|
+
return this.settingsCatalogReader.read(config, thread, this.threadPool.getBackendKey(id), target, kind, this.modelSpeedSettingsSupported);
|
|
220
229
|
},
|
|
221
230
|
busy: (id, except, queue = true) => this.settingsBusy(id, except, queue),
|
|
222
231
|
threads: () => this.threadManager.listThreads().map(thread => thread.id),
|
|
@@ -234,6 +243,7 @@ class MessageHandler {
|
|
|
234
243
|
preference: (id, target, kind, value) => this.applyPreference(id, target, kind, value),
|
|
235
244
|
delegation: (id, enabled) => this.applyDelegationSetting(id, enabled),
|
|
236
245
|
delegationSupported: id => Boolean(this.threadPool.getExecutor(id).configureDelegation),
|
|
246
|
+
speedSettingsSupported: () => this.modelSpeedSettingsSupported,
|
|
237
247
|
});
|
|
238
248
|
this.directoryBrowser = new DirectoryBrowser_1.DirectoryBrowser({
|
|
239
249
|
guard: this.directoryGuard,
|
|
@@ -344,6 +354,7 @@ class MessageHandler {
|
|
|
344
354
|
this.delegationProgressSupported = data.capabilities?.delegationProgress === true;
|
|
345
355
|
this.workerContextResetSupported = this.delegationProgressSupported && data.capabilities?.workerContextReset === true;
|
|
346
356
|
this.settingsCardsSupported = data.capabilities?.settingsCards === true;
|
|
357
|
+
this.modelSpeedSettingsSupported = this.settingsCardsSupported && data.capabilities?.modelSpeedSettings === true;
|
|
347
358
|
this.directoryCardsSupported = this.settingsCardsSupported && data.capabilities?.directoryCards === true;
|
|
348
359
|
this.delegationCardsSupported = this.settingsCardsSupported && data.capabilities?.delegationCards === true;
|
|
349
360
|
this.delegationProgressTextSupported = this.delegationProgressSupported
|
|
@@ -525,6 +536,53 @@ class MessageHandler {
|
|
|
525
536
|
}
|
|
526
537
|
const executor = this.threadPool.getExecutor(resolvedThreadId);
|
|
527
538
|
this.sendStreamContext(messageId, resolvedThreadId, executor);
|
|
539
|
+
const goalCommand = (0, GoalCommand_1.parseGoalCommand)(settingsCommand);
|
|
540
|
+
let goalRequest;
|
|
541
|
+
if (goalCommand) {
|
|
542
|
+
if (goalCommand.kind === 'invalid' || goalCommand.kind === 'help') {
|
|
543
|
+
this.sendResponse(messageId, resolvedThreadId, goalCommand.kind === 'invalid'
|
|
544
|
+
? { success: false, error: goalCommand.error } : { success: true, output: GoalCommand_1.GOAL_USAGE });
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (this.threadPool.getBackendKey(resolvedThreadId) !== 'codex'
|
|
548
|
+
|| !executor.getGoal || !executor.executeGoal || !executor.pauseGoal || !executor.clearGoal) {
|
|
549
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
550
|
+
error: '/goal requires the Codex app-server backend and an executor version with native goal support.' });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (attachments?.length || message.fileIds?.length) {
|
|
554
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
555
|
+
error: 'Send attachments in a normal message before starting a goal. No goal was changed.' });
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (goalCommand.kind !== 'run') {
|
|
559
|
+
try {
|
|
560
|
+
const pending = this.pendingGoalActivations.get(resolvedThreadId);
|
|
561
|
+
if (pending && goalCommand.kind !== 'show')
|
|
562
|
+
pending.cancelled = true;
|
|
563
|
+
const result = goalCommand.kind === 'show' ? { success: true, output: (0, GoalCommand_1.formatGoal)(await executor.getGoal()) }
|
|
564
|
+
: goalCommand.kind === 'pause' ? await executor.pauseGoal() : await executor.clearGoal();
|
|
565
|
+
this.sendResponse(messageId, resolvedThreadId, { ...result, threads: this.threadPool.getSummaries() });
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
569
|
+
error: `Goal control failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if ((0, DelegationSettings_1.isDelegationEnabled)(thread) || this.delegation.hasActiveTasks(resolvedThreadId)) {
|
|
574
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
575
|
+
error: 'Native goals require managed delegation to be off. Wait for workers to finish and send /delegation off in this thread, then retry /goal.' });
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (this.threadPool.isThreadBusy(resolvedThreadId) || executor.isBusy?.()
|
|
579
|
+
|| this.hasThreadQueueState(resolvedThreadId) || this.abortOperations.has(resolvedThreadId)) {
|
|
580
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
581
|
+
error: 'Start or resume a goal only when this thread and its queue are idle. Use /goal pause or /abort to stop current goal work.' });
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
goalRequest = goalCommand.request;
|
|
585
|
+
}
|
|
528
586
|
// Handle /abort for this specific thread (bypasses busy check)
|
|
529
587
|
if (content?.trim() === '/abort') {
|
|
530
588
|
await this.handleAbortCommand(messageId, resolvedThreadId, executor);
|
|
@@ -594,7 +652,7 @@ class MessageHandler {
|
|
|
594
652
|
return;
|
|
595
653
|
}
|
|
596
654
|
const inputExecutor = this.delegation.waitingExecutor(resolvedThreadId) ?? executor;
|
|
597
|
-
if ('isWaitingInput' in inputExecutor && typeof inputExecutor.isWaitingInput === 'function') {
|
|
655
|
+
if (!goalRequest && 'isWaitingInput' in inputExecutor && typeof inputExecutor.isWaitingInput === 'function') {
|
|
598
656
|
const ex = inputExecutor;
|
|
599
657
|
if (ex.isWaitingInput()) {
|
|
600
658
|
const input = content?.trim();
|
|
@@ -682,6 +740,9 @@ class MessageHandler {
|
|
|
682
740
|
});
|
|
683
741
|
return;
|
|
684
742
|
}
|
|
743
|
+
const goalActivation = goalRequest ? { cancelled: false } : undefined;
|
|
744
|
+
if (goalActivation)
|
|
745
|
+
this.pendingGoalActivations.set(resolvedThreadId, goalActivation);
|
|
685
746
|
const operationToken = this.beginThreadOperation(resolvedThreadId);
|
|
686
747
|
try {
|
|
687
748
|
this.trackTask(messageId, resolvedThreadId, content || '');
|
|
@@ -716,6 +777,10 @@ class MessageHandler {
|
|
|
716
777
|
await this.executePendingReplace(messageId, resolvedThreadId, pendingKey, content);
|
|
717
778
|
return;
|
|
718
779
|
}
|
|
780
|
+
if (goalRequest) {
|
|
781
|
+
await this.executeCommand(messageId, resolvedThreadId, content, executor, undefined, false, goalRequest, goalActivation);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
719
784
|
const builtInResult = await this.handleBuiltInCommand(messageId, resolvedThreadId, content, executor);
|
|
720
785
|
if (builtInResult)
|
|
721
786
|
return;
|
|
@@ -736,6 +801,9 @@ class MessageHandler {
|
|
|
736
801
|
});
|
|
737
802
|
}
|
|
738
803
|
finally {
|
|
804
|
+
if (goalActivation && this.pendingGoalActivations.get(resolvedThreadId) === goalActivation) {
|
|
805
|
+
this.pendingGoalActivations.delete(resolvedThreadId);
|
|
806
|
+
}
|
|
739
807
|
if (this.finishThreadOperation(resolvedThreadId, operationToken) && !this.pausedQueues.has(resolvedThreadId)) {
|
|
740
808
|
void this.startNextQueuedCommand(resolvedThreadId);
|
|
741
809
|
}
|
|
@@ -746,9 +814,20 @@ class MessageHandler {
|
|
|
746
814
|
*/
|
|
747
815
|
async handleAbortCommand(messageId, threadId, executor) {
|
|
748
816
|
this.fileInbox?.cancelThread(threadId);
|
|
817
|
+
this.imageDeliveries.get(threadId)?.cancel();
|
|
818
|
+
const pendingGoal = this.pendingGoalActivations.get(threadId);
|
|
819
|
+
if (pendingGoal)
|
|
820
|
+
pendingGoal.cancelled = true;
|
|
749
821
|
const existingAbort = this.abortOperations.get(threadId);
|
|
750
822
|
if (existingAbort) {
|
|
751
823
|
await existingAbort;
|
|
824
|
+
if (executor.isGoalRunning?.()) {
|
|
825
|
+
this.sendResponse(messageId, threadId, {
|
|
826
|
+
success: false,
|
|
827
|
+
error: 'Native goal stop could not be confirmed. This thread remains busy; retry /goal pause or /abort.',
|
|
828
|
+
});
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
752
831
|
this.sendResponse(messageId, threadId, {
|
|
753
832
|
success: true,
|
|
754
833
|
output: 'ℹ️ The abort operation has already completed.',
|
|
@@ -763,6 +842,9 @@ class MessageHandler {
|
|
|
763
842
|
if ((0, DelegationSettings_1.isDelegationEnabled)(this.threadManager.getThread(threadId)))
|
|
764
843
|
await this.delegation.cancelThread(threadId);
|
|
765
844
|
aborted = await executor.abort();
|
|
845
|
+
if (!aborted && executor.isGoalRunning?.()) {
|
|
846
|
+
throw new Error('Native goal stop could not be confirmed. This thread remains busy; retry /goal pause or /abort.');
|
|
847
|
+
}
|
|
766
848
|
if (activeOperation)
|
|
767
849
|
await activeOperation.done;
|
|
768
850
|
})();
|
|
@@ -1051,6 +1133,7 @@ Use /compact to reduce conversation context or /clear to start a fresh context.`
|
|
|
1051
1133
|
- /cd <directory> - Change working directory for this thread
|
|
1052
1134
|
- /model [name] - Open model settings, or set the active backend model with a text argument
|
|
1053
1135
|
- /effort [auto|level] - Open effort settings, or set a native supported level with a text argument
|
|
1136
|
+
- /goal [objective|pause|resume|clear] - Manage a native Codex goal; optional --budget <tokens> on start or resume
|
|
1054
1137
|
- /sandbox [on|off|read-only|default|allow <directory>|remove <directory>|network on|off] - Configure this thread's sandbox (Codex, Claude Code)
|
|
1055
1138
|
- /delegation [on|off|reset [backend]] - Delegate tasks between installed agent backends; reset saved worker context when needed
|
|
1056
1139
|
- /backend - Open backend settings (Current thread by default; Confirm applies)
|
|
@@ -2010,12 +2093,13 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2010
2093
|
/**
|
|
2011
2094
|
* Execute AI command on a specific executor
|
|
2012
2095
|
*/
|
|
2013
|
-
async executeCommand(messageId, threadId, content, executor, attachments, fromQueue = false) {
|
|
2096
|
+
async executeCommand(messageId, threadId, content, executor, attachments, fromQueue = false, goalRequest, goalActivation) {
|
|
2014
2097
|
this.sendStreamContext(messageId, threadId, executor);
|
|
2015
2098
|
let delegationScope;
|
|
2016
2099
|
let delegationBridge;
|
|
2017
2100
|
let executionMetadata;
|
|
2018
2101
|
let requestFinished = false;
|
|
2102
|
+
let imageDelivery;
|
|
2019
2103
|
let executionActive = false;
|
|
2020
2104
|
let lastProgramState;
|
|
2021
2105
|
const activity = new ActivityProgress_1.ActivityProgress(snapshot => {
|
|
@@ -2040,6 +2124,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2040
2124
|
lastProgramState = text;
|
|
2041
2125
|
};
|
|
2042
2126
|
const complete = async (success, error) => {
|
|
2127
|
+
await imageDelivery?.finish();
|
|
2128
|
+
if (this.imageDeliveries.get(threadId) === imageDelivery)
|
|
2129
|
+
this.imageDeliveries.delete(threadId);
|
|
2043
2130
|
requestFinished = true;
|
|
2044
2131
|
activity.dispose();
|
|
2045
2132
|
delegationBridge?.activate(undefined);
|
|
@@ -2079,6 +2166,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2079
2166
|
}
|
|
2080
2167
|
const openId = this.getMessageOpenId(messageId);
|
|
2081
2168
|
const thread = this.threadManager.getThread(threadId);
|
|
2169
|
+
if (goalRequest && thread && (0, DelegationSettings_1.isDelegationEnabled)(thread)) {
|
|
2170
|
+
throw new Error('Turn managed delegation off before starting a native goal.');
|
|
2171
|
+
}
|
|
2082
2172
|
if (thread && (0, DelegationSettings_1.isDelegationEnabled)(thread) && executor.configureDelegation) {
|
|
2083
2173
|
if (this.delegation.blocksWorkspace(executor.getCurrentWorkingDirectory(), threadId)) {
|
|
2084
2174
|
throw new Error('A delegated worker holds this workspace. Wait for it to finish; if worker cleanup failed, stop the worker and restart the CLI.');
|
|
@@ -2133,37 +2223,41 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2133
2223
|
return;
|
|
2134
2224
|
this.notificationAdapter.sendTaskNotification({ ...notification, threadId }, openId);
|
|
2135
2225
|
};
|
|
2136
|
-
const
|
|
2137
|
-
const
|
|
2226
|
+
const imageCwd = thread?.workingDirectory ?? executor.getCurrentWorkingDirectory();
|
|
2227
|
+
const imageGeneration = thread?.delegationWorkspaceGeneration ?? 0;
|
|
2228
|
+
const imageBackend = this.threadPool.getBackendKey(threadId);
|
|
2229
|
+
let imagePhase = 0;
|
|
2230
|
+
const imageEligible = () => {
|
|
2231
|
+
const phase = imagePhase;
|
|
2232
|
+
return () => phase === imagePhase && (!delegationScope
|
|
2233
|
+
|| (!delegationScope.isClosed() && !delegationScope.hasPendingResults()));
|
|
2234
|
+
};
|
|
2235
|
+
imageDelivery = new ImageDelivery_1.ImageDelivery({
|
|
2236
|
+
readFile: filePath => (0, LocalImageDetector_1.readLocalImagePath)(filePath, imageCwd, this.directoryGuard),
|
|
2237
|
+
publish: image => {
|
|
2238
|
+
if (!this.sendImage(messageId, threadId, image))
|
|
2239
|
+
throw new Error('Image send failed');
|
|
2240
|
+
},
|
|
2241
|
+
notice: text => this.sendStreamChunk(messageId, threadId, text),
|
|
2242
|
+
isCurrent: () => {
|
|
2243
|
+
const current = this.threadManager.getThread(threadId);
|
|
2244
|
+
return !requestFinished && !this.isDestroyed && !this.abortOperations.has(threadId)
|
|
2245
|
+
&& this.wsClient.isConnected() && this.imageDeliveries.get(threadId) === imageDelivery
|
|
2246
|
+
&& this.wsClient.canSendTaskOutput?.(messageId) !== false
|
|
2247
|
+
&& !!current && current.workingDirectory === imageCwd
|
|
2248
|
+
&& (current.delegationWorkspaceGeneration ?? 0) === imageGeneration
|
|
2249
|
+
&& this.threadPool.getBackendKey(threadId) === imageBackend;
|
|
2250
|
+
},
|
|
2251
|
+
});
|
|
2252
|
+
this.imageDeliveries.get(threadId)?.cancel();
|
|
2253
|
+
this.imageDeliveries.set(threadId, imageDelivery);
|
|
2138
2254
|
const delegationControlTools = new Set();
|
|
2139
2255
|
const toolImageStates = new Map();
|
|
2140
2256
|
const webToolNames = new Set(['websearch', 'webfetch', 'web_search', 'web_fetch', 'search_web', 'read_url_content', 'fetch']);
|
|
2141
|
-
let
|
|
2142
|
-
|
|
2143
|
-
try {
|
|
2144
|
-
const thread = this.threadManager.getThread(threadId);
|
|
2145
|
-
if (typeof text !== 'string' || !text || !thread?.workingDirectory)
|
|
2146
|
-
return;
|
|
2147
|
-
const images = await (0, LocalImageDetector_1.readLocalImages)(text, thread.workingDirectory, (candidate, cwd) => (this.directoryGuard.isSafePath(candidate, cwd)));
|
|
2148
|
-
for (const image of images) {
|
|
2149
|
-
if (emittedLocalImages.has(image.path))
|
|
2150
|
-
continue;
|
|
2151
|
-
emittedLocalImages.add(image.path);
|
|
2152
|
-
this.sendImage(messageId, threadId, {
|
|
2153
|
-
type: 'image',
|
|
2154
|
-
data: image.data,
|
|
2155
|
-
mimeType: image.mimeType,
|
|
2156
|
-
});
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
catch {
|
|
2160
|
-
// Image forwarding is optional; never reject a task or log native content on failure.
|
|
2161
|
-
console.warn('Failed to forward local images; continuing task output.');
|
|
2162
|
-
}
|
|
2163
|
-
};
|
|
2257
|
+
let streamedAny = false;
|
|
2258
|
+
let imageTextDropped = false;
|
|
2164
2259
|
const queueLocalImages = (text) => {
|
|
2165
|
-
|
|
2166
|
-
pendingLocalImageEmissions.add(pending);
|
|
2260
|
+
imageDelivery?.offerText(text, imageEligible());
|
|
2167
2261
|
};
|
|
2168
2262
|
const executeOptions = {
|
|
2169
2263
|
onActivity: this.activityProgressSupported ? (snapshot) => activity.offer(snapshot) : undefined,
|
|
@@ -2174,8 +2268,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2174
2268
|
onApprovalResolved: (requestId, status) => this.resolveApprovalCard(requestId, status),
|
|
2175
2269
|
onTaskNotification,
|
|
2176
2270
|
onStream: (chunk) => {
|
|
2177
|
-
|
|
2178
|
-
this.sendStreamChunk(messageId, threadId, chunk)
|
|
2271
|
+
streamedAny = true;
|
|
2272
|
+
if (this.sendStreamChunk(messageId, threadId, chunk))
|
|
2273
|
+
imageDelivery?.offerStream(chunk, imageEligible());
|
|
2274
|
+
else
|
|
2275
|
+
imageTextDropped = true;
|
|
2179
2276
|
},
|
|
2180
2277
|
onToolUse: (toolUse) => {
|
|
2181
2278
|
if (delegationScope && /remote_cli_(list_backends|delegate|result|cancel|integrate)$/.test(toolUse.name)) {
|
|
@@ -2213,7 +2310,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2213
2310
|
},
|
|
2214
2311
|
onRedactedThinking: () => this.sendRedactedThinking(messageId, threadId),
|
|
2215
2312
|
onPlanMode: (planContent) => this.sendPlanMode(messageId, threadId, planContent),
|
|
2216
|
-
onImage: (image) =>
|
|
2313
|
+
onImage: (image) => imageDelivery?.offerImage(image, imageEligible()),
|
|
2314
|
+
onImageFile: (filePath) => imageDelivery?.offerFile(filePath, imageEligible()),
|
|
2217
2315
|
attachments,
|
|
2218
2316
|
};
|
|
2219
2317
|
const assertCoordinatorActive = () => {
|
|
@@ -2225,6 +2323,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2225
2323
|
let mayRetry = true;
|
|
2226
2324
|
const executeOnce = async (prompt, options, flushImages = true) => {
|
|
2227
2325
|
assertCoordinatorActive();
|
|
2326
|
+
imagePhase++;
|
|
2327
|
+
streamedAny = false;
|
|
2328
|
+
imageTextDropped = false;
|
|
2329
|
+
imageDelivery?.resetStream();
|
|
2330
|
+
const imageEpoch = this.imageConnectionEpoch;
|
|
2228
2331
|
const scope = delegationScope;
|
|
2229
2332
|
let active = true;
|
|
2230
2333
|
let finished = false;
|
|
@@ -2280,6 +2383,10 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2280
2383
|
options.onImage?.(image);
|
|
2281
2384
|
else
|
|
2282
2385
|
suppressed = true; },
|
|
2386
|
+
onImageFile: filePath => { if (canPublish())
|
|
2387
|
+
options.onImageFile?.(filePath);
|
|
2388
|
+
else
|
|
2389
|
+
suppressed = true; },
|
|
2283
2390
|
onRedactedThinking: () => { if (canPublish())
|
|
2284
2391
|
options.onRedactedThinking?.(); },
|
|
2285
2392
|
onToolUse: tool => { if (active && !scope?.isClosed())
|
|
@@ -2292,7 +2399,19 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2292
2399
|
executionMetadata = launchMetadata;
|
|
2293
2400
|
let capturedMetadata = false;
|
|
2294
2401
|
try {
|
|
2295
|
-
|
|
2402
|
+
if (goalRequest && (goalActivation?.cancelled || this.isDestroyed || this.abortOperations.has(threadId))) {
|
|
2403
|
+
throw new Error('Native goal activation was cancelled before it started.');
|
|
2404
|
+
}
|
|
2405
|
+
// No await between releasing preparation ownership and the adapter's
|
|
2406
|
+
// synchronous installation of native goal ownership.
|
|
2407
|
+
if (goalActivation && this.pendingGoalActivations.get(threadId) === goalActivation) {
|
|
2408
|
+
this.pendingGoalActivations.delete(threadId);
|
|
2409
|
+
}
|
|
2410
|
+
const result = goalRequest
|
|
2411
|
+
? await executor.executeGoal(goalRequest, guardedOptions)
|
|
2412
|
+
: await executor.execute(prompt, guardedOptions);
|
|
2413
|
+
if (goalRequest && !streamedAny && result.output)
|
|
2414
|
+
guardedOptions.onStream?.(result.output);
|
|
2296
2415
|
executionMetadata = (0, ExecutionMetadata_1.mergeReportedExecutionMetadata)(launchMetadata, (0, ExecutionMetadata_1.captureExecutionMetadata)(executor, backend));
|
|
2297
2416
|
capturedMetadata = true;
|
|
2298
2417
|
active = false;
|
|
@@ -2303,9 +2422,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2303
2422
|
scope?.finishExecution(result.success);
|
|
2304
2423
|
finished = true;
|
|
2305
2424
|
if (flushImages) {
|
|
2306
|
-
|
|
2307
|
-
const visibleResult =
|
|
2308
|
-
|
|
2425
|
+
imageDelivery?.flushStream();
|
|
2426
|
+
const visibleResult = imageTextDropped || this.imageConnectionEpoch !== imageEpoch
|
|
2427
|
+
|| (scope && (suppressed || scope.hasPendingResults())) ? '' : result.output ?? '';
|
|
2428
|
+
queueLocalImages(visibleResult);
|
|
2429
|
+
await imageDelivery?.drain();
|
|
2309
2430
|
}
|
|
2310
2431
|
assertCoordinatorActive();
|
|
2311
2432
|
return result;
|
|
@@ -2319,7 +2440,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2319
2440
|
delegationBridge?.activate(undefined);
|
|
2320
2441
|
if (!finished)
|
|
2321
2442
|
scope?.finishExecution(false);
|
|
2322
|
-
|
|
2443
|
+
// Goal activation mutates durable native state; never replay it as a model retry.
|
|
2444
|
+
mayRetry = !goalRequest && (!scope || scope.getLaunchRevision() === launchRevision);
|
|
2323
2445
|
}
|
|
2324
2446
|
};
|
|
2325
2447
|
let prompt = content;
|
|
@@ -2514,7 +2636,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2514
2636
|
}
|
|
2515
2637
|
sendStreamChunk(messageId, threadId, chunk) {
|
|
2516
2638
|
try {
|
|
2517
|
-
this.wsClient.send({
|
|
2639
|
+
return this.wsClient.send({
|
|
2518
2640
|
type: 'stream',
|
|
2519
2641
|
messageId,
|
|
2520
2642
|
chunk,
|
|
@@ -2522,10 +2644,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2522
2644
|
openId: this.getMessageOpenId(messageId),
|
|
2523
2645
|
threadId,
|
|
2524
2646
|
timestamp: Date.now(),
|
|
2525
|
-
});
|
|
2647
|
+
}) !== false;
|
|
2526
2648
|
}
|
|
2527
2649
|
catch (error) {
|
|
2528
2650
|
console.error('Failed to send stream chunk:', error);
|
|
2651
|
+
return false;
|
|
2529
2652
|
}
|
|
2530
2653
|
}
|
|
2531
2654
|
sendToolUse(messageId, threadId, toolUse) {
|
|
@@ -2624,7 +2747,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2624
2747
|
}
|
|
2625
2748
|
sendImage(messageId, threadId, image) {
|
|
2626
2749
|
try {
|
|
2627
|
-
this.wsClient.send({
|
|
2750
|
+
return this.wsClient.send({
|
|
2628
2751
|
type: 'stream',
|
|
2629
2752
|
messageId,
|
|
2630
2753
|
streamType: 'image',
|
|
@@ -2632,10 +2755,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2632
2755
|
openId: this.getMessageOpenId(messageId),
|
|
2633
2756
|
threadId,
|
|
2634
2757
|
timestamp: Date.now(),
|
|
2635
|
-
});
|
|
2758
|
+
}) !== false;
|
|
2636
2759
|
}
|
|
2637
|
-
catch
|
|
2638
|
-
console.
|
|
2760
|
+
catch {
|
|
2761
|
+
console.warn('Failed to send generated image.');
|
|
2762
|
+
return false;
|
|
2639
2763
|
}
|
|
2640
2764
|
}
|
|
2641
2765
|
sendStructuredContent(messageId, threadId, structuredContent) {
|
|
@@ -2718,7 +2842,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2718
2842
|
return undefined;
|
|
2719
2843
|
const projection = (entry) => ({ id: entry.id, backend: entry.backend,
|
|
2720
2844
|
cwd: entry.workingDirectory, generation: entry.delegationWorkspaceGeneration, model: entry.model,
|
|
2721
|
-
models: entry.models, efforts: entry.efforts, delegation: entry.delegation, delegationBackends: entry.delegationBackends });
|
|
2845
|
+
models: entry.models, efforts: entry.efforts, speeds: entry.speeds, delegation: entry.delegation, delegationBackends: entry.delegationBackends });
|
|
2722
2846
|
const all = this.threadManager.listThreads().map(projection).sort((a, b) => a.id.localeCompare(b.id));
|
|
2723
2847
|
const configuredModels = { ...thread.models };
|
|
2724
2848
|
if (configuredModels.claude === undefined && thread.model)
|
|
@@ -2786,6 +2910,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2786
2910
|
const config = this.executorConfiguration();
|
|
2787
2911
|
const coordinator = this.threadPool.getBackendKey(threadId);
|
|
2788
2912
|
const apply = async (executor) => {
|
|
2913
|
+
if (kind === 'speed') {
|
|
2914
|
+
if (!this.modelSpeedSettingsSupported || !executor.setSpeed)
|
|
2915
|
+
return { success: false, error: 'Native speed settings are unavailable.' };
|
|
2916
|
+
return executor.setSpeed(value);
|
|
2917
|
+
}
|
|
2789
2918
|
if (kind === 'effort') {
|
|
2790
2919
|
if (!executor.setEffort)
|
|
2791
2920
|
return { success: false, error: 'This backend does not expose effort controls.' };
|
|
@@ -2822,7 +2951,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2822
2951
|
await this.threadManager.updateThread(threadId, { models: Object.keys(models).length ? models : undefined,
|
|
2823
2952
|
...(backend === 'claude' ? { model: value } : {}) });
|
|
2824
2953
|
}
|
|
2825
|
-
else {
|
|
2954
|
+
else if (kind === 'effort') {
|
|
2826
2955
|
const efforts = { ...current.efforts };
|
|
2827
2956
|
if (value === undefined)
|
|
2828
2957
|
delete efforts[backend];
|
|
@@ -2830,6 +2959,14 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2830
2959
|
efforts[backend] = value;
|
|
2831
2960
|
await this.threadManager.updateThread(threadId, { efforts: Object.keys(efforts).length ? efforts : undefined });
|
|
2832
2961
|
}
|
|
2962
|
+
else {
|
|
2963
|
+
const speeds = { ...current.speeds };
|
|
2964
|
+
// Null records an explicit return to native configuration, so resumed
|
|
2965
|
+
// worker lanes also clear any previously sticky tier. Unset old threads
|
|
2966
|
+
// retain their original RPC shapes without additional settings reads.
|
|
2967
|
+
speeds[backend] = value ?? null;
|
|
2968
|
+
await this.threadManager.updateThread(threadId, { speeds: Object.keys(speeds).length ? speeds : undefined });
|
|
2969
|
+
}
|
|
2833
2970
|
return result;
|
|
2834
2971
|
}
|
|
2835
2972
|
finally {
|
|
@@ -2997,6 +3134,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2997
3134
|
this.directoryBrowser.destroy();
|
|
2998
3135
|
this.settingsService.destroy();
|
|
2999
3136
|
this.isDestroyed = true;
|
|
3137
|
+
for (const delivery of this.imageDeliveries.values())
|
|
3138
|
+
delivery.cancel();
|
|
3139
|
+
this.imageDeliveries.clear();
|
|
3000
3140
|
await this.fileInbox?.destroy();
|
|
3001
3141
|
this.notificationAdapter.unregister();
|
|
3002
3142
|
try {
|