praxis-agent 0.34.0 → 0.36.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.
Files changed (78) hide show
  1. package/README.md +9 -1
  2. package/dist/application/background-agent-manager.d.ts +16 -6
  3. package/dist/application/background-agent-manager.js +139 -97
  4. package/dist/application/context-engine.d.ts +56 -0
  5. package/dist/application/context-engine.js +129 -0
  6. package/dist/application/session-service.d.ts +8 -5
  7. package/dist/application/session-service.js +1824 -1728
  8. package/dist/application/subagent-service.js +385 -193
  9. package/dist/application/top-level-agent-manager.d.ts +1 -0
  10. package/dist/application/top-level-agent-manager.js +1025 -403
  11. package/dist/application/transcript-projection.d.ts +33 -0
  12. package/dist/application/transcript-projection.js +110 -0
  13. package/dist/application/turn-lifecycle.d.ts +40 -0
  14. package/dist/application/turn-lifecycle.js +36 -0
  15. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  16. package/dist/application/turn-memory-coordinator.js +98 -0
  17. package/dist/cli/agents-dashboard.js +10 -5
  18. package/dist/cli/interactive.js +279 -209
  19. package/dist/cli/protocol.js +5 -2
  20. package/dist/cli/tui/claude-style.d.ts +5 -48
  21. package/dist/cli/tui/claude-style.js +167 -197
  22. package/dist/cli/tui/task-panel.js +10 -1
  23. package/dist/cli/tui/theme.d.ts +46 -3
  24. package/dist/cli/tui/theme.js +414 -56
  25. package/dist/cli/tui/tool-permission.js +9 -5
  26. package/dist/cli/tui/transcript-presentation.d.ts +89 -0
  27. package/dist/cli/tui/transcript-presentation.js +169 -0
  28. package/dist/cli/tui/transcript-viewport.d.ts +5 -34
  29. package/dist/cli/tui/transcript-viewport.js +639 -158
  30. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  31. package/dist/cli/tui/tui-interaction-router.js +127 -0
  32. package/dist/cli/tui/tui-view-model.d.ts +6 -2
  33. package/dist/cli/tui/tui-view-model.js +23 -6
  34. package/dist/cli-runtime.d.ts +2 -2
  35. package/dist/compatibility/claude/context.d.ts +2 -2
  36. package/dist/compatibility/claude/context.js +2 -14
  37. package/dist/compatibility/claude/data-plane-adapter.d.ts +9 -0
  38. package/dist/compatibility/claude/data-plane-adapter.js +44 -0
  39. package/dist/compatibility/claude/paths.js +2 -2
  40. package/dist/compatibility/claude/shared-resources.d.ts +7 -38
  41. package/dist/compatibility/claude/transcript-codec.d.ts +21 -0
  42. package/dist/compatibility/claude/transcript-codec.js +512 -0
  43. package/dist/core/agent-orchestration.d.ts +31 -0
  44. package/dist/core/agent-orchestration.js +235 -0
  45. package/dist/core/context.d.ts +25 -7
  46. package/dist/core/context.js +46 -0
  47. package/dist/core/prompt-composer.d.ts +8 -16
  48. package/dist/core/prompt-composer.js +65 -25
  49. package/dist/core/resources.d.ts +39 -0
  50. package/dist/core/resources.js +2 -0
  51. package/dist/core/runtime.d.ts +6 -1
  52. package/dist/core/runtime.js +15 -1
  53. package/dist/core/session.d.ts +2 -0
  54. package/dist/core/session.js +5 -0
  55. package/dist/core/transcript-codec.d.ts +70 -0
  56. package/dist/core/transcript-codec.js +60 -0
  57. package/dist/core/transcript-event.d.ts +33 -0
  58. package/dist/core/transcript-event.js +198 -0
  59. package/dist/persistence/claude-job-store.d.ts +58 -0
  60. package/dist/persistence/claude-job-store.js +483 -1
  61. package/dist/persistence/data-plane-adapter.d.ts +32 -0
  62. package/dist/persistence/data-plane-adapter.js +2 -0
  63. package/dist/persistence/data-plane.d.ts +4 -20
  64. package/dist/persistence/data-plane.js +10 -47
  65. package/dist/persistence/native-data-plane-adapter.d.ts +9 -0
  66. package/dist/persistence/native-data-plane-adapter.js +39 -0
  67. package/dist/persistence/native-resources.d.ts +4 -4
  68. package/dist/persistence/native-transcript-codec.d.ts +13 -0
  69. package/dist/persistence/native-transcript-codec.js +102 -0
  70. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  71. package/dist/persistence/native-transcript-reader.js +283 -0
  72. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  73. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  74. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  75. package/dist/platform/exclusive-file-lease.js +3 -0
  76. package/dist/tools/claude-user-message.d.ts +0 -1
  77. package/dist/tools/claude-user-message.js +0 -1
  78. package/package.json +6 -2
@@ -18,7 +18,6 @@ import { loadClaudeReleaseNotes } from './tui/release-notes.js';
18
18
  import { fullscreenInkRenderOptions } from './tui/fullscreen-renderer.js';
19
19
  import { StreamingFrameBuffer } from './tui/streaming-frame-buffer.js';
20
20
  import { createClaudeStatusLineInput, StatusLine } from './tui/status-line.js';
21
- import { FULLSCREEN_TRANSCRIPT_RESERVED_ROWS, transcriptLineCount, } from './tui/transcript-viewport.js';
22
21
  import { projectTuiView, resolveTuiRenderer } from './tui/tui-view-model.js';
23
22
  import { loadGitDiff, visiblePatchLines, } from './tui/git-diff.js';
24
23
  import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule, } from './tui/permission-settings.js';
@@ -27,18 +26,19 @@ import { agentColorMessage, parseAgentColorInput, } from '../compatibility/claud
27
26
  import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
28
27
  import { runDoctor, } from '../maintenance/doctor.js';
29
28
  import { canonicalClaudeCostModelName, formatCostSummary, } from './tui/cost-summary.js';
30
- import { createComposerEditor, deleteComposerBackward, deleteComposerForward, deleteComposerToEnd, deleteComposerToStart, deleteComposerWordBackward, insertComposerText, moveComposerCursor, moveComposerCursorByWord, } from './tui/composer-editor.js';
29
+ import { createComposerEditor, deleteComposerBackward, deleteComposerForward, insertComposerText, moveComposerCursor, } from './tui/composer-editor.js';
31
30
  import { routeComposerKey, } from './tui/composer-key-router.js';
31
+ import { routeTuiInteraction, } from './tui/tui-interaction-router.js';
32
32
  import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
33
33
  import { editTuiPrompt, openTuiEditorFile, } from './tui/external-editor.js';
34
34
  import { suspendTuiProcess } from './tui/terminal-suspend.js';
35
35
  import { readTuiClipboard, writeTuiClipboard, writeTuiOsc52Clipboard, } from './tui/clipboard.js';
36
- import { composerImageIds, deleteComposerImageBackward, deleteComposerImageForward, insertComposerImageMarker, moveComposerCursorAcrossImages, } from './tui/composer-images.js';
36
+ import { composerImageIds, insertComposerImageMarker, } from './tui/composer-images.js';
37
37
  import { defaultTuiKeybindings, ensureTuiKeybindingsFile, hasTuiKeybindingPrefix, loadTuiKeybindings, resolveTuiKeybinding, tuiKeyChord, } from './tui/keybindings.js';
38
38
  import { completeTuiWorkspaceDirectory, resolveTuiWorkspaceDirectory, } from './tui/workspace-directories.js';
39
39
  import { conversationExportPath, conversationExportText, defaultConversationExportFilename, writeConversationExport, } from './tui/conversation-export.js';
40
40
  import { loadTuiThemeSettings, saveTuiThemeSettings, themeSettingsWithCustomTheme, } from './tui/theme-settings.js';
41
- import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider, tuiPalette, } from './tui/theme.js';
41
+ import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider, useTuiTheme, } from './tui/theme.js';
42
42
  import { CUSTOM_THEME_TOKENS, createTuiCustomTheme, deleteTuiCustomTheme, loadTuiCustomThemes, updateTuiCustomTheme, } from './tui/custom-themes.js';
43
43
  import { setupTuiTerminal, terminalSetupTuiSlashCommand, } from './tui/terminal-setup.js';
44
44
  import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid, elicitationTextValue, expandElicitationOptions, focusedElicitationField, McpElicitationForm, McpElicitationUrl, moveElicitationFocus, moveElicitationOption, selectElicitationOption, toggleElicitationBoolean, typeaheadElicitationOption, unsetElicitationField, validateTuiElicitationForm, } from './tui/mcp-elicitation.js';
@@ -112,11 +112,6 @@ const ZERO_COST_SUMMARY = {
112
112
  hasUnknownModelCost: false,
113
113
  modelUsage: [],
114
114
  };
115
- function selectionPrefix(selected, screenReader) {
116
- if (selected)
117
- return screenReader ? 'Selected: ' : '❯ ';
118
- return screenReader ? '' : ' ';
119
- }
120
115
  function rewindActions(point) {
121
116
  const shared = [
122
117
  { action: 'summarize-from', label: 'Summarize from here' },
@@ -267,6 +262,25 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
267
262
  'update',
268
263
  'usage',
269
264
  ]);
265
+ function RewindWarning() {
266
+ const theme = useTuiTheme();
267
+ return (_jsx(Text, { ...theme.text.warning, children: "\u26A0 Rewinding does not affect files edited manually or via bash." }));
268
+ }
269
+ function ExitWarning() {
270
+ const theme = useTuiTheme();
271
+ return _jsx(Text, { ...theme.text.warning, children: "Press Ctrl-C again to exit" });
272
+ }
273
+ function DecisionOption({ selected, screenReader, children, selectedPrefix = '❯ ', unselectedPrefix = ' ', }) {
274
+ const theme = useTuiTheme();
275
+ const accessible = screenReader || theme.noColor;
276
+ return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [selected
277
+ ? accessible
278
+ ? 'Selected: '
279
+ : selectedPrefix
280
+ : accessible
281
+ ? ''
282
+ : unselectedPrefix, children] }));
283
+ }
270
284
  export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: suppliedConfigRoot, statePath: suppliedStatePath, factory, initialSessions, initialPrompt, initialHistory = [], initialSessionColor, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, doctorLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader, memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, notificationDelayMs = 6_000, elicitationUrlOpener = openTuiUrl, releaseNotesLoader = (configRoot) => loadClaudeReleaseNotes({ configRoot }), settingSources, }) {
271
285
  const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
272
286
  const width = useTerminalWidth(terminalWidth);
@@ -346,9 +360,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
346
360
  runtimeSettingsRef.current = runtimeSettings;
347
361
  runtimeGitignoreRef.current = runtimeSettings.gitignore;
348
362
  const [vimInsertMode, setVimInsertMode] = useState(true);
349
- const activePalette = tuiPalette(TUI_THEMES.includes(themeSettings.theme)
350
- ? themeSettings.theme
351
- : 'dark', themeSettings.syntaxHighlightingDisabled, process.env, themeSettings.customTheme);
352
363
  const choices = useMemo(() => allowNewSession ? [null, ...initialSessions] : initialSessions, [allowNewSession, initialSessions]);
353
364
  const [pickerIncludesNewSession, setPickerIncludesNewSession] = useState(allowNewSession);
354
365
  const pickerIncludesNewSessionRef = useRef(allowNewSession);
@@ -450,7 +461,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
450
461
  const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
451
462
  const [history, setHistory] = useState([...initialHistory]);
452
463
  const activeAttemptThinkingItemsRef = useRef([]);
453
- const [transcriptScrollOffset, setTranscriptScrollOffset] = useState(0);
464
+ const [transcriptScrollOffset, setTranscriptScrollOffsetState] = useState(0);
465
+ const transcriptScrollOffsetRef = useRef(0);
466
+ const setTranscriptScrollOffset = (offset) => {
467
+ transcriptScrollOffsetRef.current = offset;
468
+ setTranscriptScrollOffsetState(offset);
469
+ };
454
470
  // The pure TUI view model classifies the session identity (fresh/resumed/
455
471
  // started) and projects the rendered transcript. Startup diagnostics are
456
472
  // useful before the first prompt, but they are not conversation history and
@@ -466,7 +482,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
466
482
  // composer/status chrome intact and keeping the active stream visible.
467
483
  // Classic and screen-reader modes always render the full history exactly as
468
484
  // before.
469
- const { projectedHistory, resumed, freshSession, hasConversationHistory } = projectTuiView({
485
+ const { transcriptEntries, transcriptPageRows, maxTranscriptScrollOffset, resumed, freshSession, hasConversationHistory, } = useMemo(() => projectTuiView({
470
486
  initialHistory,
471
487
  history,
472
488
  resume: resume !== undefined,
@@ -475,7 +491,19 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
475
491
  rows,
476
492
  width,
477
493
  scrollOffset: transcriptScrollOffset,
478
- });
494
+ detailedTranscript: thinkingExpanded || runtimeSettings.verbose,
495
+ }), [
496
+ initialHistory,
497
+ history,
498
+ resume,
499
+ fixedViewport,
500
+ axScreenReader,
501
+ rows,
502
+ width,
503
+ transcriptScrollOffset,
504
+ thinkingExpanded,
505
+ runtimeSettings.verbose,
506
+ ]);
479
507
  const sessionLoadRef = useRef(0);
480
508
  const [turnDiffs, setTurnDiffs] = useState([]);
481
509
  const turnNumberRef = useRef(0);
@@ -1141,25 +1169,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
1141
1169
  exitConfirmationTimerRef.current = null;
1142
1170
  setExitConfirmation(false);
1143
1171
  };
1144
- const armExitConfirmation = () => {
1145
- if (exitConfirmation) {
1146
- permissionRef.current?.resolve(false);
1147
- elicitationRef.current?.resolve({ action: 'cancel' });
1148
- questionRef.current?.resolve(null);
1149
- planApprovalRef.current?.resolve({ behavior: 'deny' });
1150
- onCancel?.();
1151
- exit();
1152
- return;
1153
- }
1154
- clearComposerInput();
1155
- setExitConfirmation(true);
1156
- if (exitConfirmationTimerRef.current)
1157
- clearTimeout(exitConfirmationTimerRef.current);
1158
- exitConfirmationTimerRef.current = setTimeout(() => {
1159
- exitConfirmationTimerRef.current = null;
1160
- setExitConfirmation(false);
1161
- }, 1_500);
1162
- };
1163
1172
  useEffect(() => {
1164
1173
  setCommandSelection((current) => Math.min(current, Math.max(0, matchingSlashCommands.length - 1)));
1165
1174
  }, [matchingSlashCommands.length]);
@@ -2861,74 +2870,150 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2861
2870
  const codePoint = character.codePointAt(0) ?? 0;
2862
2871
  return codePoint >= 32 && codePoint !== 127;
2863
2872
  });
2873
+ const composerKey = {
2874
+ value,
2875
+ left: key.leftArrow,
2876
+ right: key.rightArrow,
2877
+ backspace: key.backspace,
2878
+ delete: key.delete,
2879
+ ctrl: key.ctrl,
2880
+ meta: key.meta,
2881
+ escape: key.escape || value === '\u001B',
2882
+ };
2864
2883
  const editor = () => createComposerEditor(inputRef.current, inputCursorRef.current);
2865
2884
  const editComposer = () => {
2866
- if (key.leftArrow) {
2867
- updateComposerEditor(key.meta
2868
- ? moveComposerCursorByWord(editor(), 'backward')
2869
- : moveComposerCursorAcrossImages(editor(), -1));
2870
- return true;
2871
- }
2872
- if (key.rightArrow) {
2873
- updateComposerEditor(key.meta
2874
- ? moveComposerCursorByWord(editor(), 'forward')
2875
- : moveComposerCursorAcrossImages(editor(), 1));
2876
- return true;
2877
- }
2878
- if (controlKey('a')) {
2879
- updateComposerEditor(createComposerEditor(inputRef.current, 0));
2885
+ const transition = routeComposerKey(editor(), composerKey);
2886
+ if (transition.kind === 'edit') {
2887
+ updateComposerEditor(transition.editor);
2880
2888
  return true;
2881
2889
  }
2882
- if (controlKey('e')) {
2883
- updateComposerEditor(createComposerEditor(inputRef.current));
2884
- return true;
2885
- }
2886
- if (controlKey('b')) {
2887
- updateComposerEditor(moveComposerCursorAcrossImages(editor(), -1));
2888
- return true;
2889
- }
2890
- if (controlKey('f')) {
2891
- updateComposerEditor(moveComposerCursorAcrossImages(editor(), 1));
2892
- return true;
2893
- }
2894
- if (controlKey('w')) {
2895
- updateComposerEditor(deleteComposerWordBackward(editor()));
2896
- return true;
2897
- }
2898
- if (controlKey('u')) {
2899
- updateComposerEditor(deleteComposerToStart(editor()));
2900
- return true;
2901
- }
2902
- if (controlKey('k')) {
2903
- updateComposerEditor(deleteComposerToEnd(editor()));
2904
- return true;
2905
- }
2906
- if (key.backspace) {
2907
- updateComposerEditor(deleteComposerImageBackward(editor()));
2908
- return true;
2909
- }
2910
- if (key.delete) {
2911
- updateComposerEditor(deleteComposerImageForward(editor()));
2912
- return true;
2913
- }
2914
- if (!key.ctrl && !key.meta && value && printable) {
2915
- updateComposerEditor(insertComposerText(editor(), value));
2916
- return true;
2917
- }
2918
- return false;
2890
+ if (transition.kind === 'cancel')
2891
+ clearComposerInput();
2892
+ return transition.kind === 'cancel';
2919
2893
  };
2920
- if (controlKey('c')) {
2921
- armExitConfirmation();
2922
- return;
2923
- }
2924
- if (controlKey('z')) {
2925
- if (!processSuspendRequestedRef.current) {
2926
- processSuspendRequestedRef.current = true;
2927
- setProcessSuspendRequested(true);
2894
+ const interactionComposer = () => ({
2895
+ mode: runtimeSettingsRef.current.editor === 'vim'
2896
+ ? vimInsertMode
2897
+ ? 'vim-insert'
2898
+ : 'vim-normal'
2899
+ : 'readline',
2900
+ editor: editor(),
2901
+ lastCancelAtMs: lastEscapeAtRef.current,
2902
+ });
2903
+ const applyInteractionEffects = (effects) => {
2904
+ for (const effect of effects) {
2905
+ if (effect.kind === 'request-process-suspension') {
2906
+ processSuspendRequestedRef.current = true;
2907
+ setProcessSuspendRequested(true);
2908
+ }
2909
+ else if (effect.kind === 'set-transcript-scroll-offset') {
2910
+ setTranscriptScrollOffset(effect.offset);
2911
+ }
2912
+ else if (effect.kind === 'interrupt-turn') {
2913
+ turnControllerRef.current?.abort();
2914
+ }
2915
+ else if (effect.kind === 'arm-exit-confirmation') {
2916
+ setExitConfirmation(true);
2917
+ if (exitConfirmationTimerRef.current)
2918
+ clearTimeout(exitConfirmationTimerRef.current);
2919
+ exitConfirmationTimerRef.current = setTimeout(() => {
2920
+ exitConfirmationTimerRef.current = null;
2921
+ setExitConfirmation(false);
2922
+ }, 1_500);
2923
+ }
2924
+ else if (effect.kind === 'dismiss-exit-confirmation') {
2925
+ dismissExitConfirmation();
2926
+ }
2927
+ else if (effect.kind === 'exit-application') {
2928
+ permissionRef.current?.resolve(false);
2929
+ elicitationRef.current?.resolve({ action: 'cancel' });
2930
+ questionRef.current?.resolve(null);
2931
+ planApprovalRef.current?.resolve({ behavior: 'deny' });
2932
+ onCancel?.();
2933
+ exit();
2934
+ }
2935
+ else if (effect.kind === 'set-vim-insert-mode') {
2936
+ setVimInsertMode(effect.insert);
2937
+ }
2938
+ else if (effect.kind === 'set-composer-editor') {
2939
+ updateComposerEditor(effect.editor);
2940
+ }
2941
+ else if (effect.kind === 'clear-composer') {
2942
+ clearComposerInput();
2943
+ }
2944
+ else if (effect.kind === 'record-composer-cancel') {
2945
+ lastEscapeAtRef.current = effect.timestamp;
2946
+ }
2947
+ else if (effect.kind === 'cancel-tui-layer') {
2948
+ switch (effect.target) {
2949
+ case 'permission':
2950
+ permissionRef.current?.resolve(false);
2951
+ break;
2952
+ case 'plan-approval':
2953
+ planApprovalRef.current?.resolve({ behavior: 'deny' });
2954
+ break;
2955
+ case 'question':
2956
+ questionRef.current?.resolve(null);
2957
+ break;
2958
+ case 'elicitation-url-waiting':
2959
+ if (elicitationUrlWaitingRef.current) {
2960
+ elicitationUrlWaitingRef.current = null;
2961
+ setElicitationUrlWaiting(false);
2962
+ setElicitation(null);
2963
+ setElicitationForm(null);
2964
+ clearComposerInput();
2965
+ }
2966
+ break;
2967
+ case 'elicitation-options':
2968
+ setElicitationForm((current) => current?.expandedField
2969
+ ? { ...current, expandedField: undefined }
2970
+ : current);
2971
+ break;
2972
+ case 'elicitation':
2973
+ elicitationRef.current?.resolve({ action: 'cancel' });
2974
+ break;
2975
+ case 'file-picker':
2976
+ setFilePickerOpen(false);
2977
+ break;
2978
+ case 'command-palette':
2979
+ setCommandPaletteOpen(false);
2980
+ break;
2981
+ default: {
2982
+ const unhandledTarget = effect.target;
2983
+ return unhandledTarget;
2984
+ }
2985
+ }
2986
+ }
2987
+ else {
2988
+ const unhandledEffect = effect;
2989
+ return unhandledEffect;
2990
+ }
2928
2991
  }
2992
+ };
2993
+ if (controlKey('c') || controlKey('z')) {
2994
+ const globalInteraction = routeTuiInteraction({
2995
+ suspensionPending: processSuspendRequestedRef.current,
2996
+ exitConfirmationArmed: exitConfirmation,
2997
+ layer: { kind: 'none' },
2998
+ busy,
2999
+ viewport: {
3000
+ enabled: false,
3001
+ offset: 0,
3002
+ maxOffset: 0,
3003
+ pageRows: 0,
3004
+ },
3005
+ composer: interactionComposer(),
3006
+ }, {
3007
+ globalIntent: controlKey('c') ? 'exit' : 'suspend',
3008
+ scrollIntent: 'none',
3009
+ action: undefined,
3010
+ composerKey,
3011
+ timestamp: Date.now(),
3012
+ callerIntent: 'none',
3013
+ });
3014
+ applyInteractionEffects(globalInteraction.effects);
2929
3015
  return;
2930
3016
  }
2931
- dismissExitConfirmation();
2932
3017
  const keybindingContexts = menuRef.current
2933
3018
  ? menuRef.current.kind === 'diff'
2934
3019
  ? ['DiffDialog']
@@ -2965,58 +3050,90 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2965
3050
  if (!keybindingAction) {
2966
3051
  keybindingAction = resolveTuiKeybinding(keybindings, keybindingContexts, inputChord);
2967
3052
  }
2968
- if (!keybindingAction &&
2969
- inputChord &&
2970
- hasTuiKeybindingPrefix(keybindings, keybindingContexts, inputChord)) {
3053
+ const scrollIntent = key.pageUp
3054
+ ? 'page-older'
3055
+ : key.pageDown
3056
+ ? 'page-newer'
3057
+ : controlKey('u') || controlKey('b')
3058
+ ? 'half-page-older'
3059
+ : controlKey('f') || controlKey('n')
3060
+ ? 'half-page-newer'
3061
+ : inputRef.current.length === 0 && key.upArrow
3062
+ ? 'line-older'
3063
+ : inputRef.current.length === 0 && key.downArrow
3064
+ ? 'line-newer'
3065
+ : 'none';
3066
+ const hasPendingPrefix = !keybindingAction &&
3067
+ inputChord !== null &&
3068
+ hasTuiKeybindingPrefix(keybindings, keybindingContexts, inputChord);
3069
+ if (hasPendingPrefix) {
2971
3070
  keySequenceRef.current = { chord: inputChord, at: Date.now() };
3071
+ }
3072
+ const layer = hasPendingPrefix
3073
+ ? { kind: 'pending-prefix' }
3074
+ : permission
3075
+ ? { kind: 'cancelable', target: 'permission' }
3076
+ : planApproval
3077
+ ? { kind: 'cancelable', target: 'plan-approval' }
3078
+ : question
3079
+ ? { kind: 'cancelable', target: 'question' }
3080
+ : elicitation
3081
+ ? {
3082
+ kind: 'cancelable',
3083
+ target: elicitationUrlWaiting
3084
+ ? 'elicitation-url-waiting'
3085
+ : elicitationForm?.expandedField
3086
+ ? 'elicitation-options'
3087
+ : 'elicitation',
3088
+ }
3089
+ : selectingSession
3090
+ ? { kind: 'delegated', target: 'session-picker' }
3091
+ : menuRef.current
3092
+ ? { kind: 'delegated', target: 'menu' }
3093
+ : filePickerVisible
3094
+ ? { kind: 'cancelable', target: 'file-picker' }
3095
+ : commandPaletteVisible
3096
+ ? { kind: 'cancelable', target: 'command-palette' }
3097
+ : { kind: 'none' };
3098
+ const callerIntent = !busy && value === '?' && inputRef.current.length === 0
3099
+ ? 'toggle-shortcuts'
3100
+ : runtimeSettingsRef.current.leftArrowOpensAgents &&
3101
+ key.leftArrow &&
3102
+ inputRef.current.length === 0 &&
3103
+ availableAgents.length > 0
3104
+ ? 'open-agents'
3105
+ : key.return &&
3106
+ (key.shift || key.meta) &&
3107
+ keybindingAction === undefined
3108
+ ? 'implicit-newline'
3109
+ : 'none';
3110
+ const interaction = routeTuiInteraction({
3111
+ suspensionPending: processSuspendRequestedRef.current,
3112
+ exitConfirmationArmed: exitConfirmation,
3113
+ layer,
3114
+ busy,
3115
+ viewport: {
3116
+ enabled: fixedViewport,
3117
+ offset: transcriptScrollOffsetRef.current,
3118
+ maxOffset: maxTranscriptScrollOffset,
3119
+ pageRows: transcriptPageRows,
3120
+ },
3121
+ composer: interactionComposer(),
3122
+ }, {
3123
+ globalIntent: 'none',
3124
+ scrollIntent,
3125
+ action: keybindingAction,
3126
+ composerKey,
3127
+ timestamp: Date.now(),
3128
+ callerIntent,
3129
+ });
3130
+ applyInteractionEffects(interaction.effects);
3131
+ if (interaction.disposition === 'handled') {
2972
3132
  return;
2973
3133
  }
2974
3134
  const isKeybinding = (action) => keybindingAction === action;
2975
- if (fixedViewport &&
2976
- menuRef.current === null &&
2977
- !permission &&
2978
- !planApproval &&
2979
- !question &&
2980
- !elicitation &&
2981
- !selectingSession) {
2982
- const page = Math.max(1, (rows ?? 0) - FULLSCREEN_TRANSCRIPT_RESERVED_ROWS);
2983
- const scrollDelta = key.pageUp
2984
- ? page
2985
- : key.pageDown
2986
- ? -page
2987
- : controlKey('u') || controlKey('b')
2988
- ? page / 2
2989
- : controlKey('f') || controlKey('n')
2990
- ? -page / 2
2991
- : inputRef.current.length === 0 && key.upArrow
2992
- ? 1
2993
- : inputRef.current.length === 0 && key.downArrow
2994
- ? -1
2995
- : 0;
2996
- if (scrollDelta !== 0) {
2997
- setTranscriptScrollOffset((current) => Math.min(Math.max(0, transcriptLineCount(history, width) -
2998
- Math.max(1, (rows ?? 0) - FULLSCREEN_TRANSCRIPT_RESERVED_ROWS)), Math.max(0, current + Math.trunc(scrollDelta))));
2999
- return;
3000
- }
3001
- }
3002
- if (runtimeSettingsRef.current.editor === 'vim' &&
3003
- !permission &&
3004
- !planApproval &&
3005
- !question &&
3006
- !elicitation &&
3007
- menuRef.current === null) {
3008
- if (!vimInsertMode) {
3009
- if (value === 'i' || value === 'a')
3010
- setVimInsertMode(true);
3011
- else if (key.leftArrow || key.rightArrow)
3012
- editComposer();
3013
- return;
3014
- }
3015
- if (key.escape || value === '\u001B') {
3016
- setVimInsertMode(false);
3017
- return;
3018
- }
3019
- }
3135
+ if (hasPendingPrefix)
3136
+ return;
3020
3137
  if (permission) {
3021
3138
  const options = permission.kind === 'tool' && toolPermissionModel
3022
3139
  ? toolPermissionModel.options
@@ -3162,10 +3279,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3162
3279
  }
3163
3280
  permission.resolve(feedback ? { behavior: 'deny', message: feedback } : false);
3164
3281
  };
3165
- if (key.escape) {
3166
- permission.resolve(false);
3167
- }
3168
- else if (permissionFeedbackMode) {
3282
+ if (permissionFeedbackMode) {
3169
3283
  if (key.tab) {
3170
3284
  clearComposerInput();
3171
3285
  setPermissionFeedbackMode(false);
@@ -3246,10 +3360,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3246
3360
  ...(feedback ? { feedback } : {}),
3247
3361
  });
3248
3362
  };
3249
- if (key.escape) {
3250
- planApproval.resolve({ behavior: 'deny' });
3251
- }
3252
- else if (planApprovalFeedbackMode) {
3363
+ if (planApprovalFeedbackMode) {
3253
3364
  if (key.tab) {
3254
3365
  clearComposerInput();
3255
3366
  setPlanApprovalFeedbackMode(false);
@@ -3286,10 +3397,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3286
3397
  return;
3287
3398
  }
3288
3399
  if (question) {
3289
- if (key.escape) {
3290
- question.resolve(null);
3291
- }
3292
- else if (key.return) {
3400
+ if (key.return) {
3293
3401
  try {
3294
3402
  const current = question.questions[question.index];
3295
3403
  if (!current)
@@ -3343,13 +3451,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3343
3451
  setElicitationForm(null);
3344
3452
  clearComposerInput();
3345
3453
  };
3346
- if (key.escape) {
3347
- if (elicitationUrlWaiting)
3348
- dismissUrlDialog();
3349
- else
3350
- elicitation.resolve({ action: 'cancel' });
3351
- }
3352
- else if (key.leftArrow || key.rightArrow) {
3454
+ if (key.leftArrow || key.rightArrow) {
3353
3455
  setElicitationForm({
3354
3456
  ...form,
3355
3457
  focusIndex: form.focusIndex === 0 ? 1 : 0,
@@ -3375,7 +3477,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3375
3477
  return;
3376
3478
  }
3377
3479
  if (form.expandedField && currentField) {
3378
- if (key.escape || key.leftArrow) {
3480
+ if (key.leftArrow) {
3379
3481
  setElicitationForm({ ...form, expandedField: undefined });
3380
3482
  }
3381
3483
  else if (key.upArrow) {
@@ -3394,9 +3496,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3394
3496
  setElicitationForm(typeaheadElicitationOption(form, value));
3395
3497
  }
3396
3498
  }
3397
- else if (key.escape) {
3398
- elicitation.resolve({ action: 'cancel' });
3399
- }
3400
3499
  else if (key.upArrow) {
3401
3500
  move(-1);
3402
3501
  }
@@ -5165,8 +5264,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5165
5264
  void backgrounding;
5166
5265
  return;
5167
5266
  }
5168
- if (isKeybinding('chat:cancel'))
5169
- turnControllerRef.current?.abort();
5170
5267
  return;
5171
5268
  }
5172
5269
  if (isKeybinding('chat:imagePaste')) {
@@ -5182,10 +5279,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5182
5279
  return;
5183
5280
  }
5184
5281
  if (filePickerVisible) {
5185
- if (key.escape || value === '\u001B') {
5186
- setFilePickerOpen(false);
5187
- return;
5188
- }
5189
5282
  if (key.upArrow) {
5190
5283
  setFileSelection((current) => Math.max(0, current - 1));
5191
5284
  return;
@@ -5210,13 +5303,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5210
5303
  updateMenu({ kind: 'agents', agents: availableAgents, selectedIndex: 0 });
5211
5304
  return;
5212
5305
  }
5213
- if (isKeybinding('chat:cancel')) {
5214
- const now = Date.now();
5215
- if (now - lastEscapeAtRef.current <= 500)
5216
- clearComposerInput();
5217
- lastEscapeAtRef.current = now;
5218
- return;
5219
- }
5220
5306
  if (isKeybinding('chat:cycleMode')) {
5221
5307
  const currentIndex = permissionOptions.findIndex((option) => option.mode === runtimePreferences.permissionMode);
5222
5308
  const next = permissionOptions[(currentIndex + 1) % permissionOptions.length];
@@ -5225,10 +5311,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5225
5311
  return;
5226
5312
  }
5227
5313
  if (commandPaletteVisible) {
5228
- if (key.escape) {
5229
- setCommandPaletteOpen(false);
5230
- return;
5231
- }
5232
5314
  if (key.upArrow) {
5233
5315
  if (matchingSlashCommands.length > 0) {
5234
5316
  setCommandSelection((current) => Math.max(0, current - 1));
@@ -5832,24 +5914,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5832
5914
  clearComposerInput();
5833
5915
  return;
5834
5916
  }
5835
- const transition = routeComposerKey(editor(), {
5836
- value,
5837
- left: key.leftArrow,
5838
- right: key.rightArrow,
5839
- backspace: key.backspace,
5840
- delete: key.delete,
5841
- ctrl: key.ctrl,
5842
- meta: key.meta,
5843
- escape: key.escape || value === '\u001B',
5844
- });
5845
- if (transition.kind === 'cancel') {
5846
- clearComposerInput();
5847
- }
5848
- else if (transition.kind === 'edit') {
5849
- updateComposerEditor(transition.editor);
5917
+ if (filePickerVisible || commandPaletteVisible) {
5918
+ editComposer();
5919
+ return;
5850
5920
  }
5851
5921
  });
5852
- return (_jsx(TuiThemeProvider, { settings: themeSettings, children: _jsx(Box, { flexDirection: "column", ...(!fixedViewport
5922
+ return (_jsx(TuiThemeProvider, { settings: themeSettings, screenReader: axScreenReader, children: _jsx(Box, { flexDirection: "column", ...(!fixedViewport
5853
5923
  ? {}
5854
5924
  : { height: rows, overflowY: 'hidden' }), children: selectingSession ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [!axScreenReader && freshSession ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width, showTips: runtimeSettings.tips, dataPlane: dataPlane })) : null, sessionId ? (_jsxs(Text, { dimColor: true, children: ["Session ", sessionId.slice(0, 8)] })) : null, !axScreenReader && !resumed && hasConversationHistory ? (_jsx(SessionIdentity, { display: runtimeDisplay, width: width })) : null, _jsx(Box, { ...(fixedViewport
5855
5925
  ? {
@@ -5857,18 +5927,18 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5857
5927
  minHeight: 0,
5858
5928
  overflowY: 'hidden',
5859
5929
  }
5860
- : {}), children: _jsx(Transcript, { items: projectedHistory, activeText: activeText, activeThinking: activeThinking, activeStreamVisible: transcriptScrollOffset === 0, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }) }), externalEditorRequest !== null ||
5930
+ : {}), children: _jsx(Transcript, { entries: transcriptEntries, activeText: activeText, activeThinking: activeThinking, activeStreamVisible: transcriptScrollOffset === 0, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }) }), externalEditorRequest !== null ||
5861
5931
  keybindingsEditing ||
5862
- memoryEditorRequest !== null ? (_jsx(ExternalEditorWait, { screenReader: axScreenReader })) : permission ? (permission.kind === 'tool' && toolPermissionModel ? (_jsx(ToolPermissionDialog, { model: toolPermissionModel, selection: permissionSelection, feedbackMode: permissionFeedbackMode, feedback: input, ruleEditor: permissionRuleEditor, screenReader: axScreenReader })) : (_jsxs(DialogFrame, { title: `Retry interrupted ${permission.call.name}?`, screenReader: axScreenReader, children: [_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: _jsx(Text, { bold: true, children: describeTool(permission.call, sensitiveValues) }) }), _jsx(Text, { children: "Do you want to proceed?" }), _jsxs(Text, { inverse: !axScreenReader && permissionSelection === 0, children: [selectionPrefix(permissionSelection === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && permissionSelection === 1, children: [selectionPrefix(permissionSelection === 1, axScreenReader), "2. No"] }), permissionFeedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', input ||
5932
+ memoryEditorRequest !== null ? (_jsx(ExternalEditorWait, { screenReader: axScreenReader })) : permission ? (permission.kind === 'tool' && toolPermissionModel ? (_jsx(ToolPermissionDialog, { model: toolPermissionModel, selection: permissionSelection, feedbackMode: permissionFeedbackMode, feedback: input, ruleEditor: permissionRuleEditor, screenReader: axScreenReader })) : (_jsxs(DialogFrame, { title: `Retry interrupted ${permission.call.name}?`, screenReader: axScreenReader, children: [_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: _jsx(Text, { bold: true, children: describeTool(permission.call, sensitiveValues) }) }), _jsx(Text, { children: "Do you want to proceed?" }), _jsx(DecisionOption, { selected: permissionSelection === 0, screenReader: axScreenReader, children: "1. Yes" }), _jsx(DecisionOption, { selected: permissionSelection === 1, screenReader: axScreenReader, children: "2. No" }), permissionFeedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', input ||
5863
5933
  (permissionSelection === 0
5864
5934
  ? 'tell Praxis what to do next'
5865
5935
  : 'tell Praxis what to do differently')] })) : null, _jsx(Text, { dimColor: true, children: permissionFeedbackMode
5866
5936
  ? 'Enter to submit · Tab to collapse · Esc to cancel'
5867
- : 'Enter to confirm · Tab to add feedback · Esc to cancel' })] }))) : planApproval ? (_jsxs(DialogFrame, { title: "Ready to code?", screenReader: axScreenReader, children: [_jsx(Text, { children: "Here is Praxis's plan:" }), planApproval.request.plan ? (_jsx(Box, { borderStyle: axScreenReader ? undefined : 'classic', borderLeft: false, borderRight: false, paddingX: axScreenReader ? 0 : 1, marginY: 1, children: _jsx(Text, { children: planApproval.request.plan }) })) : null, _jsx(Text, { dimColor: true, children: "Praxis has written up a plan and is ready to execute. Would you like to proceed?" }), _jsxs(Text, { inverse: !axScreenReader && planApprovalSelection === 0, children: [selectionPrefix(planApprovalSelection === 0, axScreenReader), "1.", ' ', runtimeSettings.useAutoModeDuringPlan
5937
+ : 'Enter to confirm · Tab to add feedback · Esc to cancel' })] }))) : planApproval ? (_jsxs(DialogFrame, { title: "Ready to code?", screenReader: axScreenReader, children: [_jsx(Text, { children: "Here is Praxis's plan:" }), planApproval.request.plan ? (_jsx(Box, { borderStyle: axScreenReader ? undefined : 'classic', borderLeft: false, borderRight: false, paddingX: axScreenReader ? 0 : 1, marginY: 1, children: _jsx(Text, { children: planApproval.request.plan }) })) : null, _jsx(Text, { dimColor: true, children: "Praxis has written up a plan and is ready to execute. Would you like to proceed?" }), _jsxs(DecisionOption, { selected: planApprovalSelection === 0, screenReader: axScreenReader, children: ["1.", ' ', runtimeSettings.useAutoModeDuringPlan
5868
5938
  ? 'Yes, and use auto mode'
5869
5939
  : allowDangerouslySkipPermissions
5870
5940
  ? 'Yes, and bypass permissions'
5871
- : 'Yes, auto-accept edits'] }), _jsxs(Text, { inverse: !axScreenReader && planApprovalSelection === 1, children: [selectionPrefix(planApprovalSelection === 1, axScreenReader), "2. Yes, manually approve edits"] }), _jsxs(Text, { inverse: !axScreenReader && planApprovalSelection === 2, children: [selectionPrefix(planApprovalSelection === 2, axScreenReader), "3. No, keep planning"] }), planApprovalFeedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', input ||
5941
+ : 'Yes, auto-accept edits'] }), _jsx(DecisionOption, { selected: planApprovalSelection === 1, screenReader: axScreenReader, children: "2. Yes, manually approve edits" }), _jsx(DecisionOption, { selected: planApprovalSelection === 2, screenReader: axScreenReader, children: "3. No, keep planning" }), planApprovalFeedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', input ||
5872
5942
  (planApprovalSelection === 2
5873
5943
  ? 'Tell Praxis what to change'
5874
5944
  : 'Add feedback for implementation')] })) : null, _jsx(Text, { dimColor: true, children: planApproval.request.planPath }), _jsx(Text, { dimColor: true, children: planApprovalFeedbackMode
@@ -5884,9 +5954,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5884
5954
  .slice(window.start, window.end)
5885
5955
  .map((point, offset) => {
5886
5956
  const index = window.start + offset;
5887
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { inverse: menu.selectedIndex === index, children: [menu.selectedIndex === index ? ' ❯ ' : ' ', point.prompt
5888
- .replace(/\s+/gu, ' ')
5889
- .slice(0, 72)] }), _jsxs(Text, { dimColor: true, children: [' ', point.fileChanges.length > 0
5957
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(DecisionOption, { selected: menu.selectedIndex === index, screenReader: axScreenReader, selectedPrefix: " \u276F ", unselectedPrefix: " ", children: point.prompt
5958
+ .replace(/\s+/gu, ' ')
5959
+ .slice(0, 72) }), _jsxs(Text, { dimColor: true, children: [' ', point.fileChanges.length > 0
5890
5960
  ? point.fileChanges
5891
5961
  .map((path) => path.startsWith(`${runtimeCwd}/`)
5892
5962
  ? path.slice(runtimeCwd.length + 1)
@@ -5896,11 +5966,11 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5896
5966
  ? 'No code changes'
5897
5967
  : '⚠ No code restore'] })] }, point.messageId));
5898
5968
  }), window.end < menu.points.length ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2193 ", menu.points.length - window.end, " more below"] })) : null] }));
5899
- })(), _jsxs(Text, { inverse: menu.selectedIndex === menu.points.length, children: [menu.selectedIndex === menu.points.length ? ' ❯ ' : ' ', "(current)"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter to continue \u00B7 Esc to cancel" })] })) : menu?.kind === 'rewind-confirm' ? (_jsxs(DialogFrame, { title: "Confirm restore point", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["\u2502 ", menu.point.prompt] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "The conversation will be forked." }), _jsxs(Text, { children: ["The code will", ' ', menu.point.fileChanges.length > 0
5969
+ })(), _jsx(DecisionOption, { selected: menu.selectedIndex === menu.points.length, screenReader: axScreenReader, selectedPrefix: " \u276F ", unselectedPrefix: " ", children: "(current)" }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter to continue \u00B7 Esc to cancel" })] })) : menu?.kind === 'rewind-confirm' ? (_jsxs(DialogFrame, { title: "Confirm restore point", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["\u2502 ", menu.point.prompt] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "The conversation will be forked." }), _jsxs(Text, { children: ["The code will", ' ', menu.point.fileChanges.length > 0
5900
5970
  ? `restore ${menu.point.fileChanges.join(', ')}.`
5901
- : 'be unchanged.'] }), _jsx(Text, { children: " " }), rewindActions(menu.point).map((option, index) => (_jsxs(Text, { inverse: menu.selectedIndex === index, children: [menu.selectedIndex === index ? '❯ ' : ' ', index + 1, ". ", option.label] }, option.action))), _jsx(Text, { children: " " }), _jsx(Text, { color: activePalette.warning, children: "\u26A0 Rewinding does not affect files edited manually or via bash." })] })) : menu?.kind === 'rewind-context' ? (_jsxs(DialogFrame, { title: `Summarize ${menu.direction === 'from' ? 'from' : 'up to'} here`, screenReader: axScreenReader, children: [_jsx(Text, { children: menu.direction === 'from'
5971
+ : 'be unchanged.'] }), _jsx(Text, { children: " " }), rewindActions(menu.point).map((option, index) => (_jsxs(DecisionOption, { selected: menu.selectedIndex === index, screenReader: axScreenReader, children: [index + 1, ". ", option.label] }, option.action))), _jsx(Text, { children: " " }), _jsx(RewindWarning, {})] })) : menu?.kind === 'rewind-context' ? (_jsxs(DialogFrame, { title: `Summarize ${menu.direction === 'from' ? 'from' : 'up to'} here`, screenReader: axScreenReader, children: [_jsx(Text, { children: menu.direction === 'from'
5902
5972
  ? 'Messages after this point will be summarized.'
5903
- : 'Messages up to this point will be summarized.' }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["add context (optional): ", input] }), _jsx(Text, { dimColor: true, children: "Enter to summarize \u00B7 Esc to go back" })] })) : menu?.kind === 'permission-rule-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: ["Add ", menu.behavior, " permission rule"] }), _jsx(Text, { children: "Permission rules are a tool name, optionally followed by a specifier in parentheses." }), _jsx(Text, { children: "e.g., WebFetch or Bash(ls *)" }), _jsx(Text, { children: " " }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Enter permission rule…' }) }), _jsx(Text, { dimColor: true, children: "Enter to submit \u00B7 Esc to cancel" })] })) : menu?.kind === 'workspace-directory-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Add directory to workspace" }), _jsx(Text, { children: "Praxis Code will be able to read files in this directory and make edits when auto-accept edits is on." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Enter the path to the directory:" }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Directory path…' }) }), _jsx(Text, { dimColor: true, children: "Tab to complete \u00B7 Enter to add \u00B7 Esc to cancel" })] })) : menu?.kind === 'permission-delete' ? (_jsxs(DialogFrame, { title: permissionDeleteTitle(menu.rule.behavior), screenReader: axScreenReader, children: [_jsx(Text, { children: menu.rule.rule }), permissionRuleDescription(menu.rule.rule) ? (_jsx(Text, { dimColor: true, children: permissionRuleDescription(menu.rule.rule) })) : null, _jsx(Text, { dimColor: true, children: permissionScopeLabel(menu.rule.scope) }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Are you sure you want to delete this permission rule?" }), _jsx(Text, { children: " " }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 0, children: [selectionPrefix(menu.selectedIndex === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 1, children: [selectionPrefix(menu.selectedIndex === 1, axScreenReader), "2. No"] }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] })) : menu?.kind === 'workspace-directory-delete' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Remove directory from workspace?" }), _jsxs(Text, { children: [" ", menu.path] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Praxis Code will no longer have access to files in this directory." }), _jsx(Text, { children: " " }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 0, children: [selectionPrefix(menu.selectedIndex === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 1, children: [selectionPrefix(menu.selectedIndex === 1, axScreenReader), "2. No"] }), _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Esc to cancel" })] })) : menu ? (menu.kind === 'help' ? (_jsx(HelpMenu, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, builtinCommands: builtinSlashCommands, customCommands: customSlashCommands, width: width, screenReader: axScreenReader })) : menu.kind === 'diff' ? (_jsx(DiffDashboard, { snapshots: menu.snapshots, sourceIndex: menu.sourceIndex, selectedIndex: menu.selectedIndex, viewingFile: menu.viewingFile, scrollOffset: menu.scrollOffset, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-dashboard' ? (_jsx(PermissionDashboard, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, query: menu.query, rules: menu.rules, recentDenied: recentDenied, retryingDeniedId: retryingDeniedId, workspaceDirectories: workspaceDirectories, width: width, screenReader: axScreenReader })) : menu.kind === 'sandbox' ? (_jsx(SandboxDashboard, { snapshot: menu.snapshot, tab: menu.tab, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-scope' ? (_jsx(SelectionMenu, { title: "Where should this rule be saved?", description: `${menu.rule}${permissionRuleDescription(menu.rule)
5973
+ : 'Messages up to this point will be summarized.' }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["add context (optional): ", input] }), _jsx(Text, { dimColor: true, children: "Enter to summarize \u00B7 Esc to go back" })] })) : menu?.kind === 'permission-rule-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: ["Add ", menu.behavior, " permission rule"] }), _jsx(Text, { children: "Permission rules are a tool name, optionally followed by a specifier in parentheses." }), _jsx(Text, { children: "e.g., WebFetch or Bash(ls *)" }), _jsx(Text, { children: " " }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Enter permission rule…' }) }), _jsx(Text, { dimColor: true, children: "Enter to submit \u00B7 Esc to cancel" })] })) : menu?.kind === 'workspace-directory-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Add directory to workspace" }), _jsx(Text, { children: "Praxis Code will be able to read files in this directory and make edits when auto-accept edits is on." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Enter the path to the directory:" }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Directory path…' }) }), _jsx(Text, { dimColor: true, children: "Tab to complete \u00B7 Enter to add \u00B7 Esc to cancel" })] })) : menu?.kind === 'permission-delete' ? (_jsxs(DialogFrame, { title: permissionDeleteTitle(menu.rule.behavior), screenReader: axScreenReader, children: [_jsx(Text, { children: menu.rule.rule }), permissionRuleDescription(menu.rule.rule) ? (_jsx(Text, { dimColor: true, children: permissionRuleDescription(menu.rule.rule) })) : null, _jsx(Text, { dimColor: true, children: permissionScopeLabel(menu.rule.scope) }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Are you sure you want to delete this permission rule?" }), _jsx(Text, { children: " " }), _jsx(DecisionOption, { selected: menu.selectedIndex === 0, screenReader: axScreenReader, children: "1. Yes" }), _jsx(DecisionOption, { selected: menu.selectedIndex === 1, screenReader: axScreenReader, children: "2. No" }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] })) : menu?.kind === 'workspace-directory-delete' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Remove directory from workspace?" }), _jsxs(Text, { children: [" ", menu.path] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Praxis Code will no longer have access to files in this directory." }), _jsx(Text, { children: " " }), _jsx(DecisionOption, { selected: menu.selectedIndex === 0, screenReader: axScreenReader, children: "1. Yes" }), _jsx(DecisionOption, { selected: menu.selectedIndex === 1, screenReader: axScreenReader, children: "2. No" }), _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Esc to cancel" })] })) : menu ? (menu.kind === 'help' ? (_jsx(HelpMenu, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, builtinCommands: builtinSlashCommands, customCommands: customSlashCommands, width: width, screenReader: axScreenReader })) : menu.kind === 'diff' ? (_jsx(DiffDashboard, { snapshots: menu.snapshots, sourceIndex: menu.sourceIndex, selectedIndex: menu.selectedIndex, viewingFile: menu.viewingFile, scrollOffset: menu.scrollOffset, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-dashboard' ? (_jsx(PermissionDashboard, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, query: menu.query, rules: menu.rules, recentDenied: recentDenied, retryingDeniedId: retryingDeniedId, workspaceDirectories: workspaceDirectories, width: width, screenReader: axScreenReader })) : menu.kind === 'sandbox' ? (_jsx(SandboxDashboard, { snapshot: menu.snapshot, tab: menu.tab, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-scope' ? (_jsx(SelectionMenu, { title: "Where should this rule be saved?", description: `${menu.rule}${permissionRuleDescription(menu.rule)
5904
5974
  ? ` · ${permissionRuleDescription(menu.rule)}`
5905
5975
  : ''}`, options: [
5906
5976
  {
@@ -5947,7 +6017,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5947
6017
  label: 'Save to file',
5948
6018
  description: 'Save the conversation to a file in the current directory',
5949
6019
  },
5950
- ], selectedIndex: menu.selectedIndex, footer: "Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'copy' ? (_jsx(SelectionMenu, { title: "Copy", description: "Select content to copy:", options: menu.candidates, selectedIndex: menu.selectedIndex, footer: "Enter to copy \u00B7 w to write to /tmp/claude \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : null) : (_jsxs(_Fragment, { children: [commandPaletteVisible ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, filePickerVisible ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, exitConfirmation ? (_jsx(Text, { color: activePalette.warning, children: "Press Ctrl-C again to exit" })) : null, fixedViewport ? _jsx(Box, { flexGrow: 1 }) : null, _jsx(Composer, { input: shellMode ? input.slice(1) : input, cursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor, shellMode: shellMode, ...(sessionColor === undefined ? {} : { sessionColor }), ...(commandArgumentHint === undefined
6020
+ ], selectedIndex: menu.selectedIndex, footer: "Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'copy' ? (_jsx(SelectionMenu, { title: "Copy", description: "Select content to copy:", options: menu.candidates, selectedIndex: menu.selectedIndex, footer: "Enter to copy \u00B7 w to write to /tmp/claude \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : null) : (_jsxs(_Fragment, { children: [commandPaletteVisible ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, filePickerVisible ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, exitConfirmation ? _jsx(ExitWarning, {}) : null, fixedViewport ? _jsx(Box, { flexGrow: 1 }) : null, _jsx(Composer, { input: shellMode ? input.slice(1) : input, cursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor, shellMode: shellMode, ...(sessionColor === undefined ? {} : { sessionColor }), ...(commandArgumentHint === undefined
5951
6021
  ? {}
5952
6022
  : { commandArgumentHint }), busy: busy, clipboardBusy: clipboardBusy, status: status, display: runtimeDisplay, ...(usage === undefined ? {} : { usage }), ...(costUsd === undefined ? {} : { costUsd }), width: width, screenReader: axScreenReader, hasThinking: hasDetailedTranscript, thinkingExpanded: thinkingExpanded, reduceMotion: runtimeSettings.reduceMotion, progressBar: runtimeSettings.progressBar, ...(runtimeSettings.turnDuration
5953
6023
  ? (() => {