longer-agent 0.1.0
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/LICENSE +21 -0
- package/README.md +227 -0
- package/README.zh-CN.md +227 -0
- package/agent_templates/executor/agent.yaml +22 -0
- package/agent_templates/executor/system_prompt.md +17 -0
- package/agent_templates/explorer/agent.yaml +13 -0
- package/agent_templates/explorer/system_prompt.md +19 -0
- package/agent_templates/main/agent.yaml +7 -0
- package/agent_templates/main/system_prompt.md +45 -0
- package/configExample.yaml +83 -0
- package/dist/agents/agent.d.ts +79 -0
- package/dist/agents/agent.d.ts.map +1 -0
- package/dist/agents/agent.js +156 -0
- package/dist/agents/agent.js.map +1 -0
- package/dist/agents/tool-loop.d.ts +140 -0
- package/dist/agents/tool-loop.d.ts.map +1 -0
- package/dist/agents/tool-loop.js +465 -0
- package/dist/agents/tool-loop.js.map +1 -0
- package/dist/ask.d.ts +81 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +34 -0
- package/dist/ask.js.map +1 -0
- package/dist/auth/openai-oauth.d.ts +66 -0
- package/dist/auth/openai-oauth.d.ts.map +1 -0
- package/dist/auth/openai-oauth.js +640 -0
- package/dist/auth/openai-oauth.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +254 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +118 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +862 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +130 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +648 -0
- package/dist/config.js.map +1 -0
- package/dist/context-rendering.d.ts +69 -0
- package/dist/context-rendering.d.ts.map +1 -0
- package/dist/context-rendering.js +250 -0
- package/dist/context-rendering.js.map +1 -0
- package/dist/document-projection.d.ts +12 -0
- package/dist/document-projection.d.ts.map +1 -0
- package/dist/document-projection.js +75 -0
- package/dist/document-projection.js.map +1 -0
- package/dist/ephemeral-log.d.ts +15 -0
- package/dist/ephemeral-log.d.ts.map +1 -0
- package/dist/ephemeral-log.js +173 -0
- package/dist/ephemeral-log.js.map +1 -0
- package/dist/file-attach.d.ts +89 -0
- package/dist/file-attach.d.ts.map +1 -0
- package/dist/file-attach.js +571 -0
- package/dist/file-attach.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/init-wizard.d.ts +13 -0
- package/dist/init-wizard.d.ts.map +1 -0
- package/dist/init-wizard.js +328 -0
- package/dist/init-wizard.js.map +1 -0
- package/dist/log-entry.d.ts +104 -0
- package/dist/log-entry.d.ts.map +1 -0
- package/dist/log-entry.js +292 -0
- package/dist/log-entry.js.map +1 -0
- package/dist/log-projection.d.ts +73 -0
- package/dist/log-projection.d.ts.map +1 -0
- package/dist/log-projection.js +651 -0
- package/dist/log-projection.js.map +1 -0
- package/dist/mcp-client.d.ts +55 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +402 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-selection.d.ts +16 -0
- package/dist/model-selection.d.ts.map +1 -0
- package/dist/model-selection.js +181 -0
- package/dist/model-selection.js.map +1 -0
- package/dist/network-retry.d.ts +38 -0
- package/dist/network-retry.d.ts.map +1 -0
- package/dist/network-retry.js +140 -0
- package/dist/network-retry.js.map +1 -0
- package/dist/persistence.d.ts +104 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +644 -0
- package/dist/persistence.js.map +1 -0
- package/dist/primitives/context.d.ts +29 -0
- package/dist/primitives/context.d.ts.map +1 -0
- package/dist/primitives/context.js +85 -0
- package/dist/primitives/context.js.map +1 -0
- package/dist/progress.d.ts +51 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +229 -0
- package/dist/progress.js.map +1 -0
- package/dist/provider-presets.d.ts +34 -0
- package/dist/provider-presets.d.ts.map +1 -0
- package/dist/provider-presets.js +181 -0
- package/dist/provider-presets.js.map +1 -0
- package/dist/providers/anthropic.d.ts +32 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +450 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +135 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +104 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/glm.d.ts +18 -0
- package/dist/providers/glm.d.ts.map +1 -0
- package/dist/providers/glm.js +59 -0
- package/dist/providers/glm.js.map +1 -0
- package/dist/providers/kimi.d.ts +23 -0
- package/dist/providers/kimi.d.ts.map +1 -0
- package/dist/providers/kimi.js +89 -0
- package/dist/providers/kimi.js.map +1 -0
- package/dist/providers/minimax.d.ts +20 -0
- package/dist/providers/minimax.d.ts.map +1 -0
- package/dist/providers/minimax.js +192 -0
- package/dist/providers/minimax.js.map +1 -0
- package/dist/providers/openai-chat.d.ts +33 -0
- package/dist/providers/openai-chat.d.ts.map +1 -0
- package/dist/providers/openai-chat.js +543 -0
- package/dist/providers/openai-chat.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +26 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +443 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openrouter.d.ts +24 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +177 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/registry.d.ts +7 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +38 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/security/path.d.ts +51 -0
- package/dist/security/path.d.ts.map +1 -0
- package/dist/security/path.js +187 -0
- package/dist/security/path.js.map +1 -0
- package/dist/security/sensitive-files.d.ts +3 -0
- package/dist/security/sensitive-files.d.ts.map +1 -0
- package/dist/security/sensitive-files.js +41 -0
- package/dist/security/sensitive-files.js.map +1 -0
- package/dist/session.d.ts +446 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +4595 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +46 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +134 -0
- package/dist/settings.js.map +1 -0
- package/dist/show-context.d.ts +35 -0
- package/dist/show-context.d.ts.map +1 -0
- package/dist/show-context.js +320 -0
- package/dist/show-context.js.map +1 -0
- package/dist/skills/loader.d.ts +49 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +166 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/summarize-context.d.ts +29 -0
- package/dist/summarize-context.d.ts.map +1 -0
- package/dist/summarize-context.js +247 -0
- package/dist/summarize-context.js.map +1 -0
- package/dist/templates/loader.d.ts +104 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/loader.js +514 -0
- package/dist/templates/loader.js.map +1 -0
- package/dist/tools/basic.d.ts +29 -0
- package/dist/tools/basic.d.ts.map +1 -0
- package/dist/tools/basic.js +2079 -0
- package/dist/tools/basic.js.map +1 -0
- package/dist/tools/comm.d.ts +17 -0
- package/dist/tools/comm.d.ts.map +1 -0
- package/dist/tools/comm.js +192 -0
- package/dist/tools/comm.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +237 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +24 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +51 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tui/app.d.ts +35 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +1042 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/checkbox-picker.d.ts +35 -0
- package/dist/tui/checkbox-picker.d.ts.map +1 -0
- package/dist/tui/checkbox-picker.js +85 -0
- package/dist/tui/checkbox-picker.js.map +1 -0
- package/dist/tui/command-picker.d.ts +31 -0
- package/dist/tui/command-picker.d.ts.map +1 -0
- package/dist/tui/command-picker.js +113 -0
- package/dist/tui/command-picker.js.map +1 -0
- package/dist/tui/components/ask-panel.d.ts +21 -0
- package/dist/tui/components/ask-panel.d.ts.map +1 -0
- package/dist/tui/components/ask-panel.js +81 -0
- package/dist/tui/components/ask-panel.js.map +1 -0
- package/dist/tui/components/conversation-panel.d.ts +68 -0
- package/dist/tui/components/conversation-panel.d.ts.map +1 -0
- package/dist/tui/components/conversation-panel.js +611 -0
- package/dist/tui/components/conversation-panel.js.map +1 -0
- package/dist/tui/components/input-panel.d.ts +27 -0
- package/dist/tui/components/input-panel.d.ts.map +1 -0
- package/dist/tui/components/input-panel.js +725 -0
- package/dist/tui/components/input-panel.js.map +1 -0
- package/dist/tui/components/logo-panel.d.ts +14 -0
- package/dist/tui/components/logo-panel.d.ts.map +1 -0
- package/dist/tui/components/logo-panel.js +37 -0
- package/dist/tui/components/logo-panel.js.map +1 -0
- package/dist/tui/components/plan-panel.d.ts +10 -0
- package/dist/tui/components/plan-panel.d.ts.map +1 -0
- package/dist/tui/components/plan-panel.js +8 -0
- package/dist/tui/components/plan-panel.js.map +1 -0
- package/dist/tui/components/status-bar.d.ts +24 -0
- package/dist/tui/components/status-bar.d.ts.map +1 -0
- package/dist/tui/components/status-bar.js +80 -0
- package/dist/tui/components/status-bar.js.map +1 -0
- package/dist/tui/input/editor-state.d.ts +22 -0
- package/dist/tui/input/editor-state.d.ts.map +1 -0
- package/dist/tui/input/editor-state.js +157 -0
- package/dist/tui/input/editor-state.js.map +1 -0
- package/dist/tui/input/keymap.d.ts +3 -0
- package/dist/tui/input/keymap.d.ts.map +1 -0
- package/dist/tui/input/keymap.js +72 -0
- package/dist/tui/input/keymap.js.map +1 -0
- package/dist/tui/input/paste-slots.d.ts +17 -0
- package/dist/tui/input/paste-slots.d.ts.map +1 -0
- package/dist/tui/input/paste-slots.js +46 -0
- package/dist/tui/input/paste-slots.js.map +1 -0
- package/dist/tui/input/paste.d.ts +15 -0
- package/dist/tui/input/paste.d.ts.map +1 -0
- package/dist/tui/input/paste.js +35 -0
- package/dist/tui/input/paste.js.map +1 -0
- package/dist/tui/input/protocol.d.ts +9 -0
- package/dist/tui/input/protocol.d.ts.map +1 -0
- package/dist/tui/input/protocol.js +387 -0
- package/dist/tui/input/protocol.js.map +1 -0
- package/dist/tui/input/sanitize.d.ts +6 -0
- package/dist/tui/input/sanitize.d.ts.map +1 -0
- package/dist/tui/input/sanitize.js +20 -0
- package/dist/tui/input/sanitize.js.map +1 -0
- package/dist/tui/input/types.d.ts +18 -0
- package/dist/tui/input/types.d.ts.map +1 -0
- package/dist/tui/input/types.js +2 -0
- package/dist/tui/input/types.js.map +1 -0
- package/dist/tui/launch.d.ts +23 -0
- package/dist/tui/launch.d.ts.map +1 -0
- package/dist/tui/launch.js +104 -0
- package/dist/tui/launch.js.map +1 -0
- package/dist/tui/theme.d.ts +20 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +29 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/types.d.ts +136 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +9 -0
- package/dist/tui/types.js.map +1 -0
- package/package.json +76 -0
- package/prompts/sections/agents_md.md +23 -0
- package/prompts/sections/important_log.md +16 -0
- package/prompts/sections/system_mechanisms.md +18 -0
- package/prompts/tools/apply_patch.md +31 -0
- package/prompts/tools/ask.md +18 -0
- package/prompts/tools/bash.md +13 -0
- package/prompts/tools/bash_background.md +9 -0
- package/prompts/tools/bash_output.md +9 -0
- package/prompts/tools/check_status.md +3 -0
- package/prompts/tools/diff.md +5 -0
- package/prompts/tools/edit_file.md +11 -0
- package/prompts/tools/glob.md +7 -0
- package/prompts/tools/grep.md +20 -0
- package/prompts/tools/kill_agent.md +3 -0
- package/prompts/tools/kill_shell.md +5 -0
- package/prompts/tools/list_dir.md +5 -0
- package/prompts/tools/plan.md +252 -0
- package/prompts/tools/read_file.md +9 -0
- package/prompts/tools/show_context.md +12 -0
- package/prompts/tools/skill.md +7 -0
- package/prompts/tools/spawn_agent.md +195 -0
- package/prompts/tools/summarize_context.md +122 -0
- package/prompts/tools/test.md +5 -0
- package/prompts/tools/wait.md +17 -0
- package/prompts/tools/web_fetch.md +9 -0
- package/prompts/tools/web_search.md +5 -0
- package/prompts/tools/write_file.md +11 -0
- package/skills/.staging/.gitkeep +0 -0
- package/skills/explain-code/SKILL.md +15 -0
- package/skills/skill-manager/SKILL.md +83 -0
package/dist/tui/app.js
ADDED
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Root TUI application component.
|
|
4
|
+
*
|
|
5
|
+
* Manages conversation state, session turn execution with streaming,
|
|
6
|
+
* Ctrl+C handling, and delegates rendering to child components.
|
|
7
|
+
*
|
|
8
|
+
* Layout: pure vertical — LogoPanel → ConversationPanel → InputPanel → StatusBar.
|
|
9
|
+
* Activity state (thinking / tool calling / waiting) is shown in the
|
|
10
|
+
* StatusBar alongside model name and context token count.
|
|
11
|
+
*
|
|
12
|
+
* Within a single turn, reasoning and text segments are tracked
|
|
13
|
+
* independently so they interleave in chronological order (not pinned
|
|
14
|
+
* to the top). Sub-agent tool activity is folded into compact rollup
|
|
15
|
+
* blocks in the conversation flow.
|
|
16
|
+
*
|
|
17
|
+
* Key bindings:
|
|
18
|
+
* Enter Send message
|
|
19
|
+
* Ctrl+N Insert newline
|
|
20
|
+
* Ctrl+G Toggle markdown rendered/raw mode
|
|
21
|
+
* Ctrl+C Cancel current turn (first press) / exit (second press)
|
|
22
|
+
* Ctrl+L Clear progress lines
|
|
23
|
+
* Ctrl+Y Copy last assistant reply to clipboard
|
|
24
|
+
*/
|
|
25
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
26
|
+
import { Box, Text, useApp, useStdin } from "ink";
|
|
27
|
+
import { execSync } from "node:child_process";
|
|
28
|
+
import { StringDecoder } from "node:string_decoder";
|
|
29
|
+
import { LogoPanel } from "./components/logo-panel.js";
|
|
30
|
+
import { StatusBar } from "./components/status-bar.js";
|
|
31
|
+
import { ConversationPanel } from "./components/conversation-panel.js";
|
|
32
|
+
import { AskPanel } from "./components/ask-panel.js";
|
|
33
|
+
import { PlanPanel } from "./components/plan-panel.js";
|
|
34
|
+
import { InputPanel } from "./components/input-panel.js";
|
|
35
|
+
import { InputProtocolParser } from "./input/protocol.js";
|
|
36
|
+
import { mapInputEventToCommand } from "./input/keymap.js";
|
|
37
|
+
import { withValueAndCursor, insertText, moveLeft, moveRight, moveWordLeft, moveWordRight, moveHome, moveEnd, deleteBackward, deleteForward, deleteWordBackward, deleteWordForward, deleteToLineStart, deleteToLineEnd, } from "./input/editor-state.js";
|
|
38
|
+
import { saveLog } from "../persistence.js";
|
|
39
|
+
import { isCommandExitSignal } from "../commands.js";
|
|
40
|
+
import { formatDisplayModelName } from "../config.js";
|
|
41
|
+
import { projectToTuiEntries } from "../log-projection.js";
|
|
42
|
+
// ------------------------------------------------------------------
|
|
43
|
+
// Goodbye messages
|
|
44
|
+
// ------------------------------------------------------------------
|
|
45
|
+
const GOODBYE_MESSAGES = [
|
|
46
|
+
"Bye!", "Goodbye!", "See you later!", "Until next time!",
|
|
47
|
+
"Take care!", "Happy coding!", "Catch you later!",
|
|
48
|
+
"Peace out!", "So long!", "Off I go!", "Later, gator!",
|
|
49
|
+
];
|
|
50
|
+
const CUSTOM_EMPTY_HINT = 'Custom answer is empty. Please enter an answer first, or choose "Discuss further" instead.';
|
|
51
|
+
// ------------------------------------------------------------------
|
|
52
|
+
// Clipboard helper
|
|
53
|
+
// ------------------------------------------------------------------
|
|
54
|
+
function copyToClipboard(text) {
|
|
55
|
+
try {
|
|
56
|
+
execSync("pbcopy", { input: text, timeout: 2000 });
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ------------------------------------------------------------------
|
|
64
|
+
// Inline editor helper (reuses pure functions from editor-state.ts)
|
|
65
|
+
// ------------------------------------------------------------------
|
|
66
|
+
function applyInlineEdit(value, cursor, event) {
|
|
67
|
+
if (event.type === "insert" && typeof event.text === "string") {
|
|
68
|
+
return insertText(withValueAndCursor(value, cursor, null), event.text);
|
|
69
|
+
}
|
|
70
|
+
if (event.type !== "key")
|
|
71
|
+
return null;
|
|
72
|
+
const cmd = mapInputEventToCommand(event);
|
|
73
|
+
if (!cmd)
|
|
74
|
+
return null;
|
|
75
|
+
const state = withValueAndCursor(value, cursor, null);
|
|
76
|
+
switch (cmd) {
|
|
77
|
+
case "move_left": return moveLeft(state);
|
|
78
|
+
case "move_right": return moveRight(state);
|
|
79
|
+
case "move_word_left": return moveWordLeft(state);
|
|
80
|
+
case "move_word_right": return moveWordRight(state);
|
|
81
|
+
case "move_home": return moveHome(state);
|
|
82
|
+
case "move_end": return moveEnd(state);
|
|
83
|
+
case "delete_backward": return deleteBackward(state);
|
|
84
|
+
case "delete_forward": return deleteForward(state);
|
|
85
|
+
case "delete_word_backward": return deleteWordBackward(state);
|
|
86
|
+
case "delete_word_forward": return deleteWordForward(state);
|
|
87
|
+
case "delete_to_line_start": return deleteToLineStart(state);
|
|
88
|
+
case "delete_to_line_end": return deleteToLineEnd(state);
|
|
89
|
+
default: return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function App({ session, commandRegistry, store, onProgressCallback, }) {
|
|
93
|
+
const { exit } = useApp();
|
|
94
|
+
const { stdin, setRawMode, isRawModeSupported } = useStdin();
|
|
95
|
+
const [entries, setEntries] = useState(projectToTuiEntries([...(session.log ?? [])]));
|
|
96
|
+
const [processing, setProcessing] = useState(false);
|
|
97
|
+
const [inputHint, setInputHint] = useState(null);
|
|
98
|
+
const [markdownMode, setMarkdownMode] = useState("rendered");
|
|
99
|
+
const [hideProgress, setHideProgress] = useState(false);
|
|
100
|
+
// ---- Status bar state ----
|
|
101
|
+
const [activityPhase, setActivityPhase] = useState("idle");
|
|
102
|
+
const [activityToolName, setActivityToolName] = useState();
|
|
103
|
+
const [statusError, setStatusError] = useState(false);
|
|
104
|
+
const [contextTokens, setContextTokens] = useState(0);
|
|
105
|
+
const [cacheReadTokens, setCacheReadTokens] = useState(0);
|
|
106
|
+
const [pendingAsk, setPendingAsk] = useState(typeof session.getPendingAsk === "function" ? session.getPendingAsk() : null);
|
|
107
|
+
const [askError, setAskError] = useState(null);
|
|
108
|
+
const [askSelectionIndex, setAskSelectionIndex] = useState(0);
|
|
109
|
+
// Agent question state
|
|
110
|
+
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
111
|
+
const [questionAnswers, setQuestionAnswers] = useState(new Map());
|
|
112
|
+
const [customInputMode, setCustomInputMode] = useState(false);
|
|
113
|
+
const [noteInputMode, setNoteInputMode] = useState(false);
|
|
114
|
+
// Shared inline editor for custom input and note input (mutually exclusive)
|
|
115
|
+
const [inlineEditor, setInlineEditor] = useState({ value: "", cursor: 0 });
|
|
116
|
+
// Per-option note drafts, keyed by "questionIndex-optionIndex"
|
|
117
|
+
const [optionNotes, setOptionNotes] = useState(new Map());
|
|
118
|
+
// Review mode: show summary of all answers before submitting (multi-question only)
|
|
119
|
+
const [reviewMode, setReviewMode] = useState(false);
|
|
120
|
+
// Plan panel state
|
|
121
|
+
const [planCheckpoints, setPlanCheckpoints] = useState(null);
|
|
122
|
+
const cancelledRef = useRef(false);
|
|
123
|
+
const lastCtrlCRef = useRef(0);
|
|
124
|
+
const abortControllerRef = useRef(null);
|
|
125
|
+
const inputPanelRef = useRef(null);
|
|
126
|
+
const shortcutParserRef = useRef(new InputProtocolParser());
|
|
127
|
+
const shortcutDecoderRef = useRef(new StringDecoder("utf8"));
|
|
128
|
+
const inputHintTimerRef = useRef(null);
|
|
129
|
+
const markdownModeInitializedRef = useRef(false);
|
|
130
|
+
const runTurnRef = useRef(null);
|
|
131
|
+
const runManualSummarizeRef = useRef(null);
|
|
132
|
+
const runManualCompactRef = useRef(null);
|
|
133
|
+
// Raw mode
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (!isRawModeSupported)
|
|
136
|
+
return;
|
|
137
|
+
setRawMode(true);
|
|
138
|
+
return () => {
|
|
139
|
+
setRawMode(false);
|
|
140
|
+
};
|
|
141
|
+
}, [isRawModeSupported, setRawMode]);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const syncFromLog = () => {
|
|
144
|
+
const projected = projectToTuiEntries([...(session.log ?? [])]);
|
|
145
|
+
setEntries(hideProgress
|
|
146
|
+
? projected.filter((e) => e.kind !== "progress" &&
|
|
147
|
+
e.kind !== "sub_agent_rollup" &&
|
|
148
|
+
e.kind !== "sub_agent_done")
|
|
149
|
+
: projected);
|
|
150
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
151
|
+
setContextTokens(session.lastInputTokens);
|
|
152
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
153
|
+
};
|
|
154
|
+
syncFromLog();
|
|
155
|
+
if (typeof session.subscribeLog !== "function")
|
|
156
|
+
return;
|
|
157
|
+
// Throttle log listener to limit TUI refresh rate (min 200ms between renders)
|
|
158
|
+
let lastCallTime = 0;
|
|
159
|
+
let pendingTimer = null;
|
|
160
|
+
const throttledSync = () => {
|
|
161
|
+
const now = Date.now();
|
|
162
|
+
const elapsed = now - lastCallTime;
|
|
163
|
+
if (elapsed >= 200) {
|
|
164
|
+
lastCallTime = now;
|
|
165
|
+
syncFromLog();
|
|
166
|
+
}
|
|
167
|
+
else if (!pendingTimer) {
|
|
168
|
+
pendingTimer = setTimeout(() => {
|
|
169
|
+
pendingTimer = null;
|
|
170
|
+
lastCallTime = Date.now();
|
|
171
|
+
syncFromLog();
|
|
172
|
+
}, 200 - elapsed);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const unsub = session.subscribeLog(throttledSync);
|
|
176
|
+
return () => {
|
|
177
|
+
unsub();
|
|
178
|
+
if (pendingTimer)
|
|
179
|
+
clearTimeout(pendingTimer);
|
|
180
|
+
};
|
|
181
|
+
}, [session, hideProgress]);
|
|
182
|
+
// ------------------------------------------------------------------
|
|
183
|
+
// Input hint management
|
|
184
|
+
// ------------------------------------------------------------------
|
|
185
|
+
const clearInputHint = useCallback(() => {
|
|
186
|
+
if (inputHintTimerRef.current) {
|
|
187
|
+
clearTimeout(inputHintTimerRef.current);
|
|
188
|
+
inputHintTimerRef.current = null;
|
|
189
|
+
}
|
|
190
|
+
setInputHint(null);
|
|
191
|
+
}, []);
|
|
192
|
+
const showInputHint = useCallback((message, durationMs = 2000) => {
|
|
193
|
+
if (inputHintTimerRef.current) {
|
|
194
|
+
clearTimeout(inputHintTimerRef.current);
|
|
195
|
+
inputHintTimerRef.current = null;
|
|
196
|
+
}
|
|
197
|
+
setInputHint(message);
|
|
198
|
+
inputHintTimerRef.current = setTimeout(() => {
|
|
199
|
+
inputHintTimerRef.current = null;
|
|
200
|
+
setInputHint(null);
|
|
201
|
+
}, durationMs);
|
|
202
|
+
}, []);
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
return () => {
|
|
205
|
+
if (inputHintTimerRef.current) {
|
|
206
|
+
clearTimeout(inputHintTimerRef.current);
|
|
207
|
+
inputHintTimerRef.current = null;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}, []);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!markdownModeInitializedRef.current) {
|
|
213
|
+
markdownModeInitializedRef.current = true;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
showInputHint(markdownMode === "raw" ? "Markdown raw: ON" : "Markdown raw: OFF");
|
|
217
|
+
}, [markdownMode, showInputHint]);
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
setAskSelectionIndex(0);
|
|
220
|
+
setCurrentQuestionIndex(0);
|
|
221
|
+
setQuestionAnswers(new Map());
|
|
222
|
+
setCustomInputMode(false);
|
|
223
|
+
setNoteInputMode(false);
|
|
224
|
+
setInlineEditor({ value: "", cursor: 0 });
|
|
225
|
+
setOptionNotes(new Map());
|
|
226
|
+
setReviewMode(false);
|
|
227
|
+
}, [pendingAsk?.id]);
|
|
228
|
+
// ------------------------------------------------------------------
|
|
229
|
+
// Auto-save
|
|
230
|
+
// ------------------------------------------------------------------
|
|
231
|
+
const autoSave = useCallback(() => {
|
|
232
|
+
if (!store)
|
|
233
|
+
return;
|
|
234
|
+
try {
|
|
235
|
+
if (typeof session.getLogForPersistence === "function" && store.sessionDir) {
|
|
236
|
+
const { meta, entries } = session.getLogForPersistence();
|
|
237
|
+
if (meta.turnCount === 0)
|
|
238
|
+
return; // Don't save empty sessions
|
|
239
|
+
saveLog(store.sessionDir, meta, entries);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Auto-save failed silently
|
|
244
|
+
}
|
|
245
|
+
}, [session, store]);
|
|
246
|
+
const runPendingTurn = useCallback(async () => {
|
|
247
|
+
if (typeof session.resumePendingTurn !== "function") {
|
|
248
|
+
setAskError("Current session does not support resuming pending asks.");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
cancelledRef.current = false;
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
abortControllerRef.current = controller;
|
|
254
|
+
setProcessing(true);
|
|
255
|
+
setActivityPhase("working");
|
|
256
|
+
setActivityToolName(undefined);
|
|
257
|
+
setStatusError(false);
|
|
258
|
+
try {
|
|
259
|
+
await session.resumePendingTurn({ signal: controller.signal });
|
|
260
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
261
|
+
autoSave();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
setActivityPhase("idle");
|
|
265
|
+
setActivityToolName(undefined);
|
|
266
|
+
setContextTokens(session.lastInputTokens);
|
|
267
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
268
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
269
|
+
autoSave();
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
273
|
+
autoSave();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
277
|
+
session.appendErrorMessage?.(msg, "resume_pending_turn");
|
|
278
|
+
setStatusError(true);
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
abortControllerRef.current = null;
|
|
282
|
+
inputPanelRef.current?.resetTurnPasteCounter();
|
|
283
|
+
if (!cancelledRef.current) {
|
|
284
|
+
setProcessing(false);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}, [session, autoSave]);
|
|
288
|
+
// Wire incremental save callback into Session
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
session.onSaveRequest = autoSave;
|
|
291
|
+
return () => { session.onSaveRequest = undefined; };
|
|
292
|
+
}, [session, autoSave]);
|
|
293
|
+
const performExit = useCallback(async () => {
|
|
294
|
+
clearInputHint();
|
|
295
|
+
autoSave();
|
|
296
|
+
try {
|
|
297
|
+
await session.close();
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// ignore close failures during shutdown
|
|
301
|
+
}
|
|
302
|
+
const msg = GOODBYE_MESSAGES[Math.floor(Math.random() * GOODBYE_MESSAGES.length)];
|
|
303
|
+
if (isRawModeSupported) {
|
|
304
|
+
setRawMode(false);
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
process.stdout.write(`\n${msg}\n`);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
console.log(msg);
|
|
311
|
+
}
|
|
312
|
+
exit();
|
|
313
|
+
}, [clearInputHint, autoSave, session, isRawModeSupported, setRawMode, exit]);
|
|
314
|
+
// ------------------------------------------------------------------
|
|
315
|
+
// Command context builder
|
|
316
|
+
// ------------------------------------------------------------------
|
|
317
|
+
const buildCommandContext = useCallback(() => {
|
|
318
|
+
return {
|
|
319
|
+
session,
|
|
320
|
+
store: store ?? undefined,
|
|
321
|
+
commandRegistry,
|
|
322
|
+
showMessage: (msg) => {
|
|
323
|
+
if (typeof session.appendStatusMessage === "function") {
|
|
324
|
+
session.appendStatusMessage(msg);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
showInputHint(msg, 2500);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
autoSave,
|
|
331
|
+
resetUiState: () => {
|
|
332
|
+
cancelledRef.current = false;
|
|
333
|
+
setProcessing(false);
|
|
334
|
+
// Reset token count on /new
|
|
335
|
+
setContextTokens(0);
|
|
336
|
+
setCacheReadTokens(0);
|
|
337
|
+
setActivityPhase("idle");
|
|
338
|
+
setActivityToolName(undefined);
|
|
339
|
+
setStatusError(false);
|
|
340
|
+
setPendingAsk(null);
|
|
341
|
+
setAskError(null);
|
|
342
|
+
setHideProgress(false);
|
|
343
|
+
},
|
|
344
|
+
exit: performExit,
|
|
345
|
+
onTurnRequested: (content) => {
|
|
346
|
+
runTurnRef.current?.(content);
|
|
347
|
+
},
|
|
348
|
+
onManualSummarizeRequested: (instruction) => {
|
|
349
|
+
runManualSummarizeRef.current?.(instruction);
|
|
350
|
+
},
|
|
351
|
+
onManualCompactRequested: (instruction) => {
|
|
352
|
+
runManualCompactRef.current?.(instruction);
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}, [session, store, commandRegistry, autoSave, performExit, showInputHint]);
|
|
356
|
+
// ------------------------------------------------------------------
|
|
357
|
+
// Progress callback (streaming)
|
|
358
|
+
// ------------------------------------------------------------------
|
|
359
|
+
const handleProgress = useCallback((event) => {
|
|
360
|
+
if (cancelledRef.current)
|
|
361
|
+
return;
|
|
362
|
+
const hasSubAgentId = event.extra?.["sub_agent_id"] !== undefined;
|
|
363
|
+
// ---- Status bar activity updates (primary agent only) ----
|
|
364
|
+
if (!hasSubAgentId) {
|
|
365
|
+
switch (event.action) {
|
|
366
|
+
case "reasoning_chunk":
|
|
367
|
+
setActivityPhase("thinking");
|
|
368
|
+
setActivityToolName(undefined);
|
|
369
|
+
break;
|
|
370
|
+
case "text_chunk":
|
|
371
|
+
setActivityPhase("generating");
|
|
372
|
+
setActivityToolName(undefined);
|
|
373
|
+
break;
|
|
374
|
+
case "tool_call":
|
|
375
|
+
setActivityPhase("tool_calling");
|
|
376
|
+
setActivityToolName(event.extra?.["tool"] ?? undefined);
|
|
377
|
+
break;
|
|
378
|
+
case "agent_no_reply":
|
|
379
|
+
setActivityPhase("waiting");
|
|
380
|
+
setActivityToolName(undefined);
|
|
381
|
+
break;
|
|
382
|
+
case "agent_end":
|
|
383
|
+
setContextTokens(session.lastInputTokens);
|
|
384
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
385
|
+
break;
|
|
386
|
+
case "token_update":
|
|
387
|
+
// Real-time token count update after each provider call
|
|
388
|
+
setContextTokens(event.extra?.["input_tokens"] ?? session.lastInputTokens);
|
|
389
|
+
setCacheReadTokens(event.extra?.["cache_read_tokens"] ?? 0);
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ---- Conversation entry routing ----
|
|
394
|
+
if (event.action === "ask_requested") {
|
|
395
|
+
const ask = event.extra?.["ask"] ?? session.getPendingAsk?.() ?? null;
|
|
396
|
+
setPendingAsk(ask);
|
|
397
|
+
setAskError(null);
|
|
398
|
+
setActivityPhase("waiting");
|
|
399
|
+
setActivityToolName(undefined);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (event.action === "ask_resolved") {
|
|
403
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
404
|
+
setAskError(null);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
// ---- Plan panel events ----
|
|
408
|
+
if (event.action === "plan_submit" || event.action === "plan_update") {
|
|
409
|
+
const cps = event.extra?.["checkpoints"];
|
|
410
|
+
if (cps)
|
|
411
|
+
setPlanCheckpoints(cps);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (event.action === "plan_finish") {
|
|
415
|
+
setPlanCheckpoints(null);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
}, [session]);
|
|
419
|
+
// Register progress callback on mount
|
|
420
|
+
useEffect(() => {
|
|
421
|
+
onProgressCallback(handleProgress);
|
|
422
|
+
}, [handleProgress, onProgressCallback]);
|
|
423
|
+
// ------------------------------------------------------------------
|
|
424
|
+
// Turn execution
|
|
425
|
+
// ------------------------------------------------------------------
|
|
426
|
+
const runTurn = useCallback(async (userInput) => {
|
|
427
|
+
cancelledRef.current = false;
|
|
428
|
+
const controller = new AbortController();
|
|
429
|
+
abortControllerRef.current = controller;
|
|
430
|
+
setProcessing(true);
|
|
431
|
+
setActivityPhase("working");
|
|
432
|
+
setActivityToolName(undefined);
|
|
433
|
+
setStatusError(false);
|
|
434
|
+
try {
|
|
435
|
+
await session.turn(userInput, { signal: controller.signal });
|
|
436
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
437
|
+
autoSave();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
setActivityPhase("idle");
|
|
441
|
+
setActivityToolName(undefined);
|
|
442
|
+
setContextTokens(session.lastInputTokens);
|
|
443
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
444
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
445
|
+
autoSave();
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
449
|
+
autoSave();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
453
|
+
session.appendErrorMessage?.(msg, "turn");
|
|
454
|
+
setStatusError(true);
|
|
455
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
456
|
+
}
|
|
457
|
+
finally {
|
|
458
|
+
abortControllerRef.current = null;
|
|
459
|
+
inputPanelRef.current?.resetTurnPasteCounter();
|
|
460
|
+
if (!cancelledRef.current) {
|
|
461
|
+
setProcessing(false);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}, [session, autoSave]);
|
|
465
|
+
runTurnRef.current = runTurn;
|
|
466
|
+
const runManualSummarize = useCallback(async (instruction) => {
|
|
467
|
+
if (typeof session.runManualSummarize !== "function") {
|
|
468
|
+
session.appendErrorMessage?.("Current session does not support /summarize.", "command");
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
cancelledRef.current = false;
|
|
472
|
+
const controller = new AbortController();
|
|
473
|
+
abortControllerRef.current = controller;
|
|
474
|
+
setProcessing(true);
|
|
475
|
+
setActivityPhase("working");
|
|
476
|
+
setActivityToolName(undefined);
|
|
477
|
+
setStatusError(false);
|
|
478
|
+
try {
|
|
479
|
+
await session.runManualSummarize(instruction, { signal: controller.signal });
|
|
480
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
481
|
+
autoSave();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
setActivityPhase("idle");
|
|
485
|
+
setActivityToolName(undefined);
|
|
486
|
+
setContextTokens(session.lastInputTokens);
|
|
487
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
488
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
489
|
+
autoSave();
|
|
490
|
+
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
493
|
+
autoSave();
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
497
|
+
session.appendErrorMessage?.(msg, "manual_summarize");
|
|
498
|
+
setStatusError(true);
|
|
499
|
+
}
|
|
500
|
+
finally {
|
|
501
|
+
abortControllerRef.current = null;
|
|
502
|
+
inputPanelRef.current?.resetTurnPasteCounter();
|
|
503
|
+
if (!cancelledRef.current) {
|
|
504
|
+
setProcessing(false);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}, [session, autoSave]);
|
|
508
|
+
runManualSummarizeRef.current = runManualSummarize;
|
|
509
|
+
const runManualCompact = useCallback(async (instruction) => {
|
|
510
|
+
if (typeof session.runManualCompact !== "function") {
|
|
511
|
+
session.appendErrorMessage?.("Current session does not support /compact.", "command");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
cancelledRef.current = false;
|
|
515
|
+
const controller = new AbortController();
|
|
516
|
+
abortControllerRef.current = controller;
|
|
517
|
+
setProcessing(true);
|
|
518
|
+
setActivityPhase("working");
|
|
519
|
+
setActivityToolName(undefined);
|
|
520
|
+
setStatusError(false);
|
|
521
|
+
try {
|
|
522
|
+
await session.runManualCompact(instruction, { signal: controller.signal });
|
|
523
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
524
|
+
autoSave();
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
setActivityPhase("idle");
|
|
528
|
+
setActivityToolName(undefined);
|
|
529
|
+
setContextTokens(session.lastInputTokens);
|
|
530
|
+
setCacheReadTokens(session.lastCacheReadTokens ?? 0);
|
|
531
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
532
|
+
autoSave();
|
|
533
|
+
}
|
|
534
|
+
catch (err) {
|
|
535
|
+
if (cancelledRef.current || controller.signal.aborted) {
|
|
536
|
+
autoSave();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
540
|
+
session.appendErrorMessage?.(msg, "manual_compact");
|
|
541
|
+
setStatusError(true);
|
|
542
|
+
}
|
|
543
|
+
finally {
|
|
544
|
+
abortControllerRef.current = null;
|
|
545
|
+
inputPanelRef.current?.resetTurnPasteCounter();
|
|
546
|
+
if (!cancelledRef.current) {
|
|
547
|
+
setProcessing(false);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}, [session, autoSave]);
|
|
551
|
+
runManualCompactRef.current = runManualCompact;
|
|
552
|
+
const resolveAgentQuestion = useCallback((answersOverride, notesOverride) => {
|
|
553
|
+
if (!pendingAsk || pendingAsk.kind !== "agent_question")
|
|
554
|
+
return;
|
|
555
|
+
const questions = pendingAsk.payload["questions"] ?? [];
|
|
556
|
+
const effectiveAnswers = answersOverride ?? questionAnswers;
|
|
557
|
+
const effectiveNotes = notesOverride ?? optionNotes;
|
|
558
|
+
for (let i = 0; i < questions.length; i++) {
|
|
559
|
+
if (!effectiveAnswers.has(i)) {
|
|
560
|
+
setReviewMode(false);
|
|
561
|
+
setCurrentQuestionIndex(i);
|
|
562
|
+
setAskSelectionIndex(0);
|
|
563
|
+
setAskError("Please answer all questions before continuing.");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const answers = [];
|
|
568
|
+
for (let i = 0; i < questions.length; i++) {
|
|
569
|
+
const qa = effectiveAnswers.get(i);
|
|
570
|
+
const agentOptions = questions[i].options;
|
|
571
|
+
const selectedOption = agentOptions[qa.optionIndex];
|
|
572
|
+
if (!selectedOption) {
|
|
573
|
+
setReviewMode(false);
|
|
574
|
+
setCurrentQuestionIndex(i);
|
|
575
|
+
setAskSelectionIndex(0);
|
|
576
|
+
setAskError("Selected answer is out of range.");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
// Look up note for this question's selected option
|
|
580
|
+
const note = effectiveNotes.get(`${i}-${qa.optionIndex}`) || undefined;
|
|
581
|
+
answers.push({
|
|
582
|
+
questionIndex: i,
|
|
583
|
+
selectedOptionIndex: qa.optionIndex,
|
|
584
|
+
answerText: selectedOption.kind === "custom_input"
|
|
585
|
+
? (qa.customText ?? "")
|
|
586
|
+
: selectedOption.label,
|
|
587
|
+
note,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
const decision = { answers };
|
|
591
|
+
try {
|
|
592
|
+
if (typeof session.resolveAgentQuestionAsk === "function") {
|
|
593
|
+
session.resolveAgentQuestionAsk(pendingAsk.id, decision);
|
|
594
|
+
}
|
|
595
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
596
|
+
setAskError(null);
|
|
597
|
+
autoSave();
|
|
598
|
+
if (session.hasPendingTurnToResume?.()) {
|
|
599
|
+
void runPendingTurn();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
catch (err) {
|
|
603
|
+
setAskError(err instanceof Error ? err.message : String(err));
|
|
604
|
+
}
|
|
605
|
+
}, [pendingAsk, questionAnswers, optionNotes, session, autoSave, runPendingTurn]);
|
|
606
|
+
// Helper: confirm current question's highlighted option into questionAnswers.
|
|
607
|
+
// Returns the updated map (avoids stale-closure from async setState).
|
|
608
|
+
const confirmCurrentQuestion = useCallback((sel, extra) => {
|
|
609
|
+
const next = new Map(questionAnswers);
|
|
610
|
+
next.set(currentQuestionIndex, { optionIndex: sel, ...extra });
|
|
611
|
+
setQuestionAnswers(next);
|
|
612
|
+
return next;
|
|
613
|
+
}, [questionAnswers, currentQuestionIndex]);
|
|
614
|
+
// Helper: submit or enter review mode
|
|
615
|
+
const submitOrReview = useCallback((updated) => {
|
|
616
|
+
if (!pendingAsk || pendingAsk.kind !== "agent_question")
|
|
617
|
+
return;
|
|
618
|
+
const questions = pendingAsk.payload["questions"] ?? [];
|
|
619
|
+
const firstMissing = questions.findIndex((_, idx) => !updated.has(idx));
|
|
620
|
+
if (firstMissing !== -1) {
|
|
621
|
+
setReviewMode(false);
|
|
622
|
+
setCurrentQuestionIndex(firstMissing);
|
|
623
|
+
setAskSelectionIndex(0);
|
|
624
|
+
setAskError("Please answer all questions before reviewing.");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (questions.length > 1) {
|
|
628
|
+
// Multi-question: enter review mode
|
|
629
|
+
setAskError(null);
|
|
630
|
+
setReviewMode(true);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
// Single question: submit directly
|
|
634
|
+
resolveAgentQuestion(updated, optionNotes);
|
|
635
|
+
}
|
|
636
|
+
}, [pendingAsk, optionNotes, resolveAgentQuestion]);
|
|
637
|
+
const resolveSelectedPendingAsk = useCallback(() => {
|
|
638
|
+
if (!pendingAsk)
|
|
639
|
+
return;
|
|
640
|
+
// Handle agent_question: confirm current question option
|
|
641
|
+
if (pendingAsk.kind === "agent_question") {
|
|
642
|
+
const questions = pendingAsk.payload["questions"] ?? [];
|
|
643
|
+
const q = questions[currentQuestionIndex];
|
|
644
|
+
if (!q)
|
|
645
|
+
return;
|
|
646
|
+
const selectedOption = q.options[askSelectionIndex];
|
|
647
|
+
if (!selectedOption)
|
|
648
|
+
return;
|
|
649
|
+
if (selectedOption.kind === "custom_input") {
|
|
650
|
+
if (customInputMode) {
|
|
651
|
+
const customText = inlineEditor.value.trim();
|
|
652
|
+
if (!customText) {
|
|
653
|
+
showInputHint(CUSTOM_EMPTY_HINT, 5000);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
// Confirm custom input
|
|
657
|
+
const updated = confirmCurrentQuestion(askSelectionIndex, { customText });
|
|
658
|
+
setCustomInputMode(false);
|
|
659
|
+
setInlineEditor({ value: "", cursor: 0 });
|
|
660
|
+
if (currentQuestionIndex < questions.length - 1) {
|
|
661
|
+
setCurrentQuestionIndex((prev) => prev + 1);
|
|
662
|
+
setAskSelectionIndex(0);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
submitOrReview(updated);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
const existing = questionAnswers.get(currentQuestionIndex);
|
|
670
|
+
setCustomInputMode(true);
|
|
671
|
+
setInlineEditor({
|
|
672
|
+
value: existing?.optionIndex === askSelectionIndex ? (existing.customText ?? "") : "",
|
|
673
|
+
cursor: existing?.optionIndex === askSelectionIndex ? (existing.customText ?? "").length : 0,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
// Normal option selected (including "Discuss further")
|
|
679
|
+
const updated = confirmCurrentQuestion(askSelectionIndex);
|
|
680
|
+
if (currentQuestionIndex < questions.length - 1) {
|
|
681
|
+
setCurrentQuestionIndex((prev) => prev + 1);
|
|
682
|
+
setAskSelectionIndex(0);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
submitOrReview(updated);
|
|
686
|
+
}
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
setAskError(`Unsupported ask kind: ${pendingAsk.kind}`);
|
|
690
|
+
}, [pendingAsk, askSelectionIndex, currentQuestionIndex, customInputMode, inlineEditor, confirmCurrentQuestion, optionNotes, resolveAgentQuestion, submitOrReview]);
|
|
691
|
+
// ------------------------------------------------------------------
|
|
692
|
+
// Input handling
|
|
693
|
+
// ------------------------------------------------------------------
|
|
694
|
+
const handleSubmit = useCallback((input) => {
|
|
695
|
+
clearInputHint();
|
|
696
|
+
if (pendingAsk) {
|
|
697
|
+
if (pendingAsk.kind === "agent_question") {
|
|
698
|
+
showInputHint("Use ↑/↓ to select options, ←/→ to navigate questions, Enter to confirm.", 2500);
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
showInputHint(`Unsupported ask kind: ${pendingAsk.kind}`, 2500);
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
if (processing) {
|
|
705
|
+
if (!input.trim())
|
|
706
|
+
return false;
|
|
707
|
+
// Enqueue message for delivery via check_status
|
|
708
|
+
if (typeof session.deliverMessage === "function") {
|
|
709
|
+
session.deliverMessage("user", input);
|
|
710
|
+
session.appendStatusMessage?.(`[Queued user message]\n${input}`, "queued_user_message");
|
|
711
|
+
showInputHint("Message queued for delivery.");
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
showInputHint("Assistant is replying. Enter is temporarily disabled.");
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
// Slash command handling
|
|
718
|
+
if (input.startsWith("/")) {
|
|
719
|
+
const parts = input.split(/\s+/, 2);
|
|
720
|
+
const cmdName = parts[0];
|
|
721
|
+
const cmdArgs = input.slice(cmdName.length).trim();
|
|
722
|
+
const cmd = commandRegistry.lookup(cmdName);
|
|
723
|
+
if (cmd) {
|
|
724
|
+
const ctx = buildCommandContext();
|
|
725
|
+
cmd.handler(ctx, cmdArgs).then(() => {
|
|
726
|
+
setPendingAsk(session.getPendingAsk?.() ?? null);
|
|
727
|
+
setAskError(null);
|
|
728
|
+
}).catch((err) => {
|
|
729
|
+
if (isCommandExitSignal(err)) {
|
|
730
|
+
void performExit();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
734
|
+
session.appendErrorMessage?.(`Command failed (${cmdName}): ${message}`, "command");
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
session.appendErrorMessage?.(`Unknown command: ${cmdName}. Type /help for available commands.`, "command");
|
|
739
|
+
}
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
runTurn(input);
|
|
743
|
+
return true;
|
|
744
|
+
}, [
|
|
745
|
+
processing,
|
|
746
|
+
pendingAsk,
|
|
747
|
+
commandRegistry,
|
|
748
|
+
runTurn,
|
|
749
|
+
runPendingTurn,
|
|
750
|
+
buildCommandContext,
|
|
751
|
+
clearInputHint,
|
|
752
|
+
showInputHint,
|
|
753
|
+
performExit,
|
|
754
|
+
session,
|
|
755
|
+
]);
|
|
756
|
+
// ------------------------------------------------------------------
|
|
757
|
+
// Ctrl+C / Ctrl+L / Ctrl+Y handling
|
|
758
|
+
// ------------------------------------------------------------------
|
|
759
|
+
const handleCtrlC = useCallback(() => {
|
|
760
|
+
if (inputPanelRef.current?.dismissOverlay()) {
|
|
761
|
+
clearInputHint();
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const now = Date.now();
|
|
765
|
+
if (now - lastCtrlCRef.current < 2000) {
|
|
766
|
+
if (processing) {
|
|
767
|
+
const decision = session.requestTurnInterrupt
|
|
768
|
+
? session.requestTurnInterrupt()
|
|
769
|
+
: (session.cancelCurrentTurn?.(), { accepted: true });
|
|
770
|
+
if (decision.accepted) {
|
|
771
|
+
cancelledRef.current = true;
|
|
772
|
+
abortControllerRef.current?.abort();
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
void performExit();
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
lastCtrlCRef.current = now;
|
|
779
|
+
if (!processing && inputPanelRef.current?.getValue()?.trim()) {
|
|
780
|
+
clearInputHint();
|
|
781
|
+
inputPanelRef.current.clear();
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (processing) {
|
|
785
|
+
const decision = session.requestTurnInterrupt
|
|
786
|
+
? session.requestTurnInterrupt()
|
|
787
|
+
: (session.cancelCurrentTurn?.(), { accepted: true });
|
|
788
|
+
if (!decision.accepted) {
|
|
789
|
+
if (decision.reason === "compact_in_progress") {
|
|
790
|
+
showInputHint("Interrupt is disabled during compact phase");
|
|
791
|
+
}
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
cancelledRef.current = true;
|
|
795
|
+
abortControllerRef.current?.abort();
|
|
796
|
+
setProcessing(false);
|
|
797
|
+
setActivityPhase("idle");
|
|
798
|
+
setActivityToolName(undefined);
|
|
799
|
+
clearInputHint();
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
showInputHint("Press Ctrl+C again to exit");
|
|
803
|
+
}
|
|
804
|
+
}, [
|
|
805
|
+
processing,
|
|
806
|
+
clearInputHint,
|
|
807
|
+
showInputHint,
|
|
808
|
+
performExit,
|
|
809
|
+
]);
|
|
810
|
+
const handleCtrlL = useCallback(() => {
|
|
811
|
+
setHideProgress((prev) => {
|
|
812
|
+
const next = !prev;
|
|
813
|
+
showInputHint(next ? "Progress lines hidden" : "Progress lines shown");
|
|
814
|
+
return next;
|
|
815
|
+
});
|
|
816
|
+
}, [showInputHint]);
|
|
817
|
+
const handleCtrlY = useCallback(() => {
|
|
818
|
+
const lastReply = [...entries]
|
|
819
|
+
.reverse()
|
|
820
|
+
.find((e) => e.kind === "assistant");
|
|
821
|
+
if (lastReply) {
|
|
822
|
+
if (copyToClipboard(lastReply.text)) {
|
|
823
|
+
showInputHint("Copied last reply!");
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
showInputHint("Copy failed");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
showInputHint("No reply to copy");
|
|
831
|
+
}
|
|
832
|
+
}, [entries, showInputHint]);
|
|
833
|
+
const handleCtrlG = useCallback(() => {
|
|
834
|
+
setMarkdownMode((prev) => (prev === "rendered" ? "raw" : "rendered"));
|
|
835
|
+
}, []);
|
|
836
|
+
useEffect(() => {
|
|
837
|
+
if (!stdin)
|
|
838
|
+
return;
|
|
839
|
+
const onData = (data) => {
|
|
840
|
+
const chunk = typeof data === "string" ? data : shortcutDecoderRef.current.write(data);
|
|
841
|
+
const events = shortcutParserRef.current.push(chunk);
|
|
842
|
+
for (const event of events) {
|
|
843
|
+
// --- Review mode for multi-question ask ---
|
|
844
|
+
if (pendingAsk?.kind === "agent_question" && reviewMode) {
|
|
845
|
+
if (event.type !== "key")
|
|
846
|
+
continue;
|
|
847
|
+
if (event.key === "enter") {
|
|
848
|
+
// Confirm and submit
|
|
849
|
+
resolveAgentQuestion(questionAnswers, optionNotes);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (event.key === "escape") {
|
|
853
|
+
// Go back to last question
|
|
854
|
+
setReviewMode(false);
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
// Number keys 1-9: jump to that question for editing
|
|
858
|
+
const numMatch = /^[1-9]$/.exec(event.key);
|
|
859
|
+
if (numMatch) {
|
|
860
|
+
const qNum = parseInt(numMatch[0], 10) - 1;
|
|
861
|
+
const questions = pendingAsk.payload["questions"] ?? [];
|
|
862
|
+
if (qNum < questions.length) {
|
|
863
|
+
setReviewMode(false);
|
|
864
|
+
setCurrentQuestionIndex(qNum);
|
|
865
|
+
const existing = questionAnswers.get(qNum);
|
|
866
|
+
setAskSelectionIndex(existing?.optionIndex ?? 0);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
// --- Inline editor input (custom input / note input) ---
|
|
872
|
+
if (pendingAsk?.kind === "agent_question" && (customInputMode || noteInputMode)) {
|
|
873
|
+
// Enter = confirm, Escape = cancel — handle before editor
|
|
874
|
+
if (event.type === "key" && event.key === "enter") {
|
|
875
|
+
if (noteInputMode) {
|
|
876
|
+
// Save note + auto-confirm the highlighted option (Bug 1 fix)
|
|
877
|
+
const noteText = inlineEditor.value.trim();
|
|
878
|
+
const noteKey = `${currentQuestionIndex}-${askSelectionIndex}`;
|
|
879
|
+
setOptionNotes((prev) => {
|
|
880
|
+
const next = new Map(prev);
|
|
881
|
+
if (noteText) {
|
|
882
|
+
next.set(noteKey, noteText);
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
next.delete(noteKey);
|
|
886
|
+
}
|
|
887
|
+
return next;
|
|
888
|
+
});
|
|
889
|
+
// Also confirm the option that the note was added to
|
|
890
|
+
confirmCurrentQuestion(askSelectionIndex);
|
|
891
|
+
setNoteInputMode(false);
|
|
892
|
+
setInlineEditor({ value: "", cursor: 0 });
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
// customInputMode — confirm via resolveSelectedPendingAsk
|
|
896
|
+
resolveSelectedPendingAsk();
|
|
897
|
+
}
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
if (event.type === "key" && event.key === "escape") {
|
|
901
|
+
if (noteInputMode) {
|
|
902
|
+
setNoteInputMode(false);
|
|
903
|
+
}
|
|
904
|
+
if (customInputMode) {
|
|
905
|
+
setCustomInputMode(false);
|
|
906
|
+
}
|
|
907
|
+
setInlineEditor({ value: "", cursor: 0 });
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
// Delegate to inline editor (movement, deletion, insertion)
|
|
911
|
+
const result = applyInlineEdit(inlineEditor.value, inlineEditor.cursor, event);
|
|
912
|
+
if (result)
|
|
913
|
+
setInlineEditor({ value: result.value, cursor: result.cursor });
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (event.type !== "key")
|
|
917
|
+
continue;
|
|
918
|
+
if (pendingAsk) {
|
|
919
|
+
if (pendingAsk.kind === "agent_question") {
|
|
920
|
+
const questions = pendingAsk.payload["questions"] ?? [];
|
|
921
|
+
const q = questions[currentQuestionIndex];
|
|
922
|
+
const totalOpts = q?.options?.length ?? 0;
|
|
923
|
+
const agentOptionCount = q?.options?.filter((opt) => !opt.systemAdded).length ?? 0;
|
|
924
|
+
// --- Tab to add/edit note (only on agent options) ---
|
|
925
|
+
if (event.key === "tab" && askSelectionIndex < agentOptionCount) {
|
|
926
|
+
const noteKey = `${currentQuestionIndex}-${askSelectionIndex}`;
|
|
927
|
+
const existing = optionNotes.get(noteKey) ?? "";
|
|
928
|
+
setInlineEditor({ value: existing, cursor: existing.length });
|
|
929
|
+
setNoteInputMode(true);
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (event.key === "up" && totalOpts > 0) {
|
|
933
|
+
setAskSelectionIndex((prev) => (prev - 1 + totalOpts) % totalOpts);
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
if (event.key === "down" && totalOpts > 0) {
|
|
937
|
+
setAskSelectionIndex((prev) => (prev + 1) % totalOpts);
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
if (event.key === "left" && questions.length > 1) {
|
|
941
|
+
setCurrentQuestionIndex((prev) => Math.max(0, prev - 1));
|
|
942
|
+
setAskSelectionIndex(0);
|
|
943
|
+
setCustomInputMode(false);
|
|
944
|
+
setNoteInputMode(false);
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
if (event.key === "right" && questions.length > 1) {
|
|
948
|
+
// Auto-confirm any non-custom option before advancing.
|
|
949
|
+
if (q?.options?.[askSelectionIndex]?.kind !== "custom_input") {
|
|
950
|
+
confirmCurrentQuestion(askSelectionIndex);
|
|
951
|
+
}
|
|
952
|
+
setCurrentQuestionIndex((prev) => Math.min(questions.length - 1, prev + 1));
|
|
953
|
+
setAskSelectionIndex(0);
|
|
954
|
+
setCustomInputMode(false);
|
|
955
|
+
setNoteInputMode(false);
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
if (event.key === "enter") {
|
|
959
|
+
resolveSelectedPendingAsk();
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
const optionsLen = pendingAsk.options?.length ?? 0;
|
|
965
|
+
if (event.key === "up" && optionsLen > 0) {
|
|
966
|
+
setAskSelectionIndex((prev) => (prev - 1 + optionsLen) % optionsLen);
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
if (event.key === "down" && optionsLen > 0) {
|
|
970
|
+
setAskSelectionIndex((prev) => (prev + 1) % optionsLen);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
if (event.key === "enter") {
|
|
974
|
+
resolveSelectedPendingAsk();
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (event.key === "ctrl_c") {
|
|
979
|
+
handleCtrlC();
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (event.key === "ctrl_l") {
|
|
983
|
+
handleCtrlL();
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (event.key === "ctrl_y") {
|
|
987
|
+
handleCtrlY();
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
if (event.key === "ctrl_g") {
|
|
991
|
+
handleCtrlG();
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
stdin.on("data", onData);
|
|
997
|
+
return () => {
|
|
998
|
+
const tail = shortcutDecoderRef.current.end();
|
|
999
|
+
if (tail.length > 0) {
|
|
1000
|
+
const events = shortcutParserRef.current.push(tail);
|
|
1001
|
+
for (const event of events) {
|
|
1002
|
+
if (event.type !== "key")
|
|
1003
|
+
continue;
|
|
1004
|
+
if (event.key === "ctrl_c")
|
|
1005
|
+
handleCtrlC();
|
|
1006
|
+
if (event.key === "ctrl_l")
|
|
1007
|
+
handleCtrlL();
|
|
1008
|
+
if (event.key === "ctrl_y")
|
|
1009
|
+
handleCtrlY();
|
|
1010
|
+
if (event.key === "ctrl_g")
|
|
1011
|
+
handleCtrlG();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
stdin.off("data", onData);
|
|
1015
|
+
};
|
|
1016
|
+
}, [
|
|
1017
|
+
stdin,
|
|
1018
|
+
pendingAsk,
|
|
1019
|
+
resolveSelectedPendingAsk,
|
|
1020
|
+
resolveAgentQuestion,
|
|
1021
|
+
confirmCurrentQuestion,
|
|
1022
|
+
handleCtrlC,
|
|
1023
|
+
handleCtrlL,
|
|
1024
|
+
handleCtrlY,
|
|
1025
|
+
handleCtrlG,
|
|
1026
|
+
currentQuestionIndex,
|
|
1027
|
+
customInputMode,
|
|
1028
|
+
noteInputMode,
|
|
1029
|
+
reviewMode,
|
|
1030
|
+
inlineEditor,
|
|
1031
|
+
askSelectionIndex,
|
|
1032
|
+
questionAnswers,
|
|
1033
|
+
optionNotes,
|
|
1034
|
+
]);
|
|
1035
|
+
// ------------------------------------------------------------------
|
|
1036
|
+
// Render
|
|
1037
|
+
// ------------------------------------------------------------------
|
|
1038
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { children: " " }) }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(Text, { children: " " }), _jsx(LogoPanel, { cwd: process.cwd() })] }), _jsx(ConversationPanel, { entries: entries, markdownMode: markdownMode, streamingAssistantEntryId: null }), planCheckpoints ? _jsx(PlanPanel, { checkpoints: planCheckpoints }) : null, pendingAsk ? (_jsx(AskPanel, { ask: pendingAsk, error: askError, selectedIndex: askSelectionIndex, currentQuestionIndex: currentQuestionIndex, totalQuestions: pendingAsk.kind === "agent_question"
|
|
1039
|
+
? (pendingAsk.payload["questions"] ?? []).length
|
|
1040
|
+
: 1, questionAnswers: questionAnswers, customInputMode: customInputMode, noteInputMode: noteInputMode, reviewMode: reviewMode, inlineEditorValue: inlineEditor.value, inlineEditorCursor: inlineEditor.cursor, optionNotes: optionNotes })) : null, _jsx(InputPanel, { ref: inputPanelRef, onSubmit: handleSubmit, disabled: !!pendingAsk, commandRegistry: commandRegistry, store: store, hint: inputHint, onHintRequested: showInputHint, session: session }), _jsx(StatusBar, { phase: activityPhase, toolName: activityToolName, error: statusError, modelName: formatDisplayModelName(session.primaryAgent.modelConfig?.provider, session.primaryAgent.modelConfig?.model), contextTokens: contextTokens, contextLimit: session.primaryAgent.modelConfig?.contextLength, cacheReadTokens: cacheReadTokens })] }));
|
|
1041
|
+
}
|
|
1042
|
+
//# sourceMappingURL=app.js.map
|