praxis-agent 0.46.0 → 0.46.1

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.
@@ -44,6 +44,7 @@ import { projectTuiCommandPalette, tuiCommandPaletteCommandId, } from './tui/com
44
44
  import { runDoctor, } from '../maintenance/doctor.js';
45
45
  import { canonicalClaudeCostModelName, formatCostSummary, } from './tui/cost-summary.js';
46
46
  import { createComposerEditor, deleteComposerBackward, deleteComposerForward, insertComposerText, moveComposerCursor, } from './tui/composer-editor.js';
47
+ import { createComposerPromptHistory, navigateComposerPromptHistory, recordComposerPrompt, resetComposerPromptHistoryNavigation, seedComposerPromptHistory, } from './tui/composer-prompt-history.js';
47
48
  import { routeComposerKey, } from './tui/composer-key-router.js';
48
49
  import { routeTuiInteraction, } from './tui/tui-interaction-router.js';
49
50
  import { currentTuiInteractionLayer, projectTuiFocusStack, } from './tui/tui-focus-stack.js';
@@ -239,6 +240,9 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
239
240
  'update',
240
241
  'usage',
241
242
  ]);
243
+ function transcriptPrompts(items) {
244
+ return items.flatMap((item) => (item.kind === 'user' ? [item.text] : []));
245
+ }
242
246
  /** Advances the React-owned transcript plus its exact local mutation fact. */
243
247
  export function advanceInteractiveHistoryState(current, items, changedFrom) {
244
248
  if (!Number.isInteger(changedFrom) ||
@@ -372,9 +376,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
372
376
  const [pendingFork, setPendingFork] = useState(resume?.forkSession === true);
373
377
  const inputRef = useRef('');
374
378
  const inputCursorRef = useRef(0);
375
- const inputHistoryRef = useRef([]);
376
- const inputHistoryIndexRef = useRef(null);
377
- const inputHistoryDraftRef = useRef('');
379
+ const promptHistoryRef = useRef(seedComposerPromptHistory(transcriptPrompts(initialHistory)));
378
380
  const [pendingInputs, setPendingInputs] = useState([]);
379
381
  const pendingInputsRef = useRef(pendingInputs);
380
382
  pendingInputsRef.current = pendingInputs;
@@ -1466,6 +1468,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
1466
1468
  fileSelectionRef.current = 0;
1467
1469
  };
1468
1470
  const updateComposerEditor = (editor, recordUndo = true) => {
1471
+ promptHistoryRef.current = resetComposerPromptHistoryNavigation(promptHistoryRef.current);
1469
1472
  if (recordUndo && editor.text !== inputRef.current) {
1470
1473
  undoStackRef.current = [
1471
1474
  ...undoStackRef.current,
@@ -1474,7 +1477,10 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
1474
1477
  }
1475
1478
  updateComposerInput(editor.text, editor.cursor);
1476
1479
  };
1477
- const clearComposerInput = () => updateComposerInput('');
1480
+ const clearComposerInput = () => {
1481
+ promptHistoryRef.current = resetComposerPromptHistoryNavigation(promptHistoryRef.current);
1482
+ updateComposerInput('');
1483
+ };
1478
1484
  const promptImages = (prompt) => {
1479
1485
  const seen = new Set();
1480
1486
  return composerImageIds(prompt).flatMap((id) => {
@@ -1706,36 +1712,13 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
1706
1712
  return () => clearInterval(timer);
1707
1713
  }, [menu?.kind]);
1708
1714
  const appendPromptHistory = (prompt) => {
1709
- if (!prompt)
1710
- return;
1711
- const history = inputHistoryRef.current.filter((item) => item !== prompt);
1712
- inputHistoryRef.current = [prompt, ...history].slice(0, 100);
1713
- inputHistoryIndexRef.current = null;
1714
- inputHistoryDraftRef.current = '';
1715
+ promptHistoryRef.current = recordComposerPrompt(promptHistoryRef.current, prompt);
1715
1716
  };
1716
1717
  const restorePromptHistory = (direction) => {
1717
- const history = inputHistoryRef.current;
1718
- if (history.length === 0)
1719
- return;
1720
- const currentIndex = inputHistoryIndexRef.current;
1721
- if (direction === 'previous') {
1722
- if (currentIndex === null)
1723
- inputHistoryDraftRef.current = inputRef.current;
1724
- const nextIndex = Math.min(history.length - 1, (currentIndex ?? -1) + 1);
1725
- inputHistoryIndexRef.current = nextIndex;
1726
- updateComposerInput(history[nextIndex] ?? '');
1727
- return;
1728
- }
1729
- if (currentIndex === null)
1730
- return;
1731
- const nextIndex = currentIndex - 1;
1732
- if (nextIndex < 0) {
1733
- inputHistoryIndexRef.current = null;
1734
- updateComposerInput(inputHistoryDraftRef.current);
1735
- return;
1736
- }
1737
- inputHistoryIndexRef.current = nextIndex;
1738
- updateComposerInput(history[nextIndex] ?? '');
1718
+ const transition = navigateComposerPromptHistory(promptHistoryRef.current, direction, createComposerEditor(inputRef.current, inputCursorRef.current));
1719
+ promptHistoryRef.current = transition.state;
1720
+ if (transition.editor !== null)
1721
+ updateComposerInput(transition.editor.text, transition.editor.cursor);
1739
1722
  };
1740
1723
  const withdrawLatestPending = () => {
1741
1724
  if (inputRef.current.trim().length !== 0)
@@ -2253,10 +2236,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2253
2236
  const loadId = sessionLoadRef.current + 1;
2254
2237
  sessionLoadRef.current = loadId;
2255
2238
  if (nextSessionId === null) {
2239
+ promptHistoryRef.current = createComposerPromptHistory();
2256
2240
  setHistory([]);
2257
2241
  setSessionColor(undefined);
2258
2242
  return;
2259
2243
  }
2244
+ promptHistoryRef.current = createComposerPromptHistory();
2260
2245
  const loading = (async () => {
2261
2246
  try {
2262
2247
  const commands = await service();
@@ -2266,6 +2251,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2266
2251
  : await commands.agentColor(nextSessionId);
2267
2252
  if (sessionLoadRef.current === loadId) {
2268
2253
  setHistory(transcript ? [...transcript] : []);
2254
+ promptHistoryRef.current = seedComposerPromptHistory(transcriptPrompts(transcript ?? []));
2269
2255
  setSessionColor(agentColor);
2270
2256
  }
2271
2257
  }
@@ -3249,6 +3235,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3249
3235
  setSessionId(restoredConversation.sessionId || null);
3250
3236
  setPendingFork(false);
3251
3237
  setHistory(restoredConversation.history);
3238
+ promptHistoryRef.current = seedComposerPromptHistory(transcriptPrompts(restoredConversation.history));
3252
3239
  updateComposerInput(point.prompt);
3253
3240
  }
3254
3241
  append({
@@ -6285,9 +6272,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
6285
6272
  streamingFrameRef.current?.flush();
6286
6273
  setThinkingExpanded(false);
6287
6274
  setStatus('ready');
6288
- inputHistoryRef.current = [];
6289
- inputHistoryIndexRef.current = null;
6290
- inputHistoryDraftRef.current = '';
6291
6275
  })().catch(warn);
6292
6276
  }
6293
6277
  else if (prompt === '/model') {
@@ -0,0 +1,17 @@
1
+ import { type ComposerEditorState } from './composer-editor.js';
2
+ export declare const COMPOSER_PROMPT_HISTORY_LIMIT = 100;
3
+ export interface ComposerPromptHistoryState {
4
+ readonly entries: readonly string[];
5
+ readonly index: number | null;
6
+ readonly draft: ComposerEditorState | null;
7
+ }
8
+ export interface ComposerPromptHistoryTransition {
9
+ readonly state: ComposerPromptHistoryState;
10
+ readonly editor: ComposerEditorState | null;
11
+ }
12
+ export declare function createComposerPromptHistory(): ComposerPromptHistoryState;
13
+ export declare function seedComposerPromptHistory(prompts: readonly string[]): ComposerPromptHistoryState;
14
+ export declare function recordComposerPrompt(state: ComposerPromptHistoryState, prompt: string): ComposerPromptHistoryState;
15
+ export declare function navigateComposerPromptHistory(state: ComposerPromptHistoryState, direction: 'previous' | 'next', currentEditor: ComposerEditorState): ComposerPromptHistoryTransition;
16
+ export declare function resetComposerPromptHistoryNavigation(state: ComposerPromptHistoryState): ComposerPromptHistoryState;
17
+ //# sourceMappingURL=composer-prompt-history.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { createComposerEditor, } from './composer-editor.js';
2
+ export const COMPOSER_PROMPT_HISTORY_LIMIT = 100;
3
+ export function createComposerPromptHistory() {
4
+ return { entries: [], index: null, draft: null };
5
+ }
6
+ function normalize(prompts) {
7
+ const entries = [];
8
+ for (const prompt of prompts) {
9
+ if (prompt.trim() === '')
10
+ continue;
11
+ if (entries.at(-1) === prompt)
12
+ continue;
13
+ entries.push(prompt);
14
+ }
15
+ return entries.slice(-COMPOSER_PROMPT_HISTORY_LIMIT);
16
+ }
17
+ export function seedComposerPromptHistory(prompts) {
18
+ return { entries: normalize(prompts), index: null, draft: null };
19
+ }
20
+ export function recordComposerPrompt(state, prompt) {
21
+ if (prompt.trim() === '')
22
+ return state;
23
+ return {
24
+ entries: normalize([...state.entries, prompt]),
25
+ index: null,
26
+ draft: null,
27
+ };
28
+ }
29
+ export function navigateComposerPromptHistory(state, direction, currentEditor) {
30
+ if (state.entries.length === 0)
31
+ return { state, editor: null };
32
+ if (direction === 'previous') {
33
+ const index = state.index === null
34
+ ? state.entries.length - 1
35
+ : Math.max(0, state.index - 1);
36
+ const draft = state.index === null
37
+ ? createComposerEditor(currentEditor.text, currentEditor.cursor)
38
+ : state.draft;
39
+ const nextState = { ...state, index, draft };
40
+ return {
41
+ state: nextState,
42
+ editor: createComposerEditor(state.entries[index]),
43
+ };
44
+ }
45
+ if (state.index === null)
46
+ return { state, editor: null };
47
+ if (state.index < state.entries.length - 1) {
48
+ const index = state.index + 1;
49
+ return {
50
+ state: { ...state, index },
51
+ editor: createComposerEditor(state.entries[index]),
52
+ };
53
+ }
54
+ const editor = state.draft;
55
+ return {
56
+ state: { ...state, index: null, draft: null },
57
+ editor,
58
+ };
59
+ }
60
+ export function resetComposerPromptHistoryNavigation(state) {
61
+ return { ...state, index: null, draft: null };
62
+ }
63
+ //# sourceMappingURL=composer-prompt-history.js.map
@@ -44,7 +44,8 @@ export function routeTuiInteraction(snapshot, input) {
44
44
  if (snapshot.layer.kind === 'delegated')
45
45
  return delegated(confirmationEffects);
46
46
  const viewport = snapshot.viewport;
47
- if (viewport.enabled && input.scrollIntent !== 'none') {
47
+ const scrollIsIncidental = input.action === undefined || input.action.startsWith('scroll:');
48
+ if (viewport.enabled && input.scrollIntent !== 'none' && scrollIsIncidental) {
48
49
  const pageRows = finiteNonNegativeInteger(viewport.pageRows);
49
50
  const offset = finiteNonNegativeInteger(viewport.offset);
50
51
  const maxOffset = finiteNonNegativeInteger(viewport.maxOffset);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-agent",
3
- "version": "0.46.0",
3
+ "version": "0.46.1",
4
4
  "description": "Local-first, single-user general agent for the command line.",
5
5
  "license": "MIT",
6
6
  "author": "wuqisen",