@yu_robotics/remote-cli 1.6.170 → 1.6.173
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 +21 -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 +3 -0
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +167 -43
- 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 +8 -1
- package/dist/client/WebSocketClient.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 +28 -2
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +517 -30
- package/dist/executor/CodexAppServerExecutor.js.map +1 -1
- package/dist/executor/IExecutor.d.ts +24 -0
- package/dist/executor/IExecutor.d.ts.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/maintenance/release-notes.json +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
package/README.md
CHANGED
|
@@ -78,6 +78,12 @@ The client is normally installed and run directly on the developer's machine rat
|
|
|
78
78
|
|
|
79
79
|
Claude tool-result content blocks are normalized to text; embedded media and unknown payloads are not scanned for local files. Local image detection and forwarding are best-effort: failures do not fail the task or terminate the CLI. Web-result excerpts remain excluded from local-file scanning.
|
|
80
80
|
|
|
81
|
+
CLI and Router 1.6.173 add direct forwarding for recognized successful Codex native image results, MCP image blocks, and dynamic-tool inline images. These images do not require a Markdown link. Native saved-file references use the same local directory policy as Markdown images. Structured extraction does not traverse arbitrary JSON path fields or publish image-view events, and remote URLs are not fetched. Ordinary tool-result text retains local-path detection; shell tools that return only text still need a local path or Markdown image link.
|
|
82
|
+
|
|
83
|
+
Each reply accepts up to 32 image candidates and displays up to 8 distinct images, with a 2 MiB decoded limit per image. Supported raster formats are checked from their bytes. Local files must pass canonical directory and regular-file checks; denied, missing, invalid, oversized, or timed-out images produce a bounded omission notice while text can still complete. Identical bytes are deduplicated within the reply, including native and Markdown copies; changed bytes at the same path can be displayed again. Reads and uploads have bounded concurrency and waiting times, and late results cannot be attached after cancellation or replacement.
|
|
84
|
+
|
|
85
|
+
Upgrade both CLI and Router for the complete fix: the CLI discovers and validates images, while the Router waits for admitted uploads before completing the card. The image wire format remains compatible with older image-capable peers, but an older Router retains its upload/finalization race. Delivery is best-effort with no image replay across disconnects; a model saying it sent an image is not a delivery acknowledgement.
|
|
86
|
+
|
|
81
87
|
Sending local image files requires a Router with image forwarding support and
|
|
82
88
|
the bot application's `im:resource` permission. Router 1.6.85 and newer render
|
|
83
89
|
local and URL-based Markdown image references as captions; the uploaded image
|
|
@@ -431,6 +437,21 @@ With CLI 1.6.93 or newer, `/cd` to a different normalized directory clears saved
|
|
|
431
437
|
|
|
432
438
|
Thread switch panels show `Reply from:` with the reply's full thread name and workspace. Under `Switch thread`, a check mark (`✓`) and primary button styling identify the destination of new top-level messages when the card is finalized or clicked. Selected buttons remain clickable. Switching updates only the clicked card; other historical cards retain their last displayed selection.
|
|
433
439
|
|
|
440
|
+
From Router 1.6.171, completed cards keep the thread buttons and **+ New** inside a native Feishu **Switch thread** folding panel, collapsed by default. Click its header to expand or collapse the list without sending a command or changing the input target. The reply-source caption remains visible. Selecting a thread uses the existing routing behavior. Hidden buttons still count toward card limits; unrelated full-card updates can reset the panel to its default collapsed state. This uses the existing Card 2.0 client requirement (Feishu 7.20 or newer).
|
|
441
|
+
|
|
442
|
+
From CLI 1.6.172, Codex threads support native goals through `/goal` (status), `/goal <objective>` (start), `/goal pause`, `/goal resume`, and `/goal clear`. Start or resume only when the thread and queue are idle, with managed delegation explicitly disabled using `/delegation off`. This initial restriction does not disable Codex's native subagents. Optional `--budget <tokens>` on start or resume sets an explicit positive token budget; omitting it does not invent a budget. For example:
|
|
443
|
+
|
|
444
|
+
```text
|
|
445
|
+
/delegation off
|
|
446
|
+
/goal --budget 40000 Fix the failing tests and verify the public API stays compatible
|
|
447
|
+
/goal
|
|
448
|
+
/goal pause
|
|
449
|
+
/goal resume
|
|
450
|
+
/goal clear
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Codex owns the persisted goal and automatic continuations; remote-cli keeps the execution card, approvals, and busy-state reservation across native turns. Status, pause, clear, and `/abort` remain available during work. Pause and clear stop current goal work; `/abort` pauses the goal, while clear removes the goal without clearing the conversation. After process loss, inspect with `/goal` and explicitly recover with `/goal resume`; status queries do not load or start the goal. Send attachments in an ordinary message before starting a goal. Use a Codex build exposing the native thread-goal APIs (verified with 0.163.0); unavailable APIs or disabled goals report an error. Other backends reject `/goal`. See [the native goal API](https://learn.chatgpt.com/docs/app-server#manage-a-thread-goal).
|
|
454
|
+
|
|
434
455
|
With a CLI and Router that support queue-start notifications, confirming a message keeps a static queue receipt. When the task actually starts, a new execution card appears at the bottom of the chat with its thread, working directory, task preview, and remaining queue count. Progress and results stay on that new card, and the old receipt points to it. Older clients retain the waiting-card behavior. Queued work remains in memory; this does not make queues persistent across service restarts.
|
|
435
456
|
|
|
436
457
|
When a thread is busy, ordinary messages require confirmation before they enter its queue. Use `/queue` to inspect pending work, `/queue clear` to discard queued messages, and `/abort` to stop the active task and clear that thread's queue. A message received during abort cleanup waits and starts after the backend is safe to reuse. A backend failure in the active task still advances to the first confirmed queued message; a failure in a task taken from the queue pauses the remaining queue. Context-changing commands such as `/model`, `/effort`, `/cd`, `/compact`, `/clear`, and its `/new` alias are handled separately rather than queued as ordinary messages. Feishu thread buttons show automatic names such as `thread-2` as their sequence number, such as `2`; routing continues to use the full internal name. Continuation cards repeat the thread and working-directory header of the original card.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExecutorGoal, ExecutorGoalRunRequest } from '../executor/IExecutor';
|
|
2
|
+
export declare const GOAL_USAGE: string;
|
|
3
|
+
export type GoalCommand = {
|
|
4
|
+
kind: 'show' | 'pause' | 'clear' | 'help';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'run';
|
|
7
|
+
request: ExecutorGoalRunRequest;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'invalid';
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/** Parse before approval/input routing so control commands never answer a tool prompt. */
|
|
13
|
+
export declare function parseGoalCommand(content: string): GoalCommand | undefined;
|
|
14
|
+
export declare function formatGoal(goal: ExecutorGoal | null): string;
|
|
15
|
+
//# sourceMappingURL=GoalCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoalCommand.d.ts","sourceRoot":"","sources":["../../src/client/GoalCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAElF,eAAO,MAAM,UAAU,QAMX,CAAC;AAEb,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CA6BzE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,MAAM,CAU5D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GOAL_USAGE = void 0;
|
|
4
|
+
exports.parseGoalCommand = parseGoalCommand;
|
|
5
|
+
exports.formatGoal = formatGoal;
|
|
6
|
+
exports.GOAL_USAGE = [
|
|
7
|
+
'/goal - Show the current Codex goal',
|
|
8
|
+
'/goal [--budget <tokens>] <objective> - Start a native goal',
|
|
9
|
+
'/goal pause - Pause the goal and stop its current work',
|
|
10
|
+
'/goal resume [--budget <tokens>] - Resume the existing goal',
|
|
11
|
+
'/goal clear - Remove the goal and stop its current work',
|
|
12
|
+
].join('\n');
|
|
13
|
+
/** Parse before approval/input routing so control commands never answer a tool prompt. */
|
|
14
|
+
function parseGoalCommand(content) {
|
|
15
|
+
const match = /^\/goal(?:\s+([\s\S]*))?$/.exec(content.trim());
|
|
16
|
+
if (!match)
|
|
17
|
+
return undefined;
|
|
18
|
+
const argument = (match[1] ?? '').trim();
|
|
19
|
+
if (!argument || argument === 'status')
|
|
20
|
+
return { kind: 'show' };
|
|
21
|
+
if (argument === 'pause' || argument === 'clear' || argument === 'help')
|
|
22
|
+
return { kind: argument };
|
|
23
|
+
const resume = /^resume(?:\s|$)/.test(argument);
|
|
24
|
+
let objective = resume ? argument.slice('resume'.length).trim() : argument;
|
|
25
|
+
let tokenBudget;
|
|
26
|
+
if (/^--budget(?:\s|$)/.test(objective)) {
|
|
27
|
+
const budget = /^--budget\s+([0-9]+)(?:\s+([\s\S]*))?$/.exec(objective);
|
|
28
|
+
if (!budget || !Number.isSafeInteger(Number(budget[1])) || Number(budget[1]) <= 0) {
|
|
29
|
+
return { kind: 'invalid', error: 'Goal token budget must be a positive safe integer.\n' + exports.GOAL_USAGE };
|
|
30
|
+
}
|
|
31
|
+
tokenBudget = Number(budget[1]);
|
|
32
|
+
objective = (budget[2] ?? '').trim();
|
|
33
|
+
}
|
|
34
|
+
if (resume) {
|
|
35
|
+
if (objective)
|
|
36
|
+
return { kind: 'invalid', error: 'Resume does not accept a new objective.\n' + exports.GOAL_USAGE };
|
|
37
|
+
return { kind: 'run', request: tokenBudget === undefined ? {} : { tokenBudget } };
|
|
38
|
+
}
|
|
39
|
+
if (!objective || objective.startsWith('--') || /^(?:pause|clear|status|help)(?:\s|$)/.test(objective)) {
|
|
40
|
+
return { kind: 'invalid', error: 'Invalid goal command.\n' + exports.GOAL_USAGE };
|
|
41
|
+
}
|
|
42
|
+
if (Array.from(objective).length > 4000) {
|
|
43
|
+
return { kind: 'invalid', error: 'Goal objectives must contain at most 4,000 characters.' };
|
|
44
|
+
}
|
|
45
|
+
return { kind: 'run', request: { objective, ...(tokenBudget === undefined ? {} : { tokenBudget }) } };
|
|
46
|
+
}
|
|
47
|
+
function formatGoal(goal) {
|
|
48
|
+
if (!goal)
|
|
49
|
+
return 'No Codex goal is set for this thread.\n\n' + exports.GOAL_USAGE;
|
|
50
|
+
return [
|
|
51
|
+
'Codex goal',
|
|
52
|
+
`Objective: ${goal.objective}`,
|
|
53
|
+
`Status: ${goal.status}`,
|
|
54
|
+
`Tokens used: ${goal.tokensUsed}${goal.tokenBudget === null ? ' (no explicit budget)' : ` / ${goal.tokenBudget}`}`,
|
|
55
|
+
`Active time: ${goal.timeUsedSeconds}s`,
|
|
56
|
+
'', exports.GOAL_USAGE,
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=GoalCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoalCommand.js","sourceRoot":"","sources":["../../src/client/GoalCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,7 @@ export declare class MessageHandler {
|
|
|
34
34
|
private readonly sessionControl;
|
|
35
35
|
private readonly activeThreadOperations;
|
|
36
36
|
private readonly abortOperations;
|
|
37
|
+
private readonly pendingGoalActivations;
|
|
37
38
|
private automaticUpdateInProgress;
|
|
38
39
|
private approvalCardsSupported;
|
|
39
40
|
private streamingContextSupported;
|
|
@@ -53,6 +54,8 @@ export declare class MessageHandler {
|
|
|
53
54
|
private readonly delegation;
|
|
54
55
|
private readonly delegationBridges;
|
|
55
56
|
private fileInbox?;
|
|
57
|
+
private readonly imageDeliveries;
|
|
58
|
+
private imageConnectionEpoch;
|
|
56
59
|
constructor(wsClient: WebSocketClient, threadPool: ThreadExecutorPool, threadManager: ThreadManager, directoryGuard: DirectoryGuard, config: ConfigManager);
|
|
57
60
|
reconcilePendingDelegatedWorkers(): Promise<void>;
|
|
58
61
|
/**
|
|
@@ -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;AAQpD,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;
|
|
1
|
+
{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;
|
|
@@ -186,6 +189,8 @@ class MessageHandler {
|
|
|
186
189
|
delegation;
|
|
187
190
|
delegationBridges = new Map();
|
|
188
191
|
fileInbox;
|
|
192
|
+
imageDeliveries = new Map();
|
|
193
|
+
imageConnectionEpoch = 0;
|
|
189
194
|
constructor(wsClient, threadPool, threadManager, directoryGuard, config) {
|
|
190
195
|
this.wsClient = wsClient;
|
|
191
196
|
this.threadPool = threadPool;
|
|
@@ -193,8 +198,11 @@ class MessageHandler {
|
|
|
193
198
|
this.directoryGuard = directoryGuard;
|
|
194
199
|
this.config = config;
|
|
195
200
|
this.wsClient.onClose?.(() => {
|
|
201
|
+
this.imageConnectionEpoch++;
|
|
196
202
|
this.routerVersion = undefined;
|
|
197
203
|
this.fileInbox?.disconnect();
|
|
204
|
+
for (const delivery of this.imageDeliveries.values())
|
|
205
|
+
delivery.invalidatePending();
|
|
198
206
|
});
|
|
199
207
|
this.delegation = new DelegationManager_1.DelegationManager(directoryGuard);
|
|
200
208
|
this.settingsCatalogReader = new SettingsCatalog_1.SettingsCatalogReader(directoryGuard);
|
|
@@ -525,6 +533,53 @@ class MessageHandler {
|
|
|
525
533
|
}
|
|
526
534
|
const executor = this.threadPool.getExecutor(resolvedThreadId);
|
|
527
535
|
this.sendStreamContext(messageId, resolvedThreadId, executor);
|
|
536
|
+
const goalCommand = (0, GoalCommand_1.parseGoalCommand)(settingsCommand);
|
|
537
|
+
let goalRequest;
|
|
538
|
+
if (goalCommand) {
|
|
539
|
+
if (goalCommand.kind === 'invalid' || goalCommand.kind === 'help') {
|
|
540
|
+
this.sendResponse(messageId, resolvedThreadId, goalCommand.kind === 'invalid'
|
|
541
|
+
? { success: false, error: goalCommand.error } : { success: true, output: GoalCommand_1.GOAL_USAGE });
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (this.threadPool.getBackendKey(resolvedThreadId) !== 'codex'
|
|
545
|
+
|| !executor.getGoal || !executor.executeGoal || !executor.pauseGoal || !executor.clearGoal) {
|
|
546
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
547
|
+
error: '/goal requires the Codex app-server backend and an executor version with native goal support.' });
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (attachments?.length || message.fileIds?.length) {
|
|
551
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
552
|
+
error: 'Send attachments in a normal message before starting a goal. No goal was changed.' });
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (goalCommand.kind !== 'run') {
|
|
556
|
+
try {
|
|
557
|
+
const pending = this.pendingGoalActivations.get(resolvedThreadId);
|
|
558
|
+
if (pending && goalCommand.kind !== 'show')
|
|
559
|
+
pending.cancelled = true;
|
|
560
|
+
const result = goalCommand.kind === 'show' ? { success: true, output: (0, GoalCommand_1.formatGoal)(await executor.getGoal()) }
|
|
561
|
+
: goalCommand.kind === 'pause' ? await executor.pauseGoal() : await executor.clearGoal();
|
|
562
|
+
this.sendResponse(messageId, resolvedThreadId, { ...result, threads: this.threadPool.getSummaries() });
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
566
|
+
error: `Goal control failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
567
|
+
}
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if ((0, DelegationSettings_1.isDelegationEnabled)(thread) || this.delegation.hasActiveTasks(resolvedThreadId)) {
|
|
571
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
572
|
+
error: 'Native goals require managed delegation to be off. Wait for workers to finish and send /delegation off in this thread, then retry /goal.' });
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (this.threadPool.isThreadBusy(resolvedThreadId) || executor.isBusy?.()
|
|
576
|
+
|| this.hasThreadQueueState(resolvedThreadId) || this.abortOperations.has(resolvedThreadId)) {
|
|
577
|
+
this.sendResponse(messageId, resolvedThreadId, { success: false,
|
|
578
|
+
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.' });
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
goalRequest = goalCommand.request;
|
|
582
|
+
}
|
|
528
583
|
// Handle /abort for this specific thread (bypasses busy check)
|
|
529
584
|
if (content?.trim() === '/abort') {
|
|
530
585
|
await this.handleAbortCommand(messageId, resolvedThreadId, executor);
|
|
@@ -594,7 +649,7 @@ class MessageHandler {
|
|
|
594
649
|
return;
|
|
595
650
|
}
|
|
596
651
|
const inputExecutor = this.delegation.waitingExecutor(resolvedThreadId) ?? executor;
|
|
597
|
-
if ('isWaitingInput' in inputExecutor && typeof inputExecutor.isWaitingInput === 'function') {
|
|
652
|
+
if (!goalRequest && 'isWaitingInput' in inputExecutor && typeof inputExecutor.isWaitingInput === 'function') {
|
|
598
653
|
const ex = inputExecutor;
|
|
599
654
|
if (ex.isWaitingInput()) {
|
|
600
655
|
const input = content?.trim();
|
|
@@ -682,6 +737,9 @@ class MessageHandler {
|
|
|
682
737
|
});
|
|
683
738
|
return;
|
|
684
739
|
}
|
|
740
|
+
const goalActivation = goalRequest ? { cancelled: false } : undefined;
|
|
741
|
+
if (goalActivation)
|
|
742
|
+
this.pendingGoalActivations.set(resolvedThreadId, goalActivation);
|
|
685
743
|
const operationToken = this.beginThreadOperation(resolvedThreadId);
|
|
686
744
|
try {
|
|
687
745
|
this.trackTask(messageId, resolvedThreadId, content || '');
|
|
@@ -716,6 +774,10 @@ class MessageHandler {
|
|
|
716
774
|
await this.executePendingReplace(messageId, resolvedThreadId, pendingKey, content);
|
|
717
775
|
return;
|
|
718
776
|
}
|
|
777
|
+
if (goalRequest) {
|
|
778
|
+
await this.executeCommand(messageId, resolvedThreadId, content, executor, undefined, false, goalRequest, goalActivation);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
719
781
|
const builtInResult = await this.handleBuiltInCommand(messageId, resolvedThreadId, content, executor);
|
|
720
782
|
if (builtInResult)
|
|
721
783
|
return;
|
|
@@ -736,6 +798,9 @@ class MessageHandler {
|
|
|
736
798
|
});
|
|
737
799
|
}
|
|
738
800
|
finally {
|
|
801
|
+
if (goalActivation && this.pendingGoalActivations.get(resolvedThreadId) === goalActivation) {
|
|
802
|
+
this.pendingGoalActivations.delete(resolvedThreadId);
|
|
803
|
+
}
|
|
739
804
|
if (this.finishThreadOperation(resolvedThreadId, operationToken) && !this.pausedQueues.has(resolvedThreadId)) {
|
|
740
805
|
void this.startNextQueuedCommand(resolvedThreadId);
|
|
741
806
|
}
|
|
@@ -746,9 +811,20 @@ class MessageHandler {
|
|
|
746
811
|
*/
|
|
747
812
|
async handleAbortCommand(messageId, threadId, executor) {
|
|
748
813
|
this.fileInbox?.cancelThread(threadId);
|
|
814
|
+
this.imageDeliveries.get(threadId)?.cancel();
|
|
815
|
+
const pendingGoal = this.pendingGoalActivations.get(threadId);
|
|
816
|
+
if (pendingGoal)
|
|
817
|
+
pendingGoal.cancelled = true;
|
|
749
818
|
const existingAbort = this.abortOperations.get(threadId);
|
|
750
819
|
if (existingAbort) {
|
|
751
820
|
await existingAbort;
|
|
821
|
+
if (executor.isGoalRunning?.()) {
|
|
822
|
+
this.sendResponse(messageId, threadId, {
|
|
823
|
+
success: false,
|
|
824
|
+
error: 'Native goal stop could not be confirmed. This thread remains busy; retry /goal pause or /abort.',
|
|
825
|
+
});
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
752
828
|
this.sendResponse(messageId, threadId, {
|
|
753
829
|
success: true,
|
|
754
830
|
output: 'ℹ️ The abort operation has already completed.',
|
|
@@ -763,6 +839,9 @@ class MessageHandler {
|
|
|
763
839
|
if ((0, DelegationSettings_1.isDelegationEnabled)(this.threadManager.getThread(threadId)))
|
|
764
840
|
await this.delegation.cancelThread(threadId);
|
|
765
841
|
aborted = await executor.abort();
|
|
842
|
+
if (!aborted && executor.isGoalRunning?.()) {
|
|
843
|
+
throw new Error('Native goal stop could not be confirmed. This thread remains busy; retry /goal pause or /abort.');
|
|
844
|
+
}
|
|
766
845
|
if (activeOperation)
|
|
767
846
|
await activeOperation.done;
|
|
768
847
|
})();
|
|
@@ -1051,6 +1130,7 @@ Use /compact to reduce conversation context or /clear to start a fresh context.`
|
|
|
1051
1130
|
- /cd <directory> - Change working directory for this thread
|
|
1052
1131
|
- /model [name] - Open model settings, or set the active backend model with a text argument
|
|
1053
1132
|
- /effort [auto|level] - Open effort settings, or set a native supported level with a text argument
|
|
1133
|
+
- /goal [objective|pause|resume|clear] - Manage a native Codex goal; optional --budget <tokens> on start or resume
|
|
1054
1134
|
- /sandbox [on|off|read-only|default|allow <directory>|remove <directory>|network on|off] - Configure this thread's sandbox (Codex, Claude Code)
|
|
1055
1135
|
- /delegation [on|off|reset [backend]] - Delegate tasks between installed agent backends; reset saved worker context when needed
|
|
1056
1136
|
- /backend - Open backend settings (Current thread by default; Confirm applies)
|
|
@@ -2010,12 +2090,13 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2010
2090
|
/**
|
|
2011
2091
|
* Execute AI command on a specific executor
|
|
2012
2092
|
*/
|
|
2013
|
-
async executeCommand(messageId, threadId, content, executor, attachments, fromQueue = false) {
|
|
2093
|
+
async executeCommand(messageId, threadId, content, executor, attachments, fromQueue = false, goalRequest, goalActivation) {
|
|
2014
2094
|
this.sendStreamContext(messageId, threadId, executor);
|
|
2015
2095
|
let delegationScope;
|
|
2016
2096
|
let delegationBridge;
|
|
2017
2097
|
let executionMetadata;
|
|
2018
2098
|
let requestFinished = false;
|
|
2099
|
+
let imageDelivery;
|
|
2019
2100
|
let executionActive = false;
|
|
2020
2101
|
let lastProgramState;
|
|
2021
2102
|
const activity = new ActivityProgress_1.ActivityProgress(snapshot => {
|
|
@@ -2040,6 +2121,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2040
2121
|
lastProgramState = text;
|
|
2041
2122
|
};
|
|
2042
2123
|
const complete = async (success, error) => {
|
|
2124
|
+
await imageDelivery?.finish();
|
|
2125
|
+
if (this.imageDeliveries.get(threadId) === imageDelivery)
|
|
2126
|
+
this.imageDeliveries.delete(threadId);
|
|
2043
2127
|
requestFinished = true;
|
|
2044
2128
|
activity.dispose();
|
|
2045
2129
|
delegationBridge?.activate(undefined);
|
|
@@ -2079,6 +2163,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2079
2163
|
}
|
|
2080
2164
|
const openId = this.getMessageOpenId(messageId);
|
|
2081
2165
|
const thread = this.threadManager.getThread(threadId);
|
|
2166
|
+
if (goalRequest && thread && (0, DelegationSettings_1.isDelegationEnabled)(thread)) {
|
|
2167
|
+
throw new Error('Turn managed delegation off before starting a native goal.');
|
|
2168
|
+
}
|
|
2082
2169
|
if (thread && (0, DelegationSettings_1.isDelegationEnabled)(thread) && executor.configureDelegation) {
|
|
2083
2170
|
if (this.delegation.blocksWorkspace(executor.getCurrentWorkingDirectory(), threadId)) {
|
|
2084
2171
|
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 +2220,41 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2133
2220
|
return;
|
|
2134
2221
|
this.notificationAdapter.sendTaskNotification({ ...notification, threadId }, openId);
|
|
2135
2222
|
};
|
|
2136
|
-
const
|
|
2137
|
-
const
|
|
2223
|
+
const imageCwd = thread?.workingDirectory ?? executor.getCurrentWorkingDirectory();
|
|
2224
|
+
const imageGeneration = thread?.delegationWorkspaceGeneration ?? 0;
|
|
2225
|
+
const imageBackend = this.threadPool.getBackendKey(threadId);
|
|
2226
|
+
let imagePhase = 0;
|
|
2227
|
+
const imageEligible = () => {
|
|
2228
|
+
const phase = imagePhase;
|
|
2229
|
+
return () => phase === imagePhase && (!delegationScope
|
|
2230
|
+
|| (!delegationScope.isClosed() && !delegationScope.hasPendingResults()));
|
|
2231
|
+
};
|
|
2232
|
+
imageDelivery = new ImageDelivery_1.ImageDelivery({
|
|
2233
|
+
readFile: filePath => (0, LocalImageDetector_1.readLocalImagePath)(filePath, imageCwd, this.directoryGuard),
|
|
2234
|
+
publish: image => {
|
|
2235
|
+
if (!this.sendImage(messageId, threadId, image))
|
|
2236
|
+
throw new Error('Image send failed');
|
|
2237
|
+
},
|
|
2238
|
+
notice: text => this.sendStreamChunk(messageId, threadId, text),
|
|
2239
|
+
isCurrent: () => {
|
|
2240
|
+
const current = this.threadManager.getThread(threadId);
|
|
2241
|
+
return !requestFinished && !this.isDestroyed && !this.abortOperations.has(threadId)
|
|
2242
|
+
&& this.wsClient.isConnected() && this.imageDeliveries.get(threadId) === imageDelivery
|
|
2243
|
+
&& this.wsClient.canSendTaskOutput?.(messageId) !== false
|
|
2244
|
+
&& !!current && current.workingDirectory === imageCwd
|
|
2245
|
+
&& (current.delegationWorkspaceGeneration ?? 0) === imageGeneration
|
|
2246
|
+
&& this.threadPool.getBackendKey(threadId) === imageBackend;
|
|
2247
|
+
},
|
|
2248
|
+
});
|
|
2249
|
+
this.imageDeliveries.get(threadId)?.cancel();
|
|
2250
|
+
this.imageDeliveries.set(threadId, imageDelivery);
|
|
2138
2251
|
const delegationControlTools = new Set();
|
|
2139
2252
|
const toolImageStates = new Map();
|
|
2140
2253
|
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
|
-
};
|
|
2254
|
+
let streamedAny = false;
|
|
2255
|
+
let imageTextDropped = false;
|
|
2164
2256
|
const queueLocalImages = (text) => {
|
|
2165
|
-
|
|
2166
|
-
pendingLocalImageEmissions.add(pending);
|
|
2257
|
+
imageDelivery?.offerText(text, imageEligible());
|
|
2167
2258
|
};
|
|
2168
2259
|
const executeOptions = {
|
|
2169
2260
|
onActivity: this.activityProgressSupported ? (snapshot) => activity.offer(snapshot) : undefined,
|
|
@@ -2174,8 +2265,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2174
2265
|
onApprovalResolved: (requestId, status) => this.resolveApprovalCard(requestId, status),
|
|
2175
2266
|
onTaskNotification,
|
|
2176
2267
|
onStream: (chunk) => {
|
|
2177
|
-
|
|
2178
|
-
this.sendStreamChunk(messageId, threadId, chunk)
|
|
2268
|
+
streamedAny = true;
|
|
2269
|
+
if (this.sendStreamChunk(messageId, threadId, chunk))
|
|
2270
|
+
imageDelivery?.offerStream(chunk, imageEligible());
|
|
2271
|
+
else
|
|
2272
|
+
imageTextDropped = true;
|
|
2179
2273
|
},
|
|
2180
2274
|
onToolUse: (toolUse) => {
|
|
2181
2275
|
if (delegationScope && /remote_cli_(list_backends|delegate|result|cancel|integrate)$/.test(toolUse.name)) {
|
|
@@ -2213,7 +2307,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2213
2307
|
},
|
|
2214
2308
|
onRedactedThinking: () => this.sendRedactedThinking(messageId, threadId),
|
|
2215
2309
|
onPlanMode: (planContent) => this.sendPlanMode(messageId, threadId, planContent),
|
|
2216
|
-
onImage: (image) =>
|
|
2310
|
+
onImage: (image) => imageDelivery?.offerImage(image, imageEligible()),
|
|
2311
|
+
onImageFile: (filePath) => imageDelivery?.offerFile(filePath, imageEligible()),
|
|
2217
2312
|
attachments,
|
|
2218
2313
|
};
|
|
2219
2314
|
const assertCoordinatorActive = () => {
|
|
@@ -2225,6 +2320,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2225
2320
|
let mayRetry = true;
|
|
2226
2321
|
const executeOnce = async (prompt, options, flushImages = true) => {
|
|
2227
2322
|
assertCoordinatorActive();
|
|
2323
|
+
imagePhase++;
|
|
2324
|
+
streamedAny = false;
|
|
2325
|
+
imageTextDropped = false;
|
|
2326
|
+
imageDelivery?.resetStream();
|
|
2327
|
+
const imageEpoch = this.imageConnectionEpoch;
|
|
2228
2328
|
const scope = delegationScope;
|
|
2229
2329
|
let active = true;
|
|
2230
2330
|
let finished = false;
|
|
@@ -2280,6 +2380,10 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2280
2380
|
options.onImage?.(image);
|
|
2281
2381
|
else
|
|
2282
2382
|
suppressed = true; },
|
|
2383
|
+
onImageFile: filePath => { if (canPublish())
|
|
2384
|
+
options.onImageFile?.(filePath);
|
|
2385
|
+
else
|
|
2386
|
+
suppressed = true; },
|
|
2283
2387
|
onRedactedThinking: () => { if (canPublish())
|
|
2284
2388
|
options.onRedactedThinking?.(); },
|
|
2285
2389
|
onToolUse: tool => { if (active && !scope?.isClosed())
|
|
@@ -2292,7 +2396,19 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2292
2396
|
executionMetadata = launchMetadata;
|
|
2293
2397
|
let capturedMetadata = false;
|
|
2294
2398
|
try {
|
|
2295
|
-
|
|
2399
|
+
if (goalRequest && (goalActivation?.cancelled || this.isDestroyed || this.abortOperations.has(threadId))) {
|
|
2400
|
+
throw new Error('Native goal activation was cancelled before it started.');
|
|
2401
|
+
}
|
|
2402
|
+
// No await between releasing preparation ownership and the adapter's
|
|
2403
|
+
// synchronous installation of native goal ownership.
|
|
2404
|
+
if (goalActivation && this.pendingGoalActivations.get(threadId) === goalActivation) {
|
|
2405
|
+
this.pendingGoalActivations.delete(threadId);
|
|
2406
|
+
}
|
|
2407
|
+
const result = goalRequest
|
|
2408
|
+
? await executor.executeGoal(goalRequest, guardedOptions)
|
|
2409
|
+
: await executor.execute(prompt, guardedOptions);
|
|
2410
|
+
if (goalRequest && !streamedAny && result.output)
|
|
2411
|
+
guardedOptions.onStream?.(result.output);
|
|
2296
2412
|
executionMetadata = (0, ExecutionMetadata_1.mergeReportedExecutionMetadata)(launchMetadata, (0, ExecutionMetadata_1.captureExecutionMetadata)(executor, backend));
|
|
2297
2413
|
capturedMetadata = true;
|
|
2298
2414
|
active = false;
|
|
@@ -2303,9 +2419,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2303
2419
|
scope?.finishExecution(result.success);
|
|
2304
2420
|
finished = true;
|
|
2305
2421
|
if (flushImages) {
|
|
2306
|
-
|
|
2307
|
-
const visibleResult =
|
|
2308
|
-
|
|
2422
|
+
imageDelivery?.flushStream();
|
|
2423
|
+
const visibleResult = imageTextDropped || this.imageConnectionEpoch !== imageEpoch
|
|
2424
|
+
|| (scope && (suppressed || scope.hasPendingResults())) ? '' : result.output ?? '';
|
|
2425
|
+
queueLocalImages(visibleResult);
|
|
2426
|
+
await imageDelivery?.drain();
|
|
2309
2427
|
}
|
|
2310
2428
|
assertCoordinatorActive();
|
|
2311
2429
|
return result;
|
|
@@ -2319,7 +2437,8 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2319
2437
|
delegationBridge?.activate(undefined);
|
|
2320
2438
|
if (!finished)
|
|
2321
2439
|
scope?.finishExecution(false);
|
|
2322
|
-
|
|
2440
|
+
// Goal activation mutates durable native state; never replay it as a model retry.
|
|
2441
|
+
mayRetry = !goalRequest && (!scope || scope.getLaunchRevision() === launchRevision);
|
|
2323
2442
|
}
|
|
2324
2443
|
};
|
|
2325
2444
|
let prompt = content;
|
|
@@ -2514,7 +2633,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2514
2633
|
}
|
|
2515
2634
|
sendStreamChunk(messageId, threadId, chunk) {
|
|
2516
2635
|
try {
|
|
2517
|
-
this.wsClient.send({
|
|
2636
|
+
return this.wsClient.send({
|
|
2518
2637
|
type: 'stream',
|
|
2519
2638
|
messageId,
|
|
2520
2639
|
chunk,
|
|
@@ -2522,10 +2641,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2522
2641
|
openId: this.getMessageOpenId(messageId),
|
|
2523
2642
|
threadId,
|
|
2524
2643
|
timestamp: Date.now(),
|
|
2525
|
-
});
|
|
2644
|
+
}) !== false;
|
|
2526
2645
|
}
|
|
2527
2646
|
catch (error) {
|
|
2528
2647
|
console.error('Failed to send stream chunk:', error);
|
|
2648
|
+
return false;
|
|
2529
2649
|
}
|
|
2530
2650
|
}
|
|
2531
2651
|
sendToolUse(messageId, threadId, toolUse) {
|
|
@@ -2624,7 +2744,7 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2624
2744
|
}
|
|
2625
2745
|
sendImage(messageId, threadId, image) {
|
|
2626
2746
|
try {
|
|
2627
|
-
this.wsClient.send({
|
|
2747
|
+
return this.wsClient.send({
|
|
2628
2748
|
type: 'stream',
|
|
2629
2749
|
messageId,
|
|
2630
2750
|
streamType: 'image',
|
|
@@ -2632,10 +2752,11 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2632
2752
|
openId: this.getMessageOpenId(messageId),
|
|
2633
2753
|
threadId,
|
|
2634
2754
|
timestamp: Date.now(),
|
|
2635
|
-
});
|
|
2755
|
+
}) !== false;
|
|
2636
2756
|
}
|
|
2637
|
-
catch
|
|
2638
|
-
console.
|
|
2757
|
+
catch {
|
|
2758
|
+
console.warn('Failed to send generated image.');
|
|
2759
|
+
return false;
|
|
2639
2760
|
}
|
|
2640
2761
|
}
|
|
2641
2762
|
sendStructuredContent(messageId, threadId, structuredContent) {
|
|
@@ -2997,6 +3118,9 @@ You can also use natural language commands to control Claude Code CLI.`,
|
|
|
2997
3118
|
this.directoryBrowser.destroy();
|
|
2998
3119
|
this.settingsService.destroy();
|
|
2999
3120
|
this.isDestroyed = true;
|
|
3121
|
+
for (const delivery of this.imageDeliveries.values())
|
|
3122
|
+
delivery.cancel();
|
|
3123
|
+
this.imageDeliveries.clear();
|
|
3000
3124
|
await this.fileInbox?.destroy();
|
|
3001
3125
|
this.notificationAdapter.unregister();
|
|
3002
3126
|
try {
|