wave-code 0.17.0 → 0.17.2
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/dist/acp/agent.d.ts +1 -0
- package/dist/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +116 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +5 -2
- package/dist/components/InputBox.d.ts +2 -0
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +29 -8
- package/dist/components/LoadingIndicator.d.ts +2 -1
- package/dist/components/LoadingIndicator.d.ts.map +1 -1
- package/dist/components/LoadingIndicator.js +2 -2
- package/dist/components/QueuedMessageList.d.ts.map +1 -1
- package/dist/components/QueuedMessageList.js +10 -10
- package/dist/components/StatusLine.d.ts +2 -0
- package/dist/components/StatusLine.d.ts.map +1 -1
- package/dist/components/StatusLine.js +6 -6
- package/dist/components/TaskList.d.ts +3 -0
- package/dist/components/TaskList.d.ts.map +1 -1
- package/dist/components/TaskList.js +171 -28
- package/dist/components/TaskNotificationMessage.d.ts.map +1 -1
- package/dist/components/TaskNotificationMessage.js +1 -0
- package/dist/components/WorkflowManager.d.ts +6 -0
- package/dist/components/WorkflowManager.d.ts.map +1 -0
- package/dist/components/WorkflowManager.js +102 -0
- package/dist/constants/commands.d.ts.map +1 -1
- package/dist/constants/commands.js +6 -0
- package/dist/contexts/useChat.d.ts +8 -1
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +68 -4
- package/dist/hooks/useInputManager.d.ts +2 -0
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +22 -13
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +6 -3
- package/dist/managers/inputReducer.d.ts +10 -0
- package/dist/managers/inputReducer.d.ts.map +1 -1
- package/dist/managers/inputReducer.js +41 -4
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +3 -31
- package/dist/reducers/workflowManagerReducer.d.ts +41 -0
- package/dist/reducers/workflowManagerReducer.d.ts.map +1 -0
- package/dist/reducers/workflowManagerReducer.js +100 -0
- package/package.json +2 -2
- package/src/acp/agent.ts +137 -1
- package/src/components/ChatInterface.tsx +10 -1
- package/src/components/InputBox.tsx +43 -3
- package/src/components/LoadingIndicator.tsx +4 -1
- package/src/components/QueuedMessageList.tsx +7 -3
- package/src/components/StatusLine.tsx +28 -16
- package/src/components/TaskList.tsx +213 -19
- package/src/components/TaskNotificationMessage.tsx +1 -0
- package/src/components/WorkflowManager.tsx +320 -0
- package/src/constants/commands.ts +6 -0
- package/src/contexts/useChat.tsx +83 -4
- package/src/hooks/useInputManager.ts +25 -13
- package/src/managers/inputHandlers.ts +5 -4
- package/src/managers/inputReducer.ts +54 -6
- package/src/print-cli.ts +3 -37
- package/src/reducers/workflowManagerReducer.ts +143 -0
package/src/contexts/useChat.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
PermissionDecision,
|
|
19
19
|
PermissionMode,
|
|
20
20
|
QueuedMessage,
|
|
21
|
+
WorkflowRun,
|
|
21
22
|
} from "wave-agent-sdk";
|
|
22
23
|
import {
|
|
23
24
|
Agent,
|
|
@@ -53,6 +54,8 @@ export interface ChatContextType {
|
|
|
53
54
|
) => Promise<void>;
|
|
54
55
|
askBtw: (question: string) => Promise<string>;
|
|
55
56
|
abortMessage: () => void;
|
|
57
|
+
recallQueuedMessage: () => QueuedMessage | null;
|
|
58
|
+
removeQueuedMessageById: (id: string) => boolean;
|
|
56
59
|
latestTotalTokens: number;
|
|
57
60
|
maxInputTokens: number;
|
|
58
61
|
// Model functionality
|
|
@@ -66,6 +69,9 @@ export interface ChatContextType {
|
|
|
66
69
|
disconnectMcpServer: (serverName: string) => Promise<boolean>;
|
|
67
70
|
// Background tasks
|
|
68
71
|
backgroundTasks: BackgroundTask[];
|
|
72
|
+
// Workflow runs
|
|
73
|
+
workflowRuns: WorkflowRun[];
|
|
74
|
+
stopWorkflowRun: (runId: string) => void;
|
|
69
75
|
// Tasks
|
|
70
76
|
tasks: Task[];
|
|
71
77
|
getBackgroundTaskOutput: (taskId: string) => {
|
|
@@ -122,6 +128,10 @@ export interface ChatContextType {
|
|
|
122
128
|
recreateAgent: () => void;
|
|
123
129
|
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
124
130
|
triggerWorktreeRemoveHook: (worktreePath: string) => Promise<void>;
|
|
131
|
+
// Goal state
|
|
132
|
+
isGoalActive: boolean;
|
|
133
|
+
goalElapsed?: string;
|
|
134
|
+
isGoalEvaluating: boolean;
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -201,6 +211,28 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
201
211
|
const [currentModel, setCurrentModelState] = useState("");
|
|
202
212
|
const [configuredModels, setConfiguredModels] = useState<string[]>([]);
|
|
203
213
|
const [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);
|
|
214
|
+
const [isGoalActive, setIsGoalActive] = useState(false);
|
|
215
|
+
const [goalElapsed, setGoalElapsed] = useState<string | undefined>();
|
|
216
|
+
const [isGoalEvaluating, setIsGoalEvaluating] = useState(false);
|
|
217
|
+
const goalStartedAt = useRef<number | null>(null);
|
|
218
|
+
|
|
219
|
+
// Update goal elapsed time every 30s while active
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
if (!isGoalActive || goalStartedAt.current === null) return;
|
|
222
|
+
const formatElapsed = (ms: number): string => {
|
|
223
|
+
const minutes = Math.floor(ms / 60000);
|
|
224
|
+
if (minutes < 1) return "<1m";
|
|
225
|
+
if (minutes < 60) return `${minutes}m`;
|
|
226
|
+
const hours = Math.floor(minutes / 60);
|
|
227
|
+
const remainingMin = minutes % 60;
|
|
228
|
+
return `${hours}h${remainingMin}m`;
|
|
229
|
+
};
|
|
230
|
+
const update = () =>
|
|
231
|
+
setGoalElapsed(formatElapsed(Date.now() - goalStartedAt.current!));
|
|
232
|
+
update();
|
|
233
|
+
const timer = setInterval(update, 30_000);
|
|
234
|
+
return () => clearInterval(timer);
|
|
235
|
+
}, [isGoalActive]);
|
|
204
236
|
|
|
205
237
|
// MCP State
|
|
206
238
|
const [mcpServerStatuses, setMcpServerStatuses] = useState<McpServerStatus[]>(
|
|
@@ -209,6 +241,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
209
241
|
|
|
210
242
|
// Background tasks state
|
|
211
243
|
const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
|
|
244
|
+
// Workflow runs state
|
|
245
|
+
const [workflowRuns, setWorkflowRuns] = useState<WorkflowRun[]>([]);
|
|
212
246
|
// Tasks state
|
|
213
247
|
const [tasks, setTasks] = useState<Task[]>([]);
|
|
214
248
|
|
|
@@ -344,11 +378,25 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
344
378
|
onCompactionStateChange: (isCompactingState) => {
|
|
345
379
|
setIsCompacting(isCompactingState);
|
|
346
380
|
},
|
|
347
|
-
onBackgroundTasksChange: (tasks) => {
|
|
381
|
+
onBackgroundTasksChange: async (tasks) => {
|
|
348
382
|
setBackgroundTasks([...tasks]);
|
|
383
|
+
// Also refresh workflow runs since workflows are background tasks
|
|
384
|
+
const runs = await agentRef.current?.getWorkflowRuns();
|
|
385
|
+
if (runs) setWorkflowRuns([...runs]);
|
|
349
386
|
},
|
|
350
|
-
onTasksChange: (
|
|
351
|
-
setTasks(
|
|
387
|
+
onTasksChange: (newTasks) => {
|
|
388
|
+
setTasks((prev) => {
|
|
389
|
+
if (
|
|
390
|
+
prev.length === newTasks.length &&
|
|
391
|
+
prev.every(
|
|
392
|
+
(t, i) =>
|
|
393
|
+
t.id === newTasks[i].id && t.status === newTasks[i].status,
|
|
394
|
+
)
|
|
395
|
+
) {
|
|
396
|
+
return prev;
|
|
397
|
+
}
|
|
398
|
+
return [...newTasks];
|
|
399
|
+
});
|
|
352
400
|
},
|
|
353
401
|
onPermissionModeChange: (mode) => {
|
|
354
402
|
setPermissionModeState(mode);
|
|
@@ -368,6 +416,18 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
368
416
|
onQueuedMessagesChange: (messages) => {
|
|
369
417
|
setQueuedMessages([...messages]);
|
|
370
418
|
},
|
|
419
|
+
onGoalStateChange: (active, _condition, elapsed) => {
|
|
420
|
+
setIsGoalActive(active);
|
|
421
|
+
if (active) {
|
|
422
|
+
goalStartedAt.current = Date.now();
|
|
423
|
+
} else {
|
|
424
|
+
goalStartedAt.current = null;
|
|
425
|
+
}
|
|
426
|
+
setGoalElapsed(elapsed);
|
|
427
|
+
},
|
|
428
|
+
onGoalEvaluating: (evaluating) => {
|
|
429
|
+
setIsGoalEvaluating(evaluating);
|
|
430
|
+
},
|
|
371
431
|
};
|
|
372
432
|
|
|
373
433
|
try {
|
|
@@ -424,7 +484,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
424
484
|
setIsCompacting(agent.isCompacting);
|
|
425
485
|
setPermissionModeState(agent.getPermissionMode());
|
|
426
486
|
setWorkingDirectory(agent.workingDirectory);
|
|
427
|
-
setCurrentModelState(agent.getModelConfig().model);
|
|
487
|
+
setCurrentModelState(agent.getModelConfig().model || "");
|
|
428
488
|
setConfiguredModels(agent.getConfiguredModels());
|
|
429
489
|
setMaxInputTokens(agent.getMaxInputTokens());
|
|
430
490
|
|
|
@@ -568,6 +628,14 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
568
628
|
agentRef.current?.abortMessage();
|
|
569
629
|
}, []);
|
|
570
630
|
|
|
631
|
+
const recallQueuedMessage = useCallback((): QueuedMessage | null => {
|
|
632
|
+
return agentRef.current?.recallQueuedMessage() ?? null;
|
|
633
|
+
}, []);
|
|
634
|
+
|
|
635
|
+
const removeQueuedMessageById = useCallback((id: string): boolean => {
|
|
636
|
+
return agentRef.current?.removeQueuedMessageById(id) ?? false;
|
|
637
|
+
}, []);
|
|
638
|
+
|
|
571
639
|
// Permission management methods
|
|
572
640
|
const setPermissionMode = useCallback((mode: PermissionMode) => {
|
|
573
641
|
setPermissionModeState((prev) => {
|
|
@@ -599,6 +667,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
599
667
|
return agentRef.current.stopBackgroundTask(taskId);
|
|
600
668
|
}, []);
|
|
601
669
|
|
|
670
|
+
const stopWorkflowRun = useCallback((runId: string) => {
|
|
671
|
+
agentRef.current?.stopWorkflowRun(runId);
|
|
672
|
+
}, []);
|
|
673
|
+
|
|
602
674
|
const hasSlashCommand = useCallback((commandId: string) => {
|
|
603
675
|
if (!agentRef.current) return false;
|
|
604
676
|
return agentRef.current.hasSlashCommand(commandId);
|
|
@@ -753,6 +825,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
753
825
|
sendMessage,
|
|
754
826
|
askBtw,
|
|
755
827
|
abortMessage,
|
|
828
|
+
recallQueuedMessage,
|
|
829
|
+
removeQueuedMessageById,
|
|
756
830
|
latestTotalTokens,
|
|
757
831
|
maxInputTokens,
|
|
758
832
|
currentModel,
|
|
@@ -764,6 +838,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
764
838
|
connectMcpServer,
|
|
765
839
|
disconnectMcpServer,
|
|
766
840
|
backgroundTasks,
|
|
841
|
+
workflowRuns,
|
|
842
|
+
stopWorkflowRun,
|
|
767
843
|
tasks,
|
|
768
844
|
getBackgroundTaskOutput,
|
|
769
845
|
stopBackgroundTask,
|
|
@@ -791,6 +867,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
791
867
|
workdir,
|
|
792
868
|
recreateAgent,
|
|
793
869
|
triggerWorktreeRemoveHook,
|
|
870
|
+
isGoalActive,
|
|
871
|
+
goalElapsed,
|
|
872
|
+
isGoalEvaluating,
|
|
794
873
|
};
|
|
795
874
|
|
|
796
875
|
return (
|
|
@@ -31,6 +31,7 @@ export const useInputManager = (
|
|
|
31
31
|
onStatusCommandStateChange,
|
|
32
32
|
onPluginManagerStateChange,
|
|
33
33
|
onModelSelectorStateChange,
|
|
34
|
+
onWorkflowManagerStateChange,
|
|
34
35
|
onImagesStateChange,
|
|
35
36
|
onSendMessage,
|
|
36
37
|
onHasSlashCommand,
|
|
@@ -42,18 +43,10 @@ export const useInputManager = (
|
|
|
42
43
|
workdir,
|
|
43
44
|
getFullMessageThread,
|
|
44
45
|
logger,
|
|
46
|
+
hasQueuedMessages: hasQueuedMessagesProp,
|
|
47
|
+
onRecallQueuedMessage,
|
|
45
48
|
} = callbacks;
|
|
46
49
|
|
|
47
|
-
// Handle selectorJustUsed reset
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (state.selectorJustUsed) {
|
|
50
|
-
const timer = setTimeout(() => {
|
|
51
|
-
dispatch({ type: "SET_SELECTOR_JUST_USED", payload: false });
|
|
52
|
-
}, 0);
|
|
53
|
-
return () => clearTimeout(timer);
|
|
54
|
-
}
|
|
55
|
-
}, [state.selectorJustUsed]);
|
|
56
|
-
|
|
57
50
|
// Handle debounced file search
|
|
58
51
|
useEffect(() => {
|
|
59
52
|
if (state.showFileSelector) {
|
|
@@ -217,6 +210,8 @@ export const useInputManager = (
|
|
|
217
210
|
dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
|
|
218
211
|
} else if (command === "model") {
|
|
219
212
|
dispatch({ type: "SET_SHOW_MODEL_SELECTOR", payload: true });
|
|
213
|
+
} else if (command === "workflows") {
|
|
214
|
+
dispatch({ type: "SET_SHOW_WORKFLOW_MANAGER", payload: true });
|
|
220
215
|
} else if (command === "btw") {
|
|
221
216
|
dispatch({
|
|
222
217
|
type: "SET_BTW_STATE",
|
|
@@ -225,6 +220,9 @@ export const useInputManager = (
|
|
|
225
220
|
}
|
|
226
221
|
}
|
|
227
222
|
break;
|
|
223
|
+
case "RECALL_QUEUED_MESSAGE":
|
|
224
|
+
onRecallQueuedMessage?.();
|
|
225
|
+
break;
|
|
228
226
|
}
|
|
229
227
|
} catch (error) {
|
|
230
228
|
console.error("Effect execution error:", error);
|
|
@@ -244,6 +242,7 @@ export const useInputManager = (
|
|
|
244
242
|
getFullMessageThread,
|
|
245
243
|
logger,
|
|
246
244
|
onHasSlashCommand,
|
|
245
|
+
onRecallQueuedMessage,
|
|
247
246
|
]);
|
|
248
247
|
|
|
249
248
|
useEffect(() => {
|
|
@@ -313,6 +312,10 @@ export const useInputManager = (
|
|
|
313
312
|
onModelSelectorStateChange?.(state.showModelSelector);
|
|
314
313
|
}, [state.showModelSelector, onModelSelectorStateChange]);
|
|
315
314
|
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
onWorkflowManagerStateChange?.(state.showWorkflowManager);
|
|
317
|
+
}, [state.showWorkflowManager, onWorkflowManagerStateChange]);
|
|
318
|
+
|
|
316
319
|
useEffect(() => {
|
|
317
320
|
onImagesStateChange?.(state.attachedImages);
|
|
318
321
|
}, [state.attachedImages, onImagesStateChange]);
|
|
@@ -479,6 +482,10 @@ export const useInputManager = (
|
|
|
479
482
|
dispatch({ type: "SET_SHOW_MODEL_SELECTOR", payload: show });
|
|
480
483
|
}, []);
|
|
481
484
|
|
|
485
|
+
const setShowWorkflowManager = useCallback((show: boolean) => {
|
|
486
|
+
dispatch({ type: "SET_SHOW_WORKFLOW_MANAGER", payload: show });
|
|
487
|
+
}, []);
|
|
488
|
+
|
|
482
489
|
const setPermissionMode = useCallback(
|
|
483
490
|
(mode: PermissionMode) => {
|
|
484
491
|
dispatch({ type: "SET_PERMISSION_MODE", payload: mode });
|
|
@@ -518,10 +525,11 @@ export const useInputManager = (
|
|
|
518
525
|
input,
|
|
519
526
|
key: {} as Key,
|
|
520
527
|
hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
|
|
528
|
+
hasQueuedMessages: hasQueuedMessagesProp ?? false,
|
|
521
529
|
},
|
|
522
530
|
});
|
|
523
531
|
},
|
|
524
|
-
[onHasSlashCommand],
|
|
532
|
+
[onHasSlashCommand, hasQueuedMessagesProp],
|
|
525
533
|
);
|
|
526
534
|
|
|
527
535
|
const handleSubmit = useCallback(async () => {
|
|
@@ -531,9 +539,10 @@ export const useInputManager = (
|
|
|
531
539
|
input: "",
|
|
532
540
|
key: { return: true } as Key,
|
|
533
541
|
hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
|
|
542
|
+
hasQueuedMessages: hasQueuedMessagesProp ?? false,
|
|
534
543
|
},
|
|
535
544
|
});
|
|
536
|
-
}, [onHasSlashCommand]);
|
|
545
|
+
}, [onHasSlashCommand, hasQueuedMessagesProp]);
|
|
537
546
|
|
|
538
547
|
const expandLongTextPlaceholders = useCallback(
|
|
539
548
|
(text: string) => {
|
|
@@ -564,11 +573,12 @@ export const useInputManager = (
|
|
|
564
573
|
input,
|
|
565
574
|
key,
|
|
566
575
|
hasSlashCommand: (cmd) => !!onHasSlashCommand?.(cmd),
|
|
576
|
+
hasQueuedMessages: hasQueuedMessagesProp ?? false,
|
|
567
577
|
},
|
|
568
578
|
});
|
|
569
579
|
return true;
|
|
570
580
|
},
|
|
571
|
-
[onHasSlashCommand],
|
|
581
|
+
[onHasSlashCommand, hasQueuedMessagesProp],
|
|
572
582
|
);
|
|
573
583
|
|
|
574
584
|
return {
|
|
@@ -593,6 +603,7 @@ export const useInputManager = (
|
|
|
593
603
|
showLoginCommand: state.showLoginCommand,
|
|
594
604
|
showPluginManager: state.showPluginManager,
|
|
595
605
|
showModelSelector: state.showModelSelector,
|
|
606
|
+
showWorkflowManager: state.showWorkflowManager,
|
|
596
607
|
permissionMode: state.permissionMode,
|
|
597
608
|
attachedImages: state.attachedImages,
|
|
598
609
|
btwState: state.btwState,
|
|
@@ -636,6 +647,7 @@ export const useInputManager = (
|
|
|
636
647
|
setShowLoginCommand,
|
|
637
648
|
setShowPluginManager,
|
|
638
649
|
setShowModelSelector,
|
|
650
|
+
setShowWorkflowManager,
|
|
639
651
|
setPermissionMode,
|
|
640
652
|
setBtwState,
|
|
641
653
|
|
|
@@ -371,6 +371,8 @@ export const handleCommandSelect = (
|
|
|
371
371
|
dispatch({ type: "SET_SHOW_PLUGIN_MANAGER", payload: true });
|
|
372
372
|
} else if (command === "model") {
|
|
373
373
|
dispatch({ type: "SET_SHOW_MODEL_SELECTOR", payload: true });
|
|
374
|
+
} else if (command === "workflows") {
|
|
375
|
+
dispatch({ type: "SET_SHOW_WORKFLOW_MANAGER", payload: true });
|
|
374
376
|
} else if (command === "btw") {
|
|
375
377
|
dispatch({
|
|
376
378
|
type: "SET_BTW_STATE",
|
|
@@ -699,10 +701,6 @@ export const handleInput = async (
|
|
|
699
701
|
key: Key,
|
|
700
702
|
clearImages?: () => void,
|
|
701
703
|
): Promise<boolean> => {
|
|
702
|
-
if (state.selectorJustUsed) {
|
|
703
|
-
return true;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
704
|
if (state.btwState.isActive) {
|
|
707
705
|
if (key.escape) {
|
|
708
706
|
dispatch({
|
|
@@ -756,6 +754,7 @@ export const handleInput = async (
|
|
|
756
754
|
state.showStatusCommand ||
|
|
757
755
|
state.showPluginManager ||
|
|
758
756
|
state.showModelSelector ||
|
|
757
|
+
state.showWorkflowManager ||
|
|
759
758
|
state.btwState.isActive
|
|
760
759
|
)
|
|
761
760
|
) {
|
|
@@ -780,6 +779,7 @@ export const handleInput = async (
|
|
|
780
779
|
state.showStatusCommand ||
|
|
781
780
|
state.showPluginManager ||
|
|
782
781
|
state.showModelSelector ||
|
|
782
|
+
state.showWorkflowManager ||
|
|
783
783
|
state.btwState.isActive
|
|
784
784
|
) {
|
|
785
785
|
if (
|
|
@@ -790,6 +790,7 @@ export const handleInput = async (
|
|
|
790
790
|
state.showStatusCommand ||
|
|
791
791
|
state.showPluginManager ||
|
|
792
792
|
state.showModelSelector ||
|
|
793
|
+
state.showWorkflowManager ||
|
|
793
794
|
state.btwState.isActive
|
|
794
795
|
) {
|
|
795
796
|
return true;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
SELECTOR_TRIGGERS,
|
|
14
14
|
getProjectedState,
|
|
15
15
|
} from "../utils/inputUtils.js";
|
|
16
|
+
import { AVAILABLE_COMMANDS } from "../constants/commands.js";
|
|
16
17
|
|
|
17
18
|
export interface AttachedImage {
|
|
18
19
|
id: number;
|
|
@@ -40,7 +41,8 @@ export type PendingEffect =
|
|
|
40
41
|
| { type: "PERMISSION_MODE_CHANGE"; mode: PermissionMode }
|
|
41
42
|
| { type: "FETCH_HISTORY" }
|
|
42
43
|
| { type: "PASTE_IMAGE" }
|
|
43
|
-
| { type: "EXECUTE_COMMAND"; command: string }
|
|
44
|
+
| { type: "EXECUTE_COMMAND"; command: string }
|
|
45
|
+
| { type: "RECALL_QUEUED_MESSAGE" };
|
|
44
46
|
|
|
45
47
|
export interface InputManagerCallbacks {
|
|
46
48
|
onInputTextChange?: (text: string) => void;
|
|
@@ -64,6 +66,7 @@ export interface InputManagerCallbacks {
|
|
|
64
66
|
onStatusCommandStateChange?: (show: boolean) => void;
|
|
65
67
|
onPluginManagerStateChange?: (show: boolean) => void;
|
|
66
68
|
onModelSelectorStateChange?: (show: boolean) => void;
|
|
69
|
+
onWorkflowManagerStateChange?: (show: boolean) => void;
|
|
67
70
|
onImagesStateChange?: (images: AttachedImage[]) => void;
|
|
68
71
|
onSendMessage?: (
|
|
69
72
|
content: string,
|
|
@@ -82,6 +85,8 @@ export interface InputManagerCallbacks {
|
|
|
82
85
|
sessionIds: string[];
|
|
83
86
|
}>;
|
|
84
87
|
logger?: Logger;
|
|
88
|
+
hasQueuedMessages?: boolean;
|
|
89
|
+
onRecallQueuedMessage?: () => void;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
export interface InputState {
|
|
@@ -108,6 +113,7 @@ export interface InputState {
|
|
|
108
113
|
showLoginCommand: boolean;
|
|
109
114
|
showPluginManager: boolean;
|
|
110
115
|
showModelSelector: boolean;
|
|
116
|
+
showWorkflowManager: boolean;
|
|
111
117
|
permissionMode: PermissionMode;
|
|
112
118
|
selectorJustUsed: boolean;
|
|
113
119
|
isPasting: boolean;
|
|
@@ -146,6 +152,7 @@ export const initialState: InputState = {
|
|
|
146
152
|
showLoginCommand: false,
|
|
147
153
|
showPluginManager: false,
|
|
148
154
|
showModelSelector: false,
|
|
155
|
+
showWorkflowManager: false,
|
|
149
156
|
permissionMode: "default",
|
|
150
157
|
selectorJustUsed: false,
|
|
151
158
|
isPasting: false,
|
|
@@ -191,6 +198,7 @@ export type InputAction =
|
|
|
191
198
|
| { type: "SET_SHOW_LOGIN_COMMAND"; payload: boolean }
|
|
192
199
|
| { type: "SET_SHOW_PLUGIN_MANAGER"; payload: boolean }
|
|
193
200
|
| { type: "SET_SHOW_MODEL_SELECTOR"; payload: boolean }
|
|
201
|
+
| { type: "SET_SHOW_WORKFLOW_MANAGER"; payload: boolean }
|
|
194
202
|
| { type: "SET_PERMISSION_MODE"; payload: PermissionMode }
|
|
195
203
|
| { type: "SET_SELECTOR_JUST_USED"; payload: boolean }
|
|
196
204
|
| { type: "INSERT_TEXT_WITH_PLACEHOLDER"; payload: string }
|
|
@@ -222,6 +230,7 @@ export type InputAction =
|
|
|
222
230
|
input: string;
|
|
223
231
|
key: Key;
|
|
224
232
|
hasSlashCommand: (cmd: string) => boolean;
|
|
233
|
+
hasQueuedMessages?: boolean;
|
|
225
234
|
};
|
|
226
235
|
};
|
|
227
236
|
|
|
@@ -413,6 +422,12 @@ export function inputReducer(
|
|
|
413
422
|
showModelSelector: action.payload,
|
|
414
423
|
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
415
424
|
};
|
|
425
|
+
case "SET_SHOW_WORKFLOW_MANAGER":
|
|
426
|
+
return {
|
|
427
|
+
...state,
|
|
428
|
+
showWorkflowManager: action.payload,
|
|
429
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
430
|
+
};
|
|
416
431
|
case "SET_PERMISSION_MODE":
|
|
417
432
|
return { ...state, permissionMode: action.payload };
|
|
418
433
|
case "SET_SELECTOR_JUST_USED":
|
|
@@ -671,10 +686,7 @@ export function inputReducer(
|
|
|
671
686
|
return { ...state, pendingEffect: null };
|
|
672
687
|
case "HANDLE_KEY": {
|
|
673
688
|
const { input, key } = action.payload;
|
|
674
|
-
|
|
675
|
-
if (state.selectorJustUsed) {
|
|
676
|
-
return state;
|
|
677
|
-
}
|
|
689
|
+
const hasQueuedMessages = action.payload.hasQueuedMessages ?? false;
|
|
678
690
|
|
|
679
691
|
// 1. BTW State Handling
|
|
680
692
|
if (state.btwState.isActive) {
|
|
@@ -760,7 +772,8 @@ export function inputReducer(
|
|
|
760
772
|
state.showStatusCommand ||
|
|
761
773
|
state.showLoginCommand ||
|
|
762
774
|
state.showPluginManager ||
|
|
763
|
-
state.showModelSelector
|
|
775
|
+
state.showModelSelector ||
|
|
776
|
+
state.showWorkflowManager
|
|
764
777
|
)
|
|
765
778
|
) {
|
|
766
779
|
return { ...state, pendingEffect: { type: "ABORT_MESSAGE" } };
|
|
@@ -812,6 +825,13 @@ export function inputReducer(
|
|
|
812
825
|
!state.showFileSelector &&
|
|
813
826
|
!state.showCommandSelector
|
|
814
827
|
) {
|
|
828
|
+
// If queue has messages, recall from queue first (before history)
|
|
829
|
+
if (hasQueuedMessages) {
|
|
830
|
+
return {
|
|
831
|
+
...state,
|
|
832
|
+
pendingEffect: { type: "RECALL_QUEUED_MESSAGE" },
|
|
833
|
+
};
|
|
834
|
+
}
|
|
815
835
|
if (state.history.length === 0) {
|
|
816
836
|
return { ...state, pendingEffect: { type: "FETCH_HISTORY" } };
|
|
817
837
|
}
|
|
@@ -1021,6 +1041,34 @@ export function inputReducer(
|
|
|
1021
1041
|
};
|
|
1022
1042
|
}
|
|
1023
1043
|
|
|
1044
|
+
// Check if the content is a CLI-internal slash command (help, tasks,
|
|
1045
|
+
// etc.) that should be executed locally rather than sent as a message.
|
|
1046
|
+
// Agent slash commands and unknown /commands always go to SEND_MESSAGE.
|
|
1047
|
+
if (contentWithPlaceholders.startsWith("/")) {
|
|
1048
|
+
const spaceIndex = contentWithPlaceholders.indexOf(" ");
|
|
1049
|
+
const commandName =
|
|
1050
|
+
spaceIndex === -1
|
|
1051
|
+
? contentWithPlaceholders.substring(1)
|
|
1052
|
+
: contentWithPlaceholders.substring(1, spaceIndex);
|
|
1053
|
+
|
|
1054
|
+
const isInternalCommand = AVAILABLE_COMMANDS.some(
|
|
1055
|
+
(cmd) => cmd.id === commandName,
|
|
1056
|
+
);
|
|
1057
|
+
if (isInternalCommand) {
|
|
1058
|
+
return {
|
|
1059
|
+
...state,
|
|
1060
|
+
inputText: "",
|
|
1061
|
+
cursorPosition: 0,
|
|
1062
|
+
historyIndex: -1,
|
|
1063
|
+
longTextMap: {},
|
|
1064
|
+
pendingEffect: {
|
|
1065
|
+
type: "EXECUTE_COMMAND",
|
|
1066
|
+
command: commandName,
|
|
1067
|
+
},
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1024
1072
|
return {
|
|
1025
1073
|
...state,
|
|
1026
1074
|
inputText: "",
|
package/src/print-cli.ts
CHANGED
|
@@ -65,15 +65,15 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
65
65
|
let agent: Agent;
|
|
66
66
|
let isReasoning = false;
|
|
67
67
|
let isContent = false;
|
|
68
|
-
const subagentReasoningStates = new Map<string, boolean>();
|
|
69
|
-
const subagentContentStates = new Map<string, boolean>();
|
|
70
68
|
|
|
71
69
|
// Setup callbacks for agent
|
|
70
|
+
// Print mode only outputs the main agent's response (matching Claude Code's
|
|
71
|
+
// behavior). Subagent output is internal — the main agent incorporates
|
|
72
|
+
// relevant results in its own response.
|
|
72
73
|
const callbacks: AgentCallbacks = {
|
|
73
74
|
onAssistantMessageAdded: () => {
|
|
74
75
|
isReasoning = false;
|
|
75
76
|
isContent = false;
|
|
76
|
-
// Assistant message started - no content to output yet
|
|
77
77
|
},
|
|
78
78
|
onAssistantReasoningUpdated: (chunk: string) => {
|
|
79
79
|
if (!isReasoning) {
|
|
@@ -91,43 +91,9 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
91
91
|
}
|
|
92
92
|
isContent = true;
|
|
93
93
|
}
|
|
94
|
-
// FR-001: Stream content updates for real-time display - output only the new chunk
|
|
95
94
|
process.stdout.write(chunk);
|
|
96
95
|
},
|
|
97
96
|
|
|
98
|
-
// Subagent message callbacks
|
|
99
|
-
onSubagentAssistantMessageAdded: (subagentId: string) => {
|
|
100
|
-
subagentReasoningStates.set(subagentId, false);
|
|
101
|
-
subagentContentStates.set(subagentId, false);
|
|
102
|
-
},
|
|
103
|
-
onSubagentAssistantReasoningUpdated: (
|
|
104
|
-
subagentId: string,
|
|
105
|
-
chunk: string,
|
|
106
|
-
) => {
|
|
107
|
-
if (!subagentReasoningStates.get(subagentId)) {
|
|
108
|
-
process.stdout.write("\n 💭 Reasoning: ");
|
|
109
|
-
subagentReasoningStates.set(subagentId, true);
|
|
110
|
-
}
|
|
111
|
-
process.stdout.write(chunk);
|
|
112
|
-
},
|
|
113
|
-
onSubagentAssistantContentUpdated: (subagentId: string, chunk: string) => {
|
|
114
|
-
if (!subagentContentStates.get(subagentId)) {
|
|
115
|
-
if (subagentReasoningStates.get(subagentId)) {
|
|
116
|
-
process.stdout.write("\n 📝 Response: ");
|
|
117
|
-
} else {
|
|
118
|
-
process.stdout.write("\n ");
|
|
119
|
-
}
|
|
120
|
-
subagentContentStates.set(subagentId, true);
|
|
121
|
-
}
|
|
122
|
-
process.stdout.write(chunk);
|
|
123
|
-
},
|
|
124
|
-
onSubagentUserMessageAdded: (
|
|
125
|
-
_subagentId: string,
|
|
126
|
-
params: { content: string },
|
|
127
|
-
) => {
|
|
128
|
-
process.stdout.write(`\n 👤 User: ${params.content}\n`);
|
|
129
|
-
},
|
|
130
|
-
|
|
131
97
|
// Tool block callback - display tool name when tool starts
|
|
132
98
|
onToolBlockUpdated: (params) => {
|
|
133
99
|
// Print tool name only during 'running' stage (happens once per tool call)
|