Skip to content

Commit b441fa1

Browse files
Merge pull request #78 from ai-action/feat/history
2 parents 22fde11 + 30c8369 commit b441fa1

4 files changed

Lines changed: 297 additions & 51 deletions

File tree

src/components/Chat/Chat.test.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import { prewarmCodeBlocks } from '../CodeBlock';
88

99
const mockState = vi.hoisted(() => ({
1010
handler: undefined as ((value: string) => void) | undefined,
11+
history: [] as string[],
1112
testInput: '',
1213
shouldReset: false,
1314
clear() {
1415
this.handler = undefined;
16+
this.history = [];
1517
this.testInput = '';
1618
this.shouldReset = true;
1719
},
@@ -92,8 +94,9 @@ vi.mock('../../utils', async () => ({
9294
},
9395
}));
9496

95-
vi.mock('./Input', () => ({
96-
Input: (props: {
97+
vi.mock('./ChatInput', () => ({
98+
ChatInput: (props: {
99+
history?: string[];
97100
onSubmit?: (value: string) => void;
98101
onInterrupt?: () => void;
99102
isDisabled?: boolean;
@@ -102,6 +105,8 @@ vi.mock('./Input', () => ({
102105
mockState.handler = props.onSubmit;
103106
}
104107

108+
mockState.history = props.history ?? [];
109+
105110
if (props.onInterrupt) {
106111
interruptState.handler = props.onInterrupt;
107112
}
@@ -216,6 +221,28 @@ describe('Chat', () => {
216221
expect(frame).toContain('hello');
217222
}, 10_000);
218223

224+
it('derives prompt history from user messages and excludes slash commands', async () => {
225+
render(
226+
<Chat
227+
initialMessages={[
228+
{ role: 'user', content: 'first prompt' },
229+
{ role: 'assistant', content: 'response' },
230+
{ role: 'user', content: '/model' },
231+
{ role: 'user', content: 'second prompt' },
232+
]}
233+
model="gemma4"
234+
onCommand={vi.fn()}
235+
mode={MODE.SAFE}
236+
onModeChange={onModeChange}
237+
sessionId="0"
238+
/>,
239+
);
240+
241+
await time.tick();
242+
243+
expect(mockState.history).toEqual(['first prompt', 'second prompt']);
244+
});
245+
219246
it('does not add blank messages', async () => {
220247
const chat = (
221248
<Chat

src/components/Chat/Chat.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Box, Text } from 'ink';
2-
import { useCallback, useEffect, useRef, useState } from 'react';
2+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
33

44
import { DECISION, MODE, PROMPT, ROLE } from '../../constants';
55
import type { Decision, Mode, ToolName, ToolResult } from '../../types';
@@ -9,13 +9,13 @@ import { Messages } from '../Messages';
99
import { TURN_ABORTED_MESSAGE } from '../Messages/constants';
1010
import { PlanApproval } from '../PlanApproval';
1111
import { ToolApproval } from '../ToolApproval';
12+
import { ChatInput } from './ChatInput';
1213
import {
1314
ACTION_NOT_PERFORMED,
1415
INTERRUPT_REASON,
1516
PLAN_CHECKLIST_REMINDER,
1617
PLAN_EXECUTION_REMINDER,
1718
} from './constants';
18-
import { Input } from './Input';
1919
import { hasExecutablePlan } from './plan';
2020

2121
interface Props {
@@ -38,6 +38,13 @@ export function Chat({
3838
sessionId,
3939
}: Props) {
4040
const sessionMessages = initialMessages ?? [];
41+
const history = useMemo(
42+
() =>
43+
sessionMessages.flatMap(({ role, content }) =>
44+
role === ROLE.USER && !content.startsWith('/') ? [content] : [],
45+
),
46+
[sessionMessages],
47+
);
4148
const [messages, setMessages] = useState<ollama.Message[]>(sessionMessages);
4249
const [streamingMessage, setStreamingMessage] =
4350
useState<ollama.Message | null>(null);
@@ -585,7 +592,8 @@ export function Chat({
585592

586593
{!pendingPlan && !pendingToolCall && (
587594
<Box marginTop={1}>
588-
<Input
595+
<ChatInput
596+
history={history}
589597
isDisabled={isLoading}
590598
onInterrupt={handleInterrupt}
591599
// eslint-disable-next-line @typescript-eslint/no-misused-promises

0 commit comments

Comments
 (0)