add: session history viewer (#912)
- A new “/sessions” command is available for browsing previous sessions, as shown in the updated slash command list - The CLI now documents and parses a new “--history” flag to browse past sessions from the command line - A dedicated `SessionsOverlay` component loads session metadata and allows toggling between viewing and resuming sessions - When the sessions overlay is opened during a chat, selecting a session can either show the saved rollout or resume it
This commit is contained in:
0
codex-cli/>
Normal file
0
codex-cli/>
Normal file
@@ -14,6 +14,7 @@ import type { ReasoningEffort } from "openai/resources.mjs";
|
|||||||
|
|
||||||
import App from "./app";
|
import App from "./app";
|
||||||
import { runSinglePass } from "./cli-singlepass";
|
import { runSinglePass } from "./cli-singlepass";
|
||||||
|
import SessionsOverlay from "./components/sessions-overlay.js";
|
||||||
import { AgentLoop } from "./utils/agent/agent-loop";
|
import { AgentLoop } from "./utils/agent/agent-loop";
|
||||||
import { ReviewDecision } from "./utils/agent/review";
|
import { ReviewDecision } from "./utils/agent/review";
|
||||||
import { AutoApprovalMode } from "./utils/auto-approval-mode";
|
import { AutoApprovalMode } from "./utils/auto-approval-mode";
|
||||||
@@ -60,6 +61,7 @@ const cli = meow(
|
|||||||
-p, --provider <provider> Provider to use for completions (default: openai)
|
-p, --provider <provider> Provider to use for completions (default: openai)
|
||||||
-i, --image <path> Path(s) to image files to include as input
|
-i, --image <path> Path(s) to image files to include as input
|
||||||
-v, --view <rollout> Inspect a previously saved rollout instead of starting a session
|
-v, --view <rollout> Inspect a previously saved rollout instead of starting a session
|
||||||
|
--history Browse previous sessions
|
||||||
-q, --quiet Non-interactive mode that only prints the assistant's final output
|
-q, --quiet Non-interactive mode that only prints the assistant's final output
|
||||||
-c, --config Open the instructions file in your editor
|
-c, --config Open the instructions file in your editor
|
||||||
-w, --writable-root <path> Writable folder for sandbox in full-auto mode (can be specified multiple times)
|
-w, --writable-root <path> Writable folder for sandbox in full-auto mode (can be specified multiple times)
|
||||||
@@ -104,6 +106,7 @@ const cli = meow(
|
|||||||
help: { type: "boolean", aliases: ["h"] },
|
help: { type: "boolean", aliases: ["h"] },
|
||||||
version: { type: "boolean", description: "Print version and exit" },
|
version: { type: "boolean", description: "Print version and exit" },
|
||||||
view: { type: "string" },
|
view: { type: "string" },
|
||||||
|
history: { type: "boolean", description: "Browse previous sessions" },
|
||||||
model: { type: "string", aliases: ["m"] },
|
model: { type: "string", aliases: ["m"] },
|
||||||
provider: { type: "string", aliases: ["p"] },
|
provider: { type: "string", aliases: ["p"] },
|
||||||
image: { type: "string", isMultiple: true, aliases: ["i"] },
|
image: { type: "string", isMultiple: true, aliases: ["i"] },
|
||||||
@@ -261,7 +264,10 @@ let config = loadConfig(undefined, undefined, {
|
|||||||
isFullContext: fullContextMode,
|
isFullContext: fullContextMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
const prompt = cli.input[0];
|
// `prompt` can be updated later when the user resumes a previous session
|
||||||
|
// via the `--history` flag. Therefore it must be declared with `let` rather
|
||||||
|
// than `const`.
|
||||||
|
let prompt = cli.input[0];
|
||||||
const model = cli.flags.model ?? config.model;
|
const model = cli.flags.model ?? config.model;
|
||||||
const imagePaths = cli.flags.image;
|
const imagePaths = cli.flags.image;
|
||||||
const provider = cli.flags.provider ?? config.provider ?? "openai";
|
const provider = cli.flags.provider ?? config.provider ?? "openai";
|
||||||
@@ -355,6 +361,46 @@ if (
|
|||||||
|
|
||||||
let rollout: AppRollout | undefined;
|
let rollout: AppRollout | undefined;
|
||||||
|
|
||||||
|
// For --history, show session selector and optionally update prompt or rollout.
|
||||||
|
if (cli.flags.history) {
|
||||||
|
const result: { path: string; mode: "view" | "resume" } | null =
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const instance = render(
|
||||||
|
React.createElement(SessionsOverlay, {
|
||||||
|
onView: (p: string) => {
|
||||||
|
instance.unmount();
|
||||||
|
resolve({ path: p, mode: "view" });
|
||||||
|
},
|
||||||
|
onResume: (p: string) => {
|
||||||
|
instance.unmount();
|
||||||
|
resolve({ path: p, mode: "resume" });
|
||||||
|
},
|
||||||
|
onExit: () => {
|
||||||
|
instance.unmount();
|
||||||
|
resolve(null);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.mode === "view") {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(result.path, "utf-8");
|
||||||
|
rollout = JSON.parse(content) as AppRollout;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Error reading session file:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prompt = `Resume this session: ${result.path}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For --view, optionally load an existing rollout from disk, display it and exit.
|
// For --view, optionally load an existing rollout from disk, display it and exit.
|
||||||
if (cli.flags.view) {
|
if (cli.flags.view) {
|
||||||
const viewPath = cli.flags.view;
|
const viewPath = cli.flags.view;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export default function TerminalChatInput({
|
|||||||
openApprovalOverlay,
|
openApprovalOverlay,
|
||||||
openHelpOverlay,
|
openHelpOverlay,
|
||||||
openDiffOverlay,
|
openDiffOverlay,
|
||||||
|
openSessionsOverlay,
|
||||||
onCompact,
|
onCompact,
|
||||||
interruptAgent,
|
interruptAgent,
|
||||||
active,
|
active,
|
||||||
@@ -77,6 +78,7 @@ export default function TerminalChatInput({
|
|||||||
openApprovalOverlay: () => void;
|
openApprovalOverlay: () => void;
|
||||||
openHelpOverlay: () => void;
|
openHelpOverlay: () => void;
|
||||||
openDiffOverlay: () => void;
|
openDiffOverlay: () => void;
|
||||||
|
openSessionsOverlay: () => void;
|
||||||
onCompact: () => void;
|
onCompact: () => void;
|
||||||
interruptAgent: () => void;
|
interruptAgent: () => void;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@@ -280,6 +282,9 @@ export default function TerminalChatInput({
|
|||||||
case "/history":
|
case "/history":
|
||||||
openOverlay();
|
openOverlay();
|
||||||
break;
|
break;
|
||||||
|
case "/sessions":
|
||||||
|
openSessionsOverlay();
|
||||||
|
break;
|
||||||
case "/help":
|
case "/help":
|
||||||
openHelpOverlay();
|
openHelpOverlay();
|
||||||
break;
|
break;
|
||||||
@@ -484,6 +489,10 @@ export default function TerminalChatInput({
|
|||||||
setInput("");
|
setInput("");
|
||||||
openOverlay();
|
openOverlay();
|
||||||
return;
|
return;
|
||||||
|
} else if (inputValue === "/sessions") {
|
||||||
|
setInput("");
|
||||||
|
openSessionsOverlay();
|
||||||
|
return;
|
||||||
} else if (inputValue === "/help") {
|
} else if (inputValue === "/help") {
|
||||||
setInput("");
|
setInput("");
|
||||||
openHelpOverlay();
|
openHelpOverlay();
|
||||||
@@ -728,6 +737,7 @@ export default function TerminalChatInput({
|
|||||||
openModelOverlay,
|
openModelOverlay,
|
||||||
openHelpOverlay,
|
openHelpOverlay,
|
||||||
openDiffOverlay,
|
openDiffOverlay,
|
||||||
|
openSessionsOverlay,
|
||||||
history,
|
history,
|
||||||
onCompact,
|
onCompact,
|
||||||
skipNextSubmit,
|
skipNextSubmit,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AppRollout } from "../../app.js";
|
||||||
import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js";
|
import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js";
|
||||||
import type { CommandConfirmation } from "../../utils/agent/agent-loop.js";
|
import type { CommandConfirmation } from "../../utils/agent/agent-loop.js";
|
||||||
import type { AppConfig } from "../../utils/config.js";
|
import type { AppConfig } from "../../utils/config.js";
|
||||||
@@ -5,6 +6,7 @@ import type { ColorName } from "chalk";
|
|||||||
import type { ResponseItem } from "openai/resources/responses/responses.mjs";
|
import type { ResponseItem } from "openai/resources/responses/responses.mjs";
|
||||||
|
|
||||||
import TerminalChatInput from "./terminal-chat-input.js";
|
import TerminalChatInput from "./terminal-chat-input.js";
|
||||||
|
import TerminalChatPastRollout from "./terminal-chat-past-rollout.js";
|
||||||
import { TerminalChatToolCallCommand } from "./terminal-chat-tool-call-command.js";
|
import { TerminalChatToolCallCommand } from "./terminal-chat-tool-call-command.js";
|
||||||
import TerminalMessageHistory from "./terminal-message-history.js";
|
import TerminalMessageHistory from "./terminal-message-history.js";
|
||||||
import { formatCommandForDisplay } from "../../format-command.js";
|
import { formatCommandForDisplay } from "../../format-command.js";
|
||||||
@@ -32,7 +34,9 @@ import DiffOverlay from "../diff-overlay.js";
|
|||||||
import HelpOverlay from "../help-overlay.js";
|
import HelpOverlay from "../help-overlay.js";
|
||||||
import HistoryOverlay from "../history-overlay.js";
|
import HistoryOverlay from "../history-overlay.js";
|
||||||
import ModelOverlay from "../model-overlay.js";
|
import ModelOverlay from "../model-overlay.js";
|
||||||
|
import SessionsOverlay from "../sessions-overlay.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
|
import fs from "fs/promises";
|
||||||
import { Box, Text } from "ink";
|
import { Box, Text } from "ink";
|
||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
@@ -41,6 +45,7 @@ import { inspect } from "util";
|
|||||||
export type OverlayModeType =
|
export type OverlayModeType =
|
||||||
| "none"
|
| "none"
|
||||||
| "history"
|
| "history"
|
||||||
|
| "sessions"
|
||||||
| "model"
|
| "model"
|
||||||
| "approval"
|
| "approval"
|
||||||
| "help"
|
| "help"
|
||||||
@@ -191,6 +196,7 @@ export default function TerminalChat({
|
|||||||
submitConfirmation,
|
submitConfirmation,
|
||||||
} = useConfirmation();
|
} = useConfirmation();
|
||||||
const [overlayMode, setOverlayMode] = useState<OverlayModeType>("none");
|
const [overlayMode, setOverlayMode] = useState<OverlayModeType>("none");
|
||||||
|
const [viewRollout, setViewRollout] = useState<AppRollout | null>(null);
|
||||||
|
|
||||||
// Store the diff text when opening the diff overlay so the view isn’t
|
// Store the diff text when opening the diff overlay so the view isn’t
|
||||||
// recomputed on every re‑render while it is open.
|
// recomputed on every re‑render while it is open.
|
||||||
@@ -454,6 +460,16 @@ export default function TerminalChat({
|
|||||||
[items, model],
|
[items, model],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (viewRollout) {
|
||||||
|
return (
|
||||||
|
<TerminalChatPastRollout
|
||||||
|
fileOpener={config.fileOpener}
|
||||||
|
session={viewRollout.session}
|
||||||
|
items={viewRollout.items}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
@@ -509,6 +525,7 @@ export default function TerminalChat({
|
|||||||
openModelOverlay={() => setOverlayMode("model")}
|
openModelOverlay={() => setOverlayMode("model")}
|
||||||
openApprovalOverlay={() => setOverlayMode("approval")}
|
openApprovalOverlay={() => setOverlayMode("approval")}
|
||||||
openHelpOverlay={() => setOverlayMode("help")}
|
openHelpOverlay={() => setOverlayMode("help")}
|
||||||
|
openSessionsOverlay={() => setOverlayMode("sessions")}
|
||||||
openDiffOverlay={() => {
|
openDiffOverlay={() => {
|
||||||
const { isGitRepo, diff } = getGitDiff();
|
const { isGitRepo, diff } = getGitDiff();
|
||||||
let text: string;
|
let text: string;
|
||||||
@@ -568,6 +585,25 @@ export default function TerminalChat({
|
|||||||
{overlayMode === "history" && (
|
{overlayMode === "history" && (
|
||||||
<HistoryOverlay items={items} onExit={() => setOverlayMode("none")} />
|
<HistoryOverlay items={items} onExit={() => setOverlayMode("none")} />
|
||||||
)}
|
)}
|
||||||
|
{overlayMode === "sessions" && (
|
||||||
|
<SessionsOverlay
|
||||||
|
onView={async (p) => {
|
||||||
|
try {
|
||||||
|
const txt = await fs.readFile(p, "utf-8");
|
||||||
|
const data = JSON.parse(txt) as AppRollout;
|
||||||
|
setViewRollout(data);
|
||||||
|
setOverlayMode("none");
|
||||||
|
} catch {
|
||||||
|
setOverlayMode("none");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onResume={(p) => {
|
||||||
|
setOverlayMode("none");
|
||||||
|
setInitialPrompt(`Resume this session: ${p}`);
|
||||||
|
}}
|
||||||
|
onExit={() => setOverlayMode("none")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{overlayMode === "model" && (
|
{overlayMode === "model" && (
|
||||||
<ModelOverlay
|
<ModelOverlay
|
||||||
currentModel={model}
|
currentModel={model}
|
||||||
|
|||||||
130
codex-cli/src/components/sessions-overlay.tsx
Normal file
130
codex-cli/src/components/sessions-overlay.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import type { TypeaheadItem } from "./typeahead-overlay.js";
|
||||||
|
|
||||||
|
import TypeaheadOverlay from "./typeahead-overlay.js";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import { Box, Text, useInput } from "ink";
|
||||||
|
import os from "os";
|
||||||
|
import path from "path";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const SESSIONS_ROOT = path.join(os.homedir(), ".codex", "sessions");
|
||||||
|
|
||||||
|
export type SessionMeta = {
|
||||||
|
path: string;
|
||||||
|
timestamp: string;
|
||||||
|
userMessages: number;
|
||||||
|
toolCalls: number;
|
||||||
|
firstMessage: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function loadSessions(): Promise<Array<SessionMeta>> {
|
||||||
|
try {
|
||||||
|
const entries = await fs.readdir(SESSIONS_ROOT);
|
||||||
|
const sessions: Array<SessionMeta> = [];
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.endsWith(".json")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const filePath = path.join(SESSIONS_ROOT, entry);
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const content = await fs.readFile(filePath, "utf-8");
|
||||||
|
const data = JSON.parse(content) as {
|
||||||
|
session?: { timestamp?: string };
|
||||||
|
items?: Array<{
|
||||||
|
type: string;
|
||||||
|
role: string;
|
||||||
|
content: Array<{ text: string }>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
const firstUser = items.find(
|
||||||
|
(i) => i?.type === "message" && i.role === "user",
|
||||||
|
);
|
||||||
|
const firstText =
|
||||||
|
firstUser?.content?.[0]?.text?.replace(/\n/g, " ").slice(0, 16) ?? "";
|
||||||
|
const userMessages = items.filter(
|
||||||
|
(i) => i?.type === "message" && i.role === "user",
|
||||||
|
).length;
|
||||||
|
const toolCalls = items.filter(
|
||||||
|
(i) => i?.type === "function_call",
|
||||||
|
).length;
|
||||||
|
sessions.push({
|
||||||
|
path: filePath,
|
||||||
|
timestamp: data.session?.timestamp || "",
|
||||||
|
userMessages,
|
||||||
|
toolCalls,
|
||||||
|
firstMessage: firstText,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
/* ignore invalid session */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sessions.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
||||||
|
return sessions;
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onView: (sessionPath: string) => void;
|
||||||
|
onResume: (sessionPath: string) => void;
|
||||||
|
onExit: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SessionsOverlay({
|
||||||
|
onView,
|
||||||
|
onResume,
|
||||||
|
onExit,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const [items, setItems] = useState<Array<TypeaheadItem>>([]);
|
||||||
|
const [mode, setMode] = useState<"view" | "resume">("view");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const sessions = await loadSessions();
|
||||||
|
const formatted = sessions.map((s) => {
|
||||||
|
const ts = s.timestamp
|
||||||
|
? new Date(s.timestamp).toLocaleString(undefined, {
|
||||||
|
dateStyle: "short",
|
||||||
|
timeStyle: "short",
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
const first = s.firstMessage?.slice(0, 50);
|
||||||
|
const label = `${ts} · ${s.userMessages} msgs/${s.toolCalls} tools · ${first}`;
|
||||||
|
return { label, value: s.path } as TypeaheadItem;
|
||||||
|
});
|
||||||
|
setItems(formatted);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useInput((_input, key) => {
|
||||||
|
if (key.tab) {
|
||||||
|
setMode((m) => (m === "view" ? "resume" : "view"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TypeaheadOverlay
|
||||||
|
title={mode === "view" ? "View session" : "Resume session"}
|
||||||
|
description={
|
||||||
|
<Box flexDirection="column">
|
||||||
|
<Text>
|
||||||
|
{mode === "view" ? "press enter to view" : "press enter to resume"}
|
||||||
|
</Text>
|
||||||
|
<Text dimColor>tab to toggle mode · esc to cancel</Text>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
initialItems={items}
|
||||||
|
onSelect={(value) => {
|
||||||
|
if (mode === "view") {
|
||||||
|
onView(value);
|
||||||
|
} else {
|
||||||
|
onResume(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onExit={onExit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ export const SLASH_COMMANDS: Array<SlashCommand> = [
|
|||||||
"Clear conversation history but keep a summary in context. Optional: /compact [instructions for summarization]",
|
"Clear conversation history but keep a summary in context. Optional: /compact [instructions for summarization]",
|
||||||
},
|
},
|
||||||
{ command: "/history", description: "Open command history" },
|
{ command: "/history", description: "Open command history" },
|
||||||
|
{ command: "/sessions", description: "Browse previous sessions" },
|
||||||
{ command: "/help", description: "Show list of commands" },
|
{ command: "/help", description: "Show list of commands" },
|
||||||
{ command: "/model", description: "Open model selection panel" },
|
{ command: "/model", description: "Open model selection panel" },
|
||||||
{ command: "/approval", description: "Open approval mode selection panel" },
|
{ command: "/approval", description: "Open approval mode selection panel" },
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ describe("/clear command", () => {
|
|||||||
openApprovalOverlay: () => {},
|
openApprovalOverlay: () => {},
|
||||||
openHelpOverlay: () => {},
|
openHelpOverlay: () => {},
|
||||||
openDiffOverlay: () => {},
|
openDiffOverlay: () => {},
|
||||||
|
openSessionsOverlay: () => {},
|
||||||
onCompact: () => {},
|
onCompact: () => {},
|
||||||
interruptAgent: () => {},
|
interruptAgent: () => {},
|
||||||
active: true,
|
active: true,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ test("SLASH_COMMANDS includes expected commands", () => {
|
|||||||
expect(commands).toContain("/clear");
|
expect(commands).toContain("/clear");
|
||||||
expect(commands).toContain("/compact");
|
expect(commands).toContain("/compact");
|
||||||
expect(commands).toContain("/history");
|
expect(commands).toContain("/history");
|
||||||
|
expect(commands).toContain("/sessions");
|
||||||
expect(commands).toContain("/help");
|
expect(commands).toContain("/help");
|
||||||
expect(commands).toContain("/model");
|
expect(commands).toContain("/model");
|
||||||
expect(commands).toContain("/approval");
|
expect(commands).toContain("/approval");
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ describe("TerminalChatInput compact command", () => {
|
|||||||
openModelOverlay: () => {},
|
openModelOverlay: () => {},
|
||||||
openApprovalOverlay: () => {},
|
openApprovalOverlay: () => {},
|
||||||
openHelpOverlay: () => {},
|
openHelpOverlay: () => {},
|
||||||
|
openSessionsOverlay: () => {},
|
||||||
onCompact: () => {},
|
onCompact: () => {},
|
||||||
interruptAgent: () => {},
|
interruptAgent: () => {},
|
||||||
active: true,
|
active: true,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ describe("TerminalChatInput file tag suggestions", () => {
|
|||||||
openModelOverlay: vi.fn(),
|
openModelOverlay: vi.fn(),
|
||||||
openApprovalOverlay: vi.fn(),
|
openApprovalOverlay: vi.fn(),
|
||||||
openHelpOverlay: vi.fn(),
|
openHelpOverlay: vi.fn(),
|
||||||
|
openSessionsOverlay: vi.fn(),
|
||||||
onCompact: vi.fn(),
|
onCompact: vi.fn(),
|
||||||
interruptAgent: vi.fn(),
|
interruptAgent: vi.fn(),
|
||||||
active: true,
|
active: true,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ describe("TerminalChatInput multiline functionality", () => {
|
|||||||
openModelOverlay: () => {},
|
openModelOverlay: () => {},
|
||||||
openApprovalOverlay: () => {},
|
openApprovalOverlay: () => {},
|
||||||
openHelpOverlay: () => {},
|
openHelpOverlay: () => {},
|
||||||
|
openSessionsOverlay: () => {},
|
||||||
onCompact: () => {},
|
onCompact: () => {},
|
||||||
interruptAgent: () => {},
|
interruptAgent: () => {},
|
||||||
active: true,
|
active: true,
|
||||||
@@ -93,6 +94,7 @@ describe("TerminalChatInput multiline functionality", () => {
|
|||||||
openModelOverlay: () => {},
|
openModelOverlay: () => {},
|
||||||
openApprovalOverlay: () => {},
|
openApprovalOverlay: () => {},
|
||||||
openHelpOverlay: () => {},
|
openHelpOverlay: () => {},
|
||||||
|
openSessionsOverlay: () => {},
|
||||||
onCompact: () => {},
|
onCompact: () => {},
|
||||||
interruptAgent: () => {},
|
interruptAgent: () => {},
|
||||||
active: true,
|
active: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user