wave-code 0.17.1 → 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.js +1 -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 +1 -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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAtSelectorPosition, getSlashSelectorPosition, getWordEnd, SELECTOR_TRIGGERS, getProjectedState, } from "../utils/inputUtils.js";
|
|
2
|
+
import { AVAILABLE_COMMANDS } from "../constants/commands.js";
|
|
2
3
|
export const initialState = {
|
|
3
4
|
inputText: "",
|
|
4
5
|
cursorPosition: 0,
|
|
@@ -23,6 +24,7 @@ export const initialState = {
|
|
|
23
24
|
showLoginCommand: false,
|
|
24
25
|
showPluginManager: false,
|
|
25
26
|
showModelSelector: false,
|
|
27
|
+
showWorkflowManager: false,
|
|
26
28
|
permissionMode: "default",
|
|
27
29
|
selectorJustUsed: false,
|
|
28
30
|
isPasting: false,
|
|
@@ -214,6 +216,12 @@ export function inputReducer(state, action) {
|
|
|
214
216
|
showModelSelector: action.payload,
|
|
215
217
|
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
216
218
|
};
|
|
219
|
+
case "SET_SHOW_WORKFLOW_MANAGER":
|
|
220
|
+
return {
|
|
221
|
+
...state,
|
|
222
|
+
showWorkflowManager: action.payload,
|
|
223
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
224
|
+
};
|
|
217
225
|
case "SET_PERMISSION_MODE":
|
|
218
226
|
return { ...state, permissionMode: action.payload };
|
|
219
227
|
case "SET_SELECTOR_JUST_USED":
|
|
@@ -457,9 +465,7 @@ export function inputReducer(state, action) {
|
|
|
457
465
|
return { ...state, pendingEffect: null };
|
|
458
466
|
case "HANDLE_KEY": {
|
|
459
467
|
const { input, key } = action.payload;
|
|
460
|
-
|
|
461
|
-
return state;
|
|
462
|
-
}
|
|
468
|
+
const hasQueuedMessages = action.payload.hasQueuedMessages ?? false;
|
|
463
469
|
// 1. BTW State Handling
|
|
464
470
|
if (state.btwState.isActive) {
|
|
465
471
|
if (key.escape) {
|
|
@@ -540,7 +546,8 @@ export function inputReducer(state, action) {
|
|
|
540
546
|
state.showStatusCommand ||
|
|
541
547
|
state.showLoginCommand ||
|
|
542
548
|
state.showPluginManager ||
|
|
543
|
-
state.showModelSelector
|
|
549
|
+
state.showModelSelector ||
|
|
550
|
+
state.showWorkflowManager)) {
|
|
544
551
|
return { ...state, pendingEffect: { type: "ABORT_MESSAGE" } };
|
|
545
552
|
}
|
|
546
553
|
return state;
|
|
@@ -582,6 +589,13 @@ export function inputReducer(state, action) {
|
|
|
582
589
|
if (key.upArrow &&
|
|
583
590
|
!state.showFileSelector &&
|
|
584
591
|
!state.showCommandSelector) {
|
|
592
|
+
// If queue has messages, recall from queue first (before history)
|
|
593
|
+
if (hasQueuedMessages) {
|
|
594
|
+
return {
|
|
595
|
+
...state,
|
|
596
|
+
pendingEffect: { type: "RECALL_QUEUED_MESSAGE" },
|
|
597
|
+
};
|
|
598
|
+
}
|
|
585
599
|
if (state.history.length === 0) {
|
|
586
600
|
return { ...state, pendingEffect: { type: "FETCH_HISTORY" } };
|
|
587
601
|
}
|
|
@@ -754,6 +768,29 @@ export function inputReducer(state, action) {
|
|
|
754
768
|
pendingEffect: question ? { type: "ASK_BTW", question } : null,
|
|
755
769
|
};
|
|
756
770
|
}
|
|
771
|
+
// Check if the content is a CLI-internal slash command (help, tasks,
|
|
772
|
+
// etc.) that should be executed locally rather than sent as a message.
|
|
773
|
+
// Agent slash commands and unknown /commands always go to SEND_MESSAGE.
|
|
774
|
+
if (contentWithPlaceholders.startsWith("/")) {
|
|
775
|
+
const spaceIndex = contentWithPlaceholders.indexOf(" ");
|
|
776
|
+
const commandName = spaceIndex === -1
|
|
777
|
+
? contentWithPlaceholders.substring(1)
|
|
778
|
+
: contentWithPlaceholders.substring(1, spaceIndex);
|
|
779
|
+
const isInternalCommand = AVAILABLE_COMMANDS.some((cmd) => cmd.id === commandName);
|
|
780
|
+
if (isInternalCommand) {
|
|
781
|
+
return {
|
|
782
|
+
...state,
|
|
783
|
+
inputText: "",
|
|
784
|
+
cursorPosition: 0,
|
|
785
|
+
historyIndex: -1,
|
|
786
|
+
longTextMap: {},
|
|
787
|
+
pendingEffect: {
|
|
788
|
+
type: "EXECUTE_COMMAND",
|
|
789
|
+
command: commandName,
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
}
|
|
757
794
|
return {
|
|
758
795
|
...state,
|
|
759
796
|
inputText: "",
|
package/dist/print-cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,gBAAgB,EAAE,eAAe,CAAC,CAAC;CACvE;AAgBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,gBAAgB,EAAE,eAAe,CAAC,CAAC;CACvE;AAgBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAqL3E"}
|
package/dist/print-cli.js
CHANGED
|
@@ -24,14 +24,14 @@ export async function startPrintCli(options) {
|
|
|
24
24
|
let agent;
|
|
25
25
|
let isReasoning = false;
|
|
26
26
|
let isContent = false;
|
|
27
|
-
const subagentReasoningStates = new Map();
|
|
28
|
-
const subagentContentStates = new Map();
|
|
29
27
|
// Setup callbacks for agent
|
|
28
|
+
// Print mode only outputs the main agent's response (matching Claude Code's
|
|
29
|
+
// behavior). Subagent output is internal — the main agent incorporates
|
|
30
|
+
// relevant results in its own response.
|
|
30
31
|
const callbacks = {
|
|
31
32
|
onAssistantMessageAdded: () => {
|
|
32
33
|
isReasoning = false;
|
|
33
34
|
isContent = false;
|
|
34
|
-
// Assistant message started - no content to output yet
|
|
35
35
|
},
|
|
36
36
|
onAssistantReasoningUpdated: (chunk) => {
|
|
37
37
|
if (!isReasoning) {
|
|
@@ -50,36 +50,8 @@ export async function startPrintCli(options) {
|
|
|
50
50
|
}
|
|
51
51
|
isContent = true;
|
|
52
52
|
}
|
|
53
|
-
// FR-001: Stream content updates for real-time display - output only the new chunk
|
|
54
53
|
process.stdout.write(chunk);
|
|
55
54
|
},
|
|
56
|
-
// Subagent message callbacks
|
|
57
|
-
onSubagentAssistantMessageAdded: (subagentId) => {
|
|
58
|
-
subagentReasoningStates.set(subagentId, false);
|
|
59
|
-
subagentContentStates.set(subagentId, false);
|
|
60
|
-
},
|
|
61
|
-
onSubagentAssistantReasoningUpdated: (subagentId, chunk) => {
|
|
62
|
-
if (!subagentReasoningStates.get(subagentId)) {
|
|
63
|
-
process.stdout.write("\n 💭 Reasoning: ");
|
|
64
|
-
subagentReasoningStates.set(subagentId, true);
|
|
65
|
-
}
|
|
66
|
-
process.stdout.write(chunk);
|
|
67
|
-
},
|
|
68
|
-
onSubagentAssistantContentUpdated: (subagentId, chunk) => {
|
|
69
|
-
if (!subagentContentStates.get(subagentId)) {
|
|
70
|
-
if (subagentReasoningStates.get(subagentId)) {
|
|
71
|
-
process.stdout.write("\n 📝 Response: ");
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
process.stdout.write("\n ");
|
|
75
|
-
}
|
|
76
|
-
subagentContentStates.set(subagentId, true);
|
|
77
|
-
}
|
|
78
|
-
process.stdout.write(chunk);
|
|
79
|
-
},
|
|
80
|
-
onSubagentUserMessageAdded: (_subagentId, params) => {
|
|
81
|
-
process.stdout.write(`\n 👤 User: ${params.content}\n`);
|
|
82
|
-
},
|
|
83
55
|
// Tool block callback - display tool name when tool starts
|
|
84
56
|
onToolBlockUpdated: (params) => {
|
|
85
57
|
// Print tool name only during 'running' stage (happens once per tool call)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
import type { WorkflowRun } from "wave-agent-sdk";
|
|
3
|
+
export type PendingEffect = {
|
|
4
|
+
type: "CANCEL";
|
|
5
|
+
} | {
|
|
6
|
+
type: "STOP_RUN";
|
|
7
|
+
runId: string;
|
|
8
|
+
};
|
|
9
|
+
export interface WorkflowManagerState {
|
|
10
|
+
runs: WorkflowRun[];
|
|
11
|
+
selectedIndex: number;
|
|
12
|
+
viewMode: "list" | "detail";
|
|
13
|
+
detailRunId: string | null;
|
|
14
|
+
pendingEffect: PendingEffect | null;
|
|
15
|
+
}
|
|
16
|
+
export type WorkflowManagerAction = {
|
|
17
|
+
type: "SET_RUNS";
|
|
18
|
+
runs: WorkflowRun[];
|
|
19
|
+
} | {
|
|
20
|
+
type: "SELECT_INDEX";
|
|
21
|
+
index: number;
|
|
22
|
+
} | {
|
|
23
|
+
type: "MOVE_UP";
|
|
24
|
+
} | {
|
|
25
|
+
type: "MOVE_DOWN";
|
|
26
|
+
} | {
|
|
27
|
+
type: "SELECT_CURRENT";
|
|
28
|
+
} | {
|
|
29
|
+
type: "SET_VIEW_MODE";
|
|
30
|
+
mode: "list" | "detail";
|
|
31
|
+
} | {
|
|
32
|
+
type: "RESET_DETAIL";
|
|
33
|
+
} | {
|
|
34
|
+
type: "HANDLE_KEY";
|
|
35
|
+
input: string;
|
|
36
|
+
key: Key;
|
|
37
|
+
} | {
|
|
38
|
+
type: "CLEAR_PENDING_EFFECT";
|
|
39
|
+
};
|
|
40
|
+
export declare function workflowManagerReducer(state: WorkflowManagerState, action: WorkflowManagerAction): WorkflowManagerState;
|
|
41
|
+
//# sourceMappingURL=workflowManagerReducer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowManagerReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/workflowManagerReducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,WAAW,EAAE,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAErC,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,qBAAqB,GAC5B,oBAAoB,CAiHtB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export function workflowManagerReducer(state, action) {
|
|
2
|
+
switch (action.type) {
|
|
3
|
+
case "SET_RUNS":
|
|
4
|
+
return { ...state, runs: action.runs };
|
|
5
|
+
case "SELECT_INDEX":
|
|
6
|
+
return { ...state, selectedIndex: action.index };
|
|
7
|
+
case "MOVE_UP":
|
|
8
|
+
return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
|
|
9
|
+
case "MOVE_DOWN":
|
|
10
|
+
return {
|
|
11
|
+
...state,
|
|
12
|
+
selectedIndex: Math.min(state.runs.length > 0 ? state.runs.length - 1 : 0, state.selectedIndex + 1),
|
|
13
|
+
};
|
|
14
|
+
case "SELECT_CURRENT":
|
|
15
|
+
if (state.runs.length > 0 && state.selectedIndex < state.runs.length) {
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
detailRunId: state.runs[state.selectedIndex].runId,
|
|
19
|
+
viewMode: "detail",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return state;
|
|
23
|
+
case "SET_VIEW_MODE":
|
|
24
|
+
return { ...state, viewMode: action.mode };
|
|
25
|
+
case "RESET_DETAIL":
|
|
26
|
+
return {
|
|
27
|
+
...state,
|
|
28
|
+
viewMode: "list",
|
|
29
|
+
detailRunId: null,
|
|
30
|
+
};
|
|
31
|
+
case "HANDLE_KEY": {
|
|
32
|
+
const { input, key } = action;
|
|
33
|
+
if (state.viewMode === "list") {
|
|
34
|
+
if (key.return) {
|
|
35
|
+
if (state.runs.length > 0 &&
|
|
36
|
+
state.selectedIndex < state.runs.length) {
|
|
37
|
+
return {
|
|
38
|
+
...state,
|
|
39
|
+
detailRunId: state.runs[state.selectedIndex].runId,
|
|
40
|
+
viewMode: "detail",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return state;
|
|
44
|
+
}
|
|
45
|
+
if (key.escape) {
|
|
46
|
+
return { ...state, pendingEffect: { type: "CANCEL" } };
|
|
47
|
+
}
|
|
48
|
+
if (key.upArrow) {
|
|
49
|
+
return {
|
|
50
|
+
...state,
|
|
51
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (key.downArrow) {
|
|
55
|
+
return {
|
|
56
|
+
...state,
|
|
57
|
+
selectedIndex: Math.min(state.runs.length > 0 ? state.runs.length - 1 : 0, state.selectedIndex + 1),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (input === "k") {
|
|
61
|
+
if (state.runs.length > 0 &&
|
|
62
|
+
state.selectedIndex < state.runs.length) {
|
|
63
|
+
const selectedRun = state.runs[state.selectedIndex];
|
|
64
|
+
if (selectedRun.status === "running") {
|
|
65
|
+
return {
|
|
66
|
+
...state,
|
|
67
|
+
pendingEffect: { type: "STOP_RUN", runId: selectedRun.runId },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (state.viewMode === "detail") {
|
|
75
|
+
if (key.escape) {
|
|
76
|
+
return {
|
|
77
|
+
...state,
|
|
78
|
+
viewMode: "list",
|
|
79
|
+
detailRunId: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (input === "k" && state.detailRunId) {
|
|
83
|
+
const run = state.runs.find((r) => r.runId === state.detailRunId);
|
|
84
|
+
if (run && run.status === "running") {
|
|
85
|
+
return {
|
|
86
|
+
...state,
|
|
87
|
+
pendingEffect: { type: "STOP_RUN", runId: state.detailRunId },
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return state;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
case "CLEAR_PENDING_EFFECT":
|
|
96
|
+
return { ...state, pendingEffect: null };
|
|
97
|
+
default:
|
|
98
|
+
return state;
|
|
99
|
+
}
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"semver": "^7.7.4",
|
|
43
43
|
"yargs": "^17.7.2",
|
|
44
44
|
"zod": "^3.23.8",
|
|
45
|
-
"wave-agent-sdk": "0.17.
|
|
45
|
+
"wave-agent-sdk": "0.17.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/react": "^19.1.8",
|
package/src/acp/agent.ts
CHANGED
|
@@ -102,7 +102,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
102
102
|
|
|
103
103
|
private getSessionConfigOptions(agent: WaveAgent): SessionConfigOption[] {
|
|
104
104
|
const configuredModels = agent.getConfiguredModels();
|
|
105
|
-
const currentModel = agent.getModelConfig().model;
|
|
105
|
+
const currentModel = agent.getModelConfig().model || "";
|
|
106
106
|
|
|
107
107
|
return [
|
|
108
108
|
{
|
|
@@ -37,6 +37,9 @@ export const ChatInterface: React.FC = () => {
|
|
|
37
37
|
workdir,
|
|
38
38
|
remountKey,
|
|
39
39
|
requestRemount,
|
|
40
|
+
isGoalActive,
|
|
41
|
+
goalElapsed,
|
|
42
|
+
isGoalEvaluating,
|
|
40
43
|
} = useChat();
|
|
41
44
|
|
|
42
45
|
const displayMessages = messages;
|
|
@@ -80,11 +83,15 @@ export const ChatInterface: React.FC = () => {
|
|
|
80
83
|
|
|
81
84
|
{!isConfirmationVisible && !isExpanded && (
|
|
82
85
|
<>
|
|
83
|
-
{(isLoading ||
|
|
86
|
+
{(isLoading ||
|
|
87
|
+
isCommandRunning ||
|
|
88
|
+
isCompacting ||
|
|
89
|
+
isGoalEvaluating) && (
|
|
84
90
|
<LoadingIndicator
|
|
85
91
|
isLoading={isLoading}
|
|
86
92
|
isCommandRunning={isCommandRunning}
|
|
87
93
|
isCompacting={isCompacting}
|
|
94
|
+
isGoalEvaluating={isGoalEvaluating}
|
|
88
95
|
latestTotalTokens={latestTotalTokens}
|
|
89
96
|
/>
|
|
90
97
|
)}
|
|
@@ -102,6 +109,8 @@ export const ChatInterface: React.FC = () => {
|
|
|
102
109
|
hasSlashCommand={hasSlashCommand}
|
|
103
110
|
latestTotalTokens={latestTotalTokens}
|
|
104
111
|
maxInputTokens={maxInputTokens}
|
|
112
|
+
isGoalActive={isGoalActive}
|
|
113
|
+
goalElapsed={goalElapsed}
|
|
105
114
|
/>
|
|
106
115
|
</>
|
|
107
116
|
)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
1
|
+
import React, { useEffect, useRef, useCallback } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { useInput } from "ink";
|
|
4
4
|
import { FileSelector } from "./FileSelector.js";
|
|
@@ -12,6 +12,7 @@ import { StatusCommand } from "./StatusCommand.js";
|
|
|
12
12
|
import { LoginCommand } from "./LoginCommand.js";
|
|
13
13
|
import { PluginManagerShell } from "./PluginManagerShell.js";
|
|
14
14
|
import { ModelSelector } from "./ModelSelector.js";
|
|
15
|
+
import { WorkflowManager } from "./WorkflowManager.js";
|
|
15
16
|
import { StatusLine } from "./StatusLine.js";
|
|
16
17
|
import { BtwDisplay } from "./BtwDisplay.js";
|
|
17
18
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
@@ -47,6 +48,9 @@ export interface InputBoxProps {
|
|
|
47
48
|
// Token usage
|
|
48
49
|
latestTotalTokens?: number;
|
|
49
50
|
maxInputTokens?: number;
|
|
51
|
+
// Goal state
|
|
52
|
+
isGoalActive?: boolean;
|
|
53
|
+
goalElapsed?: string;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
export const InputBox: React.FC<InputBoxProps> = ({
|
|
@@ -59,6 +63,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
59
63
|
hasSlashCommand = () => false,
|
|
60
64
|
latestTotalTokens = 0,
|
|
61
65
|
maxInputTokens = 128000,
|
|
66
|
+
isGoalActive,
|
|
67
|
+
goalElapsed,
|
|
62
68
|
}) => {
|
|
63
69
|
const {
|
|
64
70
|
permissionMode: chatPermissionMode,
|
|
@@ -74,8 +80,23 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
74
80
|
configuredModels,
|
|
75
81
|
setModel,
|
|
76
82
|
recreateAgent,
|
|
83
|
+
recallQueuedMessage,
|
|
84
|
+
queuedMessages,
|
|
77
85
|
} = useChat();
|
|
78
86
|
|
|
87
|
+
// Ref to hold setInputText so queue callbacks can access it before useInputManager returns
|
|
88
|
+
const setInputTextRef = useRef<(text: string) => void>(() => {});
|
|
89
|
+
|
|
90
|
+
const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
|
|
91
|
+
|
|
92
|
+
const onRecallQueuedMessage = useCallback(() => {
|
|
93
|
+
const msg = recallQueuedMessage();
|
|
94
|
+
if (msg) {
|
|
95
|
+
const prefix = msg.type === "bang" ? "!" : "";
|
|
96
|
+
setInputTextRef.current(prefix + msg.content);
|
|
97
|
+
}
|
|
98
|
+
}, [recallQueuedMessage]);
|
|
99
|
+
|
|
79
100
|
// Input manager with all input state and functionality (including images)
|
|
80
101
|
const {
|
|
81
102
|
inputText,
|
|
@@ -110,6 +131,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
110
131
|
showLoginCommand,
|
|
111
132
|
showPluginManager,
|
|
112
133
|
showModelSelector,
|
|
134
|
+
showWorkflowManager,
|
|
113
135
|
setShowBackgroundTaskManager,
|
|
114
136
|
setShowMcpManager,
|
|
115
137
|
setShowRewindManager,
|
|
@@ -118,6 +140,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
118
140
|
setShowLoginCommand,
|
|
119
141
|
setShowPluginManager,
|
|
120
142
|
setShowModelSelector,
|
|
143
|
+
setShowWorkflowManager,
|
|
121
144
|
// Permission mode
|
|
122
145
|
permissionMode,
|
|
123
146
|
setPermissionMode,
|
|
@@ -127,6 +150,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
127
150
|
handleInput,
|
|
128
151
|
// Manager ready state
|
|
129
152
|
isManagerReady,
|
|
153
|
+
// Direct state setters
|
|
154
|
+
setInputText,
|
|
130
155
|
} = useInputManager({
|
|
131
156
|
onSendMessage: sendMessage,
|
|
132
157
|
onAskBtw: askBtw,
|
|
@@ -137,8 +162,15 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
137
162
|
sessionId,
|
|
138
163
|
workdir: workingDirectory,
|
|
139
164
|
getFullMessageThread,
|
|
165
|
+
hasQueuedMessages,
|
|
166
|
+
onRecallQueuedMessage,
|
|
140
167
|
});
|
|
141
168
|
|
|
169
|
+
// Keep setInputText ref updated for queue callbacks
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
setInputTextRef.current = setInputText;
|
|
172
|
+
}, [setInputText]);
|
|
173
|
+
|
|
142
174
|
// Sync permission mode from useChat to InputManager
|
|
143
175
|
useEffect(() => {
|
|
144
176
|
setPermissionMode(chatPermissionMode);
|
|
@@ -157,7 +189,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
157
189
|
showPluginManager ||
|
|
158
190
|
showModelSelector ||
|
|
159
191
|
showBackgroundTaskManager ||
|
|
160
|
-
showMcpManager
|
|
192
|
+
showMcpManager ||
|
|
193
|
+
showWorkflowManager
|
|
161
194
|
) {
|
|
162
195
|
return;
|
|
163
196
|
}
|
|
@@ -291,13 +324,18 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
291
324
|
/>
|
|
292
325
|
)}
|
|
293
326
|
|
|
327
|
+
{showWorkflowManager && (
|
|
328
|
+
<WorkflowManager onCancel={() => setShowWorkflowManager(false)} />
|
|
329
|
+
)}
|
|
330
|
+
|
|
294
331
|
{showBackgroundTaskManager ||
|
|
295
332
|
showMcpManager ||
|
|
296
333
|
showRewindManager ||
|
|
297
334
|
showHelp ||
|
|
298
335
|
showStatusCommand ||
|
|
299
336
|
showLoginCommand ||
|
|
300
|
-
showPluginManager ||
|
|
337
|
+
showPluginManager ||
|
|
338
|
+
showWorkflowManager || (
|
|
301
339
|
<Box flexDirection="column">
|
|
302
340
|
<Box
|
|
303
341
|
borderStyle="single"
|
|
@@ -330,6 +368,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
330
368
|
permissionMode={permissionMode}
|
|
331
369
|
isShellCommand={isShellCommand}
|
|
332
370
|
isBtwActive={btwState.isActive}
|
|
371
|
+
isGoalActive={isGoalActive}
|
|
372
|
+
goalElapsed={goalElapsed}
|
|
333
373
|
latestTotalTokens={latestTotalTokens}
|
|
334
374
|
maxInputTokens={maxInputTokens}
|
|
335
375
|
/>
|
|
@@ -5,6 +5,7 @@ export interface LoadingIndicatorProps {
|
|
|
5
5
|
isLoading?: boolean;
|
|
6
6
|
isCommandRunning?: boolean;
|
|
7
7
|
isCompacting?: boolean;
|
|
8
|
+
isGoalEvaluating?: boolean;
|
|
8
9
|
latestTotalTokens?: number;
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -12,11 +13,12 @@ export const LoadingIndicator = ({
|
|
|
12
13
|
isLoading = false,
|
|
13
14
|
isCommandRunning = false,
|
|
14
15
|
isCompacting = false,
|
|
16
|
+
isGoalEvaluating = false,
|
|
15
17
|
latestTotalTokens = 0,
|
|
16
18
|
}: LoadingIndicatorProps) => {
|
|
17
19
|
return (
|
|
18
20
|
<Box flexDirection="column">
|
|
19
|
-
{isLoading && !isCompacting && (
|
|
21
|
+
{isLoading && !isCompacting && !isGoalEvaluating && (
|
|
20
22
|
<Box>
|
|
21
23
|
<Text color="yellow">✻ AI is thinking... </Text>
|
|
22
24
|
{latestTotalTokens > 0 && (
|
|
@@ -49,6 +51,7 @@ export const LoadingIndicator = ({
|
|
|
49
51
|
{isCompacting && (
|
|
50
52
|
<Text color="magenta">✻ Compacting message history...</Text>
|
|
51
53
|
)}
|
|
54
|
+
{isGoalEvaluating && <Text color="cyan">✻ Evaluating goal...</Text>}
|
|
52
55
|
</Box>
|
|
53
56
|
);
|
|
54
57
|
};
|
|
@@ -18,15 +18,19 @@ export const QueuedMessageList: React.FC = () => {
|
|
|
18
18
|
const displayText = prefix + (content || (hasImages ? "[Images]" : ""));
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
|
-
<Box key={index}>
|
|
21
|
+
<Box key={msg.id ?? index}>
|
|
22
|
+
<Text color="gray">[{index + 1}] </Text>
|
|
22
23
|
<Text color="gray" italic>
|
|
23
|
-
{displayText.length >
|
|
24
|
-
? `${displayText.substring(0,
|
|
24
|
+
{displayText.length > 55
|
|
25
|
+
? `${displayText.substring(0, 52)}...`
|
|
25
26
|
: displayText}
|
|
26
27
|
</Text>
|
|
27
28
|
</Box>
|
|
28
29
|
);
|
|
29
30
|
})}
|
|
31
|
+
<Text color="gray" dimColor>
|
|
32
|
+
{" "}↑ recall
|
|
33
|
+
</Text>
|
|
30
34
|
</Box>
|
|
31
35
|
);
|
|
32
36
|
};
|
|
@@ -5,6 +5,8 @@ export interface StatusLineProps {
|
|
|
5
5
|
permissionMode: string;
|
|
6
6
|
isShellCommand: boolean;
|
|
7
7
|
isBtwActive: boolean;
|
|
8
|
+
isGoalActive?: boolean;
|
|
9
|
+
goalElapsed?: string;
|
|
8
10
|
latestTotalTokens?: number;
|
|
9
11
|
maxInputTokens?: number;
|
|
10
12
|
}
|
|
@@ -13,6 +15,8 @@ export const StatusLine: React.FC<StatusLineProps> = ({
|
|
|
13
15
|
permissionMode,
|
|
14
16
|
isShellCommand,
|
|
15
17
|
isBtwActive,
|
|
18
|
+
isGoalActive,
|
|
19
|
+
goalElapsed,
|
|
16
20
|
latestTotalTokens = 0,
|
|
17
21
|
maxInputTokens = 128000,
|
|
18
22
|
}) => {
|
|
@@ -35,22 +39,30 @@ export const StatusLine: React.FC<StatusLineProps> = ({
|
|
|
35
39
|
Shell: <Text color="yellow">Run shell command</Text>
|
|
36
40
|
</Text>
|
|
37
41
|
) : (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
<Box>
|
|
43
|
+
{isGoalActive && (
|
|
44
|
+
<Text color="gray">
|
|
45
|
+
<Text color="cyan">◎ /goal</Text> active{" "}
|
|
46
|
+
{goalElapsed && <Text>({goalElapsed})</Text>} |{" "}
|
|
47
|
+
</Text>
|
|
48
|
+
)}
|
|
49
|
+
<Text color="gray">
|
|
50
|
+
Mode:{" "}
|
|
51
|
+
<Text
|
|
52
|
+
color={
|
|
53
|
+
permissionMode === "plan"
|
|
54
|
+
? "yellow"
|
|
55
|
+
: permissionMode === "bypassPermissions"
|
|
56
|
+
? "red"
|
|
57
|
+
: "cyan"
|
|
58
|
+
}
|
|
59
|
+
bold={permissionMode === "bypassPermissions"}
|
|
60
|
+
>
|
|
61
|
+
{permissionMode}
|
|
62
|
+
</Text>{" "}
|
|
63
|
+
(Shift+Tab to cycle)
|
|
64
|
+
</Text>
|
|
65
|
+
</Box>
|
|
54
66
|
)}
|
|
55
67
|
{percentage > 0 && (
|
|
56
68
|
<Text color={contextColor}>{percentage}% context</Text>
|