wave-code 0.17.1 → 0.17.3
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.map +1 -1
- package/dist/acp/agent.js +1 -11
- 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 +1 -12
- 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/dist/acp/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAyBA,OAAO,EACL,KAAK,KAAK,IAAI,QAAQ,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAUzB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAOpC,MAAM,0BAA0B,CAAC;AAGlC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IA2EnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAcrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,uBAAuB;IA8LrC,OAAO,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAyBA,OAAO,EACL,KAAK,KAAK,IAAI,QAAQ,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAUzB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAOpC,MAAM,0BAA0B,CAAC;AAGlC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IA2EnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAcrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,uBAAuB;IA8LrC,OAAO,CAAC,cAAc;IA6FtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;YAmBL,yBAAyB;IAiIvC,OAAO,CAAC,eAAe;CAyOxB"}
|
package/dist/acp/agent.js
CHANGED
|
@@ -40,7 +40,7 @@ export class WaveAcpAgent {
|
|
|
40
40
|
}
|
|
41
41
|
getSessionConfigOptions(agent) {
|
|
42
42
|
const configuredModels = agent.getConfiguredModels();
|
|
43
|
-
const currentModel = agent.getModelConfig().model;
|
|
43
|
+
const currentModel = agent.getModelConfig().model || "";
|
|
44
44
|
return [
|
|
45
45
|
{
|
|
46
46
|
id: "permission_mode",
|
|
@@ -513,16 +513,6 @@ export class WaveAcpAgent {
|
|
|
513
513
|
newText: parameters.new_string,
|
|
514
514
|
});
|
|
515
515
|
}
|
|
516
|
-
else if (name === BASH_TOOL_NAME &&
|
|
517
|
-
typeof parameters.command === "string") {
|
|
518
|
-
contents.push({
|
|
519
|
-
type: "content",
|
|
520
|
-
content: {
|
|
521
|
-
type: "text",
|
|
522
|
-
text: "**Command:**\n```bash\n" + parameters.command + "\n```",
|
|
523
|
-
},
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
516
|
else if (name === EXIT_PLAN_MODE_TOOL_NAME && planContent) {
|
|
527
517
|
contents.push({
|
|
528
518
|
type: "content",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EA2IjC,CAAC"}
|
|
@@ -10,7 +10,7 @@ import { ConfirmationDetails } from "./ConfirmationDetails.js";
|
|
|
10
10
|
import { ConfirmationSelector } from "./ConfirmationSelector.js";
|
|
11
11
|
import { useChat } from "../contexts/useChat.js";
|
|
12
12
|
export const ChatInterface = () => {
|
|
13
|
-
const { messages, isLoading, isCommandRunning, isCompacting, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, maxInputTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, } = useChat();
|
|
13
|
+
const { messages, isLoading, isCommandRunning, isCompacting, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, maxInputTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, isGoalActive, goalElapsed, isGoalEvaluating, } = useChat();
|
|
14
14
|
const displayMessages = messages;
|
|
15
15
|
const [forceStatic, setForceStatic] = useState(false);
|
|
16
16
|
const { stdout } = useStdout();
|
|
@@ -37,5 +37,8 @@ export const ChatInterface = () => {
|
|
|
37
37
|
]);
|
|
38
38
|
if (!sessionId)
|
|
39
39
|
return null;
|
|
40
|
-
return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading ||
|
|
40
|
+
return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading ||
|
|
41
|
+
isCommandRunning ||
|
|
42
|
+
isCompacting ||
|
|
43
|
+
isGoalEvaluating) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompacting: isCompacting, isGoalEvaluating: isGoalEvaluating, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens, isGoalActive: isGoalActive, goalElapsed: goalElapsed })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }, "confirmation-details")) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel })] }))] }));
|
|
41
44
|
};
|
|
@@ -18,6 +18,8 @@ export interface InputBoxProps {
|
|
|
18
18
|
hasSlashCommand?: (commandId: string) => boolean;
|
|
19
19
|
latestTotalTokens?: number;
|
|
20
20
|
maxInputTokens?: number;
|
|
21
|
+
isGoalActive?: boolean;
|
|
22
|
+
goalElapsed?: string;
|
|
21
23
|
}
|
|
22
24
|
export declare const InputBox: React.FC<InputBoxProps>;
|
|
23
25
|
//# sourceMappingURL=InputBox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAoB9D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAoU5C,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useRef, useCallback } from "react";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
import { useInput } from "ink";
|
|
5
5
|
import { FileSelector } from "./FileSelector.js";
|
|
@@ -13,14 +13,25 @@ import { StatusCommand } from "./StatusCommand.js";
|
|
|
13
13
|
import { LoginCommand } from "./LoginCommand.js";
|
|
14
14
|
import { PluginManagerShell } from "./PluginManagerShell.js";
|
|
15
15
|
import { ModelSelector } from "./ModelSelector.js";
|
|
16
|
+
import { WorkflowManager } from "./WorkflowManager.js";
|
|
16
17
|
import { StatusLine } from "./StatusLine.js";
|
|
17
18
|
import { BtwDisplay } from "./BtwDisplay.js";
|
|
18
19
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
19
20
|
import { useChat } from "../contexts/useChat.js";
|
|
20
21
|
export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
|
|
21
22
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
|
|
22
|
-
export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
|
|
23
|
-
const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, } = useChat();
|
|
23
|
+
export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 128000, isGoalActive, goalElapsed, }) => {
|
|
24
|
+
const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, } = useChat();
|
|
25
|
+
// Ref to hold setInputText so queue callbacks can access it before useInputManager returns
|
|
26
|
+
const setInputTextRef = useRef(() => { });
|
|
27
|
+
const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
|
|
28
|
+
const onRecallQueuedMessage = useCallback(() => {
|
|
29
|
+
const msg = recallQueuedMessage();
|
|
30
|
+
if (msg) {
|
|
31
|
+
const prefix = msg.type === "bang" ? "!" : "";
|
|
32
|
+
setInputTextRef.current(prefix + msg.content);
|
|
33
|
+
}
|
|
34
|
+
}, [recallQueuedMessage]);
|
|
24
35
|
// Input manager with all input state and functionality (including images)
|
|
25
36
|
const { inputText, cursorPosition,
|
|
26
37
|
// Image management
|
|
@@ -32,7 +43,7 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
|
|
|
32
43
|
// History search
|
|
33
44
|
showHistorySearch, historySearchQuery,
|
|
34
45
|
// Task/MCP Manager
|
|
35
|
-
showBackgroundTaskManager, showMcpManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, setShowBackgroundTaskManager, setShowMcpManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector,
|
|
46
|
+
showBackgroundTaskManager, showMcpManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, showWorkflowManager, setShowBackgroundTaskManager, setShowMcpManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector, setShowWorkflowManager,
|
|
36
47
|
// Permission mode
|
|
37
48
|
permissionMode, setPermissionMode,
|
|
38
49
|
// BTW state
|
|
@@ -40,7 +51,9 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
|
|
|
40
51
|
// Main handler
|
|
41
52
|
handleInput,
|
|
42
53
|
// Manager ready state
|
|
43
|
-
isManagerReady,
|
|
54
|
+
isManagerReady,
|
|
55
|
+
// Direct state setters
|
|
56
|
+
setInputText, } = useInputManager({
|
|
44
57
|
onSendMessage: sendMessage,
|
|
45
58
|
onAskBtw: askBtw,
|
|
46
59
|
onHasSlashCommand: hasSlashCommand,
|
|
@@ -50,7 +63,13 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
|
|
|
50
63
|
sessionId,
|
|
51
64
|
workdir: workingDirectory,
|
|
52
65
|
getFullMessageThread,
|
|
66
|
+
hasQueuedMessages,
|
|
67
|
+
onRecallQueuedMessage,
|
|
53
68
|
});
|
|
69
|
+
// Keep setInputText ref updated for queue callbacks
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
setInputTextRef.current = setInputText;
|
|
72
|
+
}, [setInputText]);
|
|
54
73
|
// Sync permission mode from useChat to InputManager
|
|
55
74
|
useEffect(() => {
|
|
56
75
|
setPermissionMode(chatPermissionMode);
|
|
@@ -67,7 +86,8 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
|
|
|
67
86
|
showPluginManager ||
|
|
68
87
|
showModelSelector ||
|
|
69
88
|
showBackgroundTaskManager ||
|
|
70
|
-
showMcpManager
|
|
89
|
+
showMcpManager ||
|
|
90
|
+
showWorkflowManager) {
|
|
71
91
|
return;
|
|
72
92
|
}
|
|
73
93
|
await handleInput(input, key, attachedImages, clearImages);
|
|
@@ -116,11 +136,12 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
|
|
|
116
136
|
if (showModelSelector) {
|
|
117
137
|
return (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel }));
|
|
118
138
|
}
|
|
119
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showBackgroundTaskManager ||
|
|
139
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), showBackgroundTaskManager ||
|
|
120
140
|
showMcpManager ||
|
|
121
141
|
showRewindManager ||
|
|
122
142
|
showHelp ||
|
|
123
143
|
showStatusCommand ||
|
|
124
144
|
showLoginCommand ||
|
|
125
|
-
showPluginManager ||
|
|
145
|
+
showPluginManager ||
|
|
146
|
+
showWorkflowManager || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: btwState.isActive ? "cyan" : "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: btwState.isActive && isPlaceholder ? (_jsxs(_Fragment, { children: [_jsx(Text, { backgroundColor: "white", color: "black", children: " " }), _jsx(Text, { color: "cyan", children: "Type your side question..." })] })) : shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand, isBtwActive: btwState.isActive, isGoalActive: isGoalActive, goalElapsed: goalElapsed, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] }))] }));
|
|
126
147
|
};
|
|
@@ -2,10 +2,11 @@ export interface LoadingIndicatorProps {
|
|
|
2
2
|
isLoading?: boolean;
|
|
3
3
|
isCommandRunning?: boolean;
|
|
4
4
|
isCompacting?: boolean;
|
|
5
|
+
isGoalEvaluating?: boolean;
|
|
5
6
|
latestTotalTokens?: number;
|
|
6
7
|
}
|
|
7
8
|
export declare const LoadingIndicator: {
|
|
8
|
-
({ isLoading, isCommandRunning, isCompacting, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
({ isLoading, isCommandRunning, isCompacting, isGoalEvaluating, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
9
10
|
displayName: string;
|
|
10
11
|
};
|
|
11
12
|
//# sourceMappingURL=LoadingIndicator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;
|
|
1
|
+
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;0FAM1B,qBAAqB;;CAuCvB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompacting = false, latestTotalTokens = 0, }) => {
|
|
4
|
-
return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompacting && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompacting && (_jsx(Text, { color: "magenta", children: "\u273B Compacting message history..." }))] }));
|
|
3
|
+
export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompacting = false, isGoalEvaluating = false, latestTotalTokens = 0, }) => {
|
|
4
|
+
return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompacting && !isGoalEvaluating && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompacting && (_jsx(Text, { color: "magenta", children: "\u273B Compacting message history..." })), isGoalEvaluating && _jsx(Text, { color: "cyan", children: "\u273B Evaluating goal..." })] }));
|
|
5
5
|
};
|
|
6
6
|
LoadingIndicator.displayName = "LoadingIndicator";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueuedMessageList.d.ts","sourceRoot":"","sources":["../../src/components/QueuedMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"QueuedMessageList.d.ts","sourceRoot":"","sources":["../../src/components/QueuedMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EA+BrC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useChat } from "../contexts/useChat.js";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
export const QueuedMessageList = () => {
|
|
@@ -6,13 +6,13 @@ export const QueuedMessageList = () => {
|
|
|
6
6
|
if (queuedMessages.length === 0) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
|
-
return (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", children: [queuedMessages.map((msg, index) => {
|
|
10
|
+
const content = msg.content.trim();
|
|
11
|
+
const hasImages = msg.images && msg.images.length > 0;
|
|
12
|
+
const prefix = msg.type === "bang" ? "! " : "";
|
|
13
|
+
const displayText = prefix + (content || (hasImages ? "[Images]" : ""));
|
|
14
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ["[", index + 1, "] "] }), _jsx(Text, { color: "gray", italic: true, children: displayText.length > 55
|
|
15
|
+
? `${displayText.substring(0, 52)}...`
|
|
16
|
+
: displayText })] }, msg.id ?? index));
|
|
17
|
+
}), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "\u2191 recall"] })] }));
|
|
18
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../src/components/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../src/components/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0DhD,CAAC"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
export const StatusLine = ({ permissionMode, isShellCommand, isBtwActive, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
|
|
3
|
+
export const StatusLine = ({ permissionMode, isShellCommand, isBtwActive, isGoalActive, goalElapsed, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
|
|
4
4
|
const percentage = latestTotalTokens > 0
|
|
5
5
|
? Math.min(Math.round((latestTotalTokens / maxInputTokens) * 100), 100)
|
|
6
6
|
: 0;
|
|
7
7
|
const contextColor = percentage > 95 ? "red" : percentage > 80 ? "yellow" : "gray";
|
|
8
|
-
return (_jsxs(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: [isBtwActive ? (_jsxs(Text, { color: "gray", children: ["Mode: ", _jsx(Text, { color: "cyan", children: "BTW" }), " (ESC to dismiss)"] })) : isShellCommand ? (_jsxs(Text, { color: "gray", children: ["Shell: ", _jsx(Text, { color: "yellow", children: "Run shell command" })] })) : (_jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
return (_jsxs(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: [isBtwActive ? (_jsxs(Text, { color: "gray", children: ["Mode: ", _jsx(Text, { color: "cyan", children: "BTW" }), " (ESC to dismiss)"] })) : isShellCommand ? (_jsxs(Text, { color: "gray", children: ["Shell: ", _jsx(Text, { color: "yellow", children: "Run shell command" })] })) : (_jsxs(Box, { children: [isGoalActive && (_jsxs(Text, { color: "gray", children: [_jsx(Text, { color: "cyan", children: "\u25CE /goal" }), " active", " ", goalElapsed && _jsxs(Text, { children: ["(", goalElapsed, ")"] }), " |", " "] })), _jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan"
|
|
9
|
+
? "yellow"
|
|
10
|
+
: permissionMode === "bypassPermissions"
|
|
11
|
+
? "red"
|
|
12
|
+
: "cyan", bold: permissionMode === "bypassPermissions", children: permissionMode }), " ", "(Shift+Tab to cycle)"] })] })), percentage > 0 && (_jsxs(Text, { color: contextColor, children: [percentage, "% context"] }))] }));
|
|
13
13
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { Task, TaskStatus } from "wave-agent-sdk";
|
|
3
|
+
export declare const getStatusIcon: (status: TaskStatus) => React.ReactNode;
|
|
4
|
+
export declare function sortTasksByPriority(tasks: Task[], completionTimestamps: Map<string, number>, now: number, needsTruncation: boolean): Task[];
|
|
2
5
|
export declare const TaskList: React.FC;
|
|
3
6
|
//# sourceMappingURL=TaskList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../src/components/TaskList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../src/components/TaskList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKvD,eAAO,MAAM,aAAa,GAAI,QAAQ,UAAU,KAAG,KAAK,CAAC,SAaxD,CAAC;AAWF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,EAAE,EACb,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,IAAI,EAAE,CA8CR;AAOD,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EA6J5B,CAAC"}
|
|
@@ -1,37 +1,180 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
2
3
|
import { useChat } from "../contexts/useChat.js";
|
|
3
|
-
import { Box, Text } from "ink";
|
|
4
|
+
import { Box, Text, useStdout } from "ink";
|
|
4
5
|
import { useTasks } from "../hooks/useTasks.js";
|
|
6
|
+
const RECENTLY_COMPLETED_TTL_MS = 30000;
|
|
7
|
+
const AUTO_HIDE_DELAY_MS = 5000;
|
|
8
|
+
export const getStatusIcon = (status) => {
|
|
9
|
+
switch (status) {
|
|
10
|
+
case "pending":
|
|
11
|
+
return _jsx(Text, { color: "gray", children: "\u25A1" });
|
|
12
|
+
case "in_progress":
|
|
13
|
+
return _jsx(Text, { color: "yellow", children: "\u25A0" });
|
|
14
|
+
case "completed":
|
|
15
|
+
return _jsx(Text, { color: "green", children: "\u2713" });
|
|
16
|
+
case "deleted":
|
|
17
|
+
return _jsx(Text, { color: "red", children: "\u2715" });
|
|
18
|
+
default:
|
|
19
|
+
return _jsx(Text, { color: "gray", children: "?" });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function byIdAsc(a, b) {
|
|
23
|
+
const aNum = parseInt(a.id, 10);
|
|
24
|
+
const bNum = parseInt(b.id, 10);
|
|
25
|
+
if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
26
|
+
return aNum - bNum;
|
|
27
|
+
}
|
|
28
|
+
return a.id.localeCompare(b.id);
|
|
29
|
+
}
|
|
30
|
+
export function sortTasksByPriority(tasks, completionTimestamps, now, needsTruncation) {
|
|
31
|
+
if (!needsTruncation) {
|
|
32
|
+
// No truncation needed — sort by ID for stable ordering
|
|
33
|
+
return [...tasks].sort(byIdAsc);
|
|
34
|
+
}
|
|
35
|
+
// When truncation is needed, prioritize:
|
|
36
|
+
// recently completed > in_progress > pending (unblocked first) > older completed > deleted
|
|
37
|
+
const recentCompleted = [];
|
|
38
|
+
const olderCompleted = [];
|
|
39
|
+
for (const task of tasks.filter((t) => t.status === "completed")) {
|
|
40
|
+
const ts = completionTimestamps.get(task.id);
|
|
41
|
+
if (ts && now - ts < RECENTLY_COMPLETED_TTL_MS) {
|
|
42
|
+
recentCompleted.push(task);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
olderCompleted.push(task);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
recentCompleted.sort(byIdAsc);
|
|
49
|
+
olderCompleted.sort(byIdAsc);
|
|
50
|
+
const inProgress = tasks
|
|
51
|
+
.filter((t) => t.status === "in_progress")
|
|
52
|
+
.sort(byIdAsc);
|
|
53
|
+
const unresolvedTaskIds = new Set(tasks.filter((t) => t.status !== "completed").map((t) => t.id));
|
|
54
|
+
const pending = tasks
|
|
55
|
+
.filter((t) => t.status === "pending")
|
|
56
|
+
.sort((a, b) => {
|
|
57
|
+
const aBlocked = a.blockedBy.some((id) => unresolvedTaskIds.has(id));
|
|
58
|
+
const bBlocked = b.blockedBy.some((id) => unresolvedTaskIds.has(id));
|
|
59
|
+
if (aBlocked !== bBlocked)
|
|
60
|
+
return aBlocked ? 1 : -1;
|
|
61
|
+
return byIdAsc(a, b);
|
|
62
|
+
});
|
|
63
|
+
const deleted = tasks.filter((t) => t.status === "deleted").sort(byIdAsc);
|
|
64
|
+
return [
|
|
65
|
+
...recentCompleted,
|
|
66
|
+
...inProgress,
|
|
67
|
+
...pending,
|
|
68
|
+
...olderCompleted,
|
|
69
|
+
...deleted,
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
function getDisplayLimit(rows) {
|
|
73
|
+
const available = rows ?? 24;
|
|
74
|
+
return available <= 10 ? 0 : Math.min(10, Math.max(3, available - 14));
|
|
75
|
+
}
|
|
5
76
|
export const TaskList = () => {
|
|
6
77
|
const tasks = useTasks();
|
|
7
78
|
const { isTaskListVisible } = useChat();
|
|
8
|
-
|
|
9
|
-
|
|
79
|
+
const { stdout } = useStdout();
|
|
80
|
+
const completionTimestampsRef = React.useRef(new Map());
|
|
81
|
+
const previousCompletedIdsRef = React.useRef(null);
|
|
82
|
+
const autoHideTimerRef = React.useRef(null);
|
|
83
|
+
const [autoHidden, setAutoHidden] = React.useState(() => {
|
|
84
|
+
// If all tasks are already completed on mount (e.g. session restore),
|
|
85
|
+
// start hidden immediately instead of flashing for 5 seconds.
|
|
86
|
+
const active = tasks.filter((t) => t.status !== "deleted");
|
|
87
|
+
return active.length > 0 && active.every((t) => t.status === "completed");
|
|
88
|
+
});
|
|
89
|
+
const [, forceUpdate] = React.useState(0);
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
const activeTasks = tasks.filter((t) => t.status !== "deleted");
|
|
92
|
+
// Track completion timestamps: only set when a task newly transitions to completed
|
|
93
|
+
if (previousCompletedIdsRef.current === null) {
|
|
94
|
+
previousCompletedIdsRef.current = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
10
95
|
}
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
case "in_progress":
|
|
16
|
-
return _jsx(Text, { color: "yellow", children: "\u25A0" });
|
|
17
|
-
case "completed":
|
|
18
|
-
return _jsx(Text, { color: "green", children: "\u2713" });
|
|
19
|
-
case "deleted":
|
|
20
|
-
return _jsx(Text, { color: "red", children: "\u2715" });
|
|
21
|
-
default:
|
|
22
|
-
return _jsx(Text, { color: "gray", children: "?" });
|
|
96
|
+
const currentCompletedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
97
|
+
for (const id of currentCompletedIds) {
|
|
98
|
+
if (!previousCompletedIdsRef.current.has(id)) {
|
|
99
|
+
completionTimestampsRef.current.set(id, now);
|
|
23
100
|
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
101
|
+
}
|
|
102
|
+
for (const id of completionTimestampsRef.current.keys()) {
|
|
103
|
+
if (!currentCompletedIds.has(id)) {
|
|
104
|
+
completionTimestampsRef.current.delete(id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
previousCompletedIdsRef.current = currentCompletedIds;
|
|
108
|
+
// Schedule re-render when the next recent completion expires
|
|
109
|
+
React.useEffect(() => {
|
|
110
|
+
if (completionTimestampsRef.current.size === 0)
|
|
111
|
+
return;
|
|
112
|
+
const currentNow = Date.now();
|
|
113
|
+
let earliestExpiry = Infinity;
|
|
114
|
+
for (const ts of completionTimestampsRef.current.values()) {
|
|
115
|
+
const expiry = ts + RECENTLY_COMPLETED_TTL_MS;
|
|
116
|
+
if (expiry > currentNow && expiry < earliestExpiry) {
|
|
117
|
+
earliestExpiry = expiry;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (earliestExpiry === Infinity)
|
|
121
|
+
return;
|
|
122
|
+
const timer = setTimeout(() => forceUpdate((n) => n + 1), earliestExpiry - currentNow);
|
|
123
|
+
return () => clearTimeout(timer);
|
|
124
|
+
}, [tasks]);
|
|
125
|
+
// Auto-hide logic: when all active tasks are completed
|
|
126
|
+
const allCompleted = activeTasks.length > 0 &&
|
|
127
|
+
activeTasks.every((t) => t.status === "completed");
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
if (allCompleted && !autoHidden) {
|
|
130
|
+
autoHideTimerRef.current = setTimeout(() => {
|
|
131
|
+
setAutoHidden(true);
|
|
132
|
+
}, AUTO_HIDE_DELAY_MS);
|
|
133
|
+
}
|
|
134
|
+
if (!allCompleted && autoHidden) {
|
|
135
|
+
setAutoHidden(false);
|
|
136
|
+
}
|
|
137
|
+
return () => {
|
|
138
|
+
if (autoHideTimerRef.current) {
|
|
139
|
+
clearTimeout(autoHideTimerRef.current);
|
|
140
|
+
autoHideTimerRef.current = null;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}, [allCompleted, autoHidden]);
|
|
144
|
+
if (tasks.length === 0 || !isTaskListVisible || autoHidden) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const displayLimit = getDisplayLimit(stdout?.rows);
|
|
148
|
+
const needsTruncation = tasks.length > displayLimit;
|
|
149
|
+
const sorted = sortTasksByPriority(tasks, completionTimestampsRef.current, now, needsTruncation);
|
|
150
|
+
const displayed = displayLimit > 0 ? sorted.slice(0, displayLimit) : [];
|
|
151
|
+
const hidden = displayLimit > 0 ? sorted.slice(displayLimit) : sorted;
|
|
152
|
+
const doneCount = tasks.filter((t) => t.status === "completed").length;
|
|
153
|
+
const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
|
|
154
|
+
const openCount = tasks.filter((t) => t.status === "pending").length;
|
|
155
|
+
// Summary parts for hidden tasks
|
|
156
|
+
const hiddenInProgress = hidden.filter((t) => t.status === "in_progress").length;
|
|
157
|
+
const hiddenPending = hidden.filter((t) => t.status === "pending").length;
|
|
158
|
+
const hiddenCompleted = hidden.filter((t) => t.status === "completed").length;
|
|
159
|
+
const summaryParts = [];
|
|
160
|
+
if (hiddenInProgress > 0)
|
|
161
|
+
summaryParts.push(`${hiddenInProgress} in progress`);
|
|
162
|
+
if (hiddenPending > 0)
|
|
163
|
+
summaryParts.push(`${hiddenPending} pending`);
|
|
164
|
+
if (hiddenCompleted > 0)
|
|
165
|
+
summaryParts.push(`${hiddenCompleted} completed`);
|
|
166
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [tasks.length, " tasks (", doneCount, " done, ", inProgressCount, " in progress,", " ", openCount, " open)"] }), displayed.map((task) => {
|
|
167
|
+
const isDimmed = task.status === "completed" || task.status === "deleted";
|
|
168
|
+
const unresolvedTaskIds = new Set(tasks.filter((t) => t.status !== "completed").map((t) => t.id));
|
|
169
|
+
const isBlocked = task.blockedBy &&
|
|
170
|
+
task.blockedBy.some((id) => unresolvedTaskIds.has(id));
|
|
171
|
+
const blockingTaskIds = isBlocked
|
|
172
|
+
? task.blockedBy.map((id) => `#${id}`)
|
|
173
|
+
: [];
|
|
174
|
+
const blockedByText = isBlocked && blockingTaskIds.length > 0
|
|
175
|
+
? ` (Blocked by: ${blockingTaskIds.join(", ")})`
|
|
176
|
+
: "";
|
|
177
|
+
const fullText = `${task.subject}${blockedByText}`;
|
|
178
|
+
return (_jsxs(Box, { gap: 1, children: [getStatusIcon(task.status), _jsx(Text, { dimColor: isDimmed, wrap: "truncate-end", children: fullText })] }, task.id));
|
|
179
|
+
}), displayLimit > 0 && hidden.length > 0 && (_jsxs(Text, { dimColor: true, children: [" \u2026 +", summaryParts.join(", ")] }))] }));
|
|
37
180
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskNotificationMessage.d.ts","sourceRoot":"","sources":["../../src/components/TaskNotificationMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,qBAAqB,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"TaskNotificationMessage.d.ts","sourceRoot":"","sources":["../../src/components/TaskNotificationMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AASD,eAAO,MAAM,uBAAuB,GAAI,YAErC,4BAA4B,4CAW9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkflowManager.d.ts","sourceRoot":"","sources":["../../src/components/WorkflowManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAmCD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAiR1D,CAAC"}
|