praxis-agent 0.35.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 (38) hide show
  1. package/README.md +5 -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 -4
  7. package/dist/application/session-service.js +528 -434
  8. package/dist/application/subagent-service.js +358 -177
  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 +2 -0
  14. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  15. package/dist/application/turn-memory-coordinator.js +98 -0
  16. package/dist/cli/agents-dashboard.js +10 -5
  17. package/dist/cli/interactive.js +264 -204
  18. package/dist/cli/tui/claude-style.js +154 -130
  19. package/dist/cli/tui/task-panel.js +10 -1
  20. package/dist/cli/tui/theme.d.ts +46 -3
  21. package/dist/cli/tui/theme.js +414 -56
  22. package/dist/cli/tui/tool-permission.js +9 -5
  23. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  24. package/dist/cli/tui/tui-interaction-router.js +127 -0
  25. package/dist/cli-runtime.d.ts +2 -2
  26. package/dist/core/agent-orchestration.d.ts +31 -0
  27. package/dist/core/agent-orchestration.js +235 -0
  28. package/dist/core/runtime.d.ts +5 -0
  29. package/dist/core/runtime.js +15 -1
  30. package/dist/persistence/claude-job-store.d.ts +58 -0
  31. package/dist/persistence/claude-job-store.js +483 -1
  32. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  33. package/dist/persistence/native-transcript-reader.js +283 -0
  34. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  35. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  36. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  37. package/dist/platform/exclusive-file-lease.js +3 -0
  38. package/package.json +1 -1
@@ -26,18 +26,19 @@ import { agentColorMessage, parseAgentColorInput, } from '../compatibility/claud
26
26
  import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
27
27
  import { runDoctor, } from '../maintenance/doctor.js';
28
28
  import { canonicalClaudeCostModelName, formatCostSummary, } from './tui/cost-summary.js';
29
- 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';
30
30
  import { routeComposerKey, } from './tui/composer-key-router.js';
31
+ import { routeTuiInteraction, } from './tui/tui-interaction-router.js';
31
32
  import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
32
33
  import { editTuiPrompt, openTuiEditorFile, } from './tui/external-editor.js';
33
34
  import { suspendTuiProcess } from './tui/terminal-suspend.js';
34
35
  import { readTuiClipboard, writeTuiClipboard, writeTuiOsc52Clipboard, } from './tui/clipboard.js';
35
- import { composerImageIds, deleteComposerImageBackward, deleteComposerImageForward, insertComposerImageMarker, moveComposerCursorAcrossImages, } from './tui/composer-images.js';
36
+ import { composerImageIds, insertComposerImageMarker, } from './tui/composer-images.js';
36
37
  import { defaultTuiKeybindings, ensureTuiKeybindingsFile, hasTuiKeybindingPrefix, loadTuiKeybindings, resolveTuiKeybinding, tuiKeyChord, } from './tui/keybindings.js';
37
38
  import { completeTuiWorkspaceDirectory, resolveTuiWorkspaceDirectory, } from './tui/workspace-directories.js';
38
39
  import { conversationExportPath, conversationExportText, defaultConversationExportFilename, writeConversationExport, } from './tui/conversation-export.js';
39
40
  import { loadTuiThemeSettings, saveTuiThemeSettings, themeSettingsWithCustomTheme, } from './tui/theme-settings.js';
40
- 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';
41
42
  import { CUSTOM_THEME_TOKENS, createTuiCustomTheme, deleteTuiCustomTheme, loadTuiCustomThemes, updateTuiCustomTheme, } from './tui/custom-themes.js';
42
43
  import { setupTuiTerminal, terminalSetupTuiSlashCommand, } from './tui/terminal-setup.js';
43
44
  import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid, elicitationTextValue, expandElicitationOptions, focusedElicitationField, McpElicitationForm, McpElicitationUrl, moveElicitationFocus, moveElicitationOption, selectElicitationOption, toggleElicitationBoolean, typeaheadElicitationOption, unsetElicitationField, validateTuiElicitationForm, } from './tui/mcp-elicitation.js';
@@ -111,11 +112,6 @@ const ZERO_COST_SUMMARY = {
111
112
  hasUnknownModelCost: false,
112
113
  modelUsage: [],
113
114
  };
114
- function selectionPrefix(selected, screenReader) {
115
- if (selected)
116
- return screenReader ? 'Selected: ' : '❯ ';
117
- return screenReader ? '' : ' ';
118
- }
119
115
  function rewindActions(point) {
120
116
  const shared = [
121
117
  { action: 'summarize-from', label: 'Summarize from here' },
@@ -266,6 +262,25 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
266
262
  'update',
267
263
  'usage',
268
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
+ }
269
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, }) {
270
285
  const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
271
286
  const width = useTerminalWidth(terminalWidth);
@@ -345,9 +360,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
345
360
  runtimeSettingsRef.current = runtimeSettings;
346
361
  runtimeGitignoreRef.current = runtimeSettings.gitignore;
347
362
  const [vimInsertMode, setVimInsertMode] = useState(true);
348
- const activePalette = tuiPalette(TUI_THEMES.includes(themeSettings.theme)
349
- ? themeSettings.theme
350
- : 'dark', themeSettings.syntaxHighlightingDisabled, process.env, themeSettings.customTheme);
351
363
  const choices = useMemo(() => allowNewSession ? [null, ...initialSessions] : initialSessions, [allowNewSession, initialSessions]);
352
364
  const [pickerIncludesNewSession, setPickerIncludesNewSession] = useState(allowNewSession);
353
365
  const pickerIncludesNewSessionRef = useRef(allowNewSession);
@@ -449,7 +461,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
449
461
  const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
450
462
  const [history, setHistory] = useState([...initialHistory]);
451
463
  const activeAttemptThinkingItemsRef = useRef([]);
452
- 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
+ };
453
470
  // The pure TUI view model classifies the session identity (fresh/resumed/
454
471
  // started) and projects the rendered transcript. Startup diagnostics are
455
472
  // useful before the first prompt, but they are not conversation history and
@@ -1152,25 +1169,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
1152
1169
  exitConfirmationTimerRef.current = null;
1153
1170
  setExitConfirmation(false);
1154
1171
  };
1155
- const armExitConfirmation = () => {
1156
- if (exitConfirmation) {
1157
- permissionRef.current?.resolve(false);
1158
- elicitationRef.current?.resolve({ action: 'cancel' });
1159
- questionRef.current?.resolve(null);
1160
- planApprovalRef.current?.resolve({ behavior: 'deny' });
1161
- onCancel?.();
1162
- exit();
1163
- return;
1164
- }
1165
- clearComposerInput();
1166
- setExitConfirmation(true);
1167
- if (exitConfirmationTimerRef.current)
1168
- clearTimeout(exitConfirmationTimerRef.current);
1169
- exitConfirmationTimerRef.current = setTimeout(() => {
1170
- exitConfirmationTimerRef.current = null;
1171
- setExitConfirmation(false);
1172
- }, 1_500);
1173
- };
1174
1172
  useEffect(() => {
1175
1173
  setCommandSelection((current) => Math.min(current, Math.max(0, matchingSlashCommands.length - 1)));
1176
1174
  }, [matchingSlashCommands.length]);
@@ -2872,74 +2870,150 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2872
2870
  const codePoint = character.codePointAt(0) ?? 0;
2873
2871
  return codePoint >= 32 && codePoint !== 127;
2874
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
+ };
2875
2883
  const editor = () => createComposerEditor(inputRef.current, inputCursorRef.current);
2876
2884
  const editComposer = () => {
2877
- if (key.leftArrow) {
2878
- updateComposerEditor(key.meta
2879
- ? moveComposerCursorByWord(editor(), 'backward')
2880
- : moveComposerCursorAcrossImages(editor(), -1));
2881
- return true;
2882
- }
2883
- if (key.rightArrow) {
2884
- updateComposerEditor(key.meta
2885
- ? moveComposerCursorByWord(editor(), 'forward')
2886
- : moveComposerCursorAcrossImages(editor(), 1));
2887
- return true;
2888
- }
2889
- if (controlKey('a')) {
2890
- updateComposerEditor(createComposerEditor(inputRef.current, 0));
2891
- return true;
2892
- }
2893
- if (controlKey('e')) {
2894
- updateComposerEditor(createComposerEditor(inputRef.current));
2895
- return true;
2896
- }
2897
- if (controlKey('b')) {
2898
- updateComposerEditor(moveComposerCursorAcrossImages(editor(), -1));
2899
- return true;
2900
- }
2901
- if (controlKey('f')) {
2902
- updateComposerEditor(moveComposerCursorAcrossImages(editor(), 1));
2903
- return true;
2904
- }
2905
- if (controlKey('w')) {
2906
- updateComposerEditor(deleteComposerWordBackward(editor()));
2907
- return true;
2908
- }
2909
- if (controlKey('u')) {
2910
- updateComposerEditor(deleteComposerToStart(editor()));
2911
- return true;
2912
- }
2913
- if (controlKey('k')) {
2914
- updateComposerEditor(deleteComposerToEnd(editor()));
2915
- return true;
2916
- }
2917
- if (key.backspace) {
2918
- updateComposerEditor(deleteComposerImageBackward(editor()));
2885
+ const transition = routeComposerKey(editor(), composerKey);
2886
+ if (transition.kind === 'edit') {
2887
+ updateComposerEditor(transition.editor);
2919
2888
  return true;
2920
2889
  }
2921
- if (key.delete) {
2922
- updateComposerEditor(deleteComposerImageForward(editor()));
2923
- return true;
2924
- }
2925
- if (!key.ctrl && !key.meta && value && printable) {
2926
- updateComposerEditor(insertComposerText(editor(), value));
2927
- return true;
2928
- }
2929
- return false;
2890
+ if (transition.kind === 'cancel')
2891
+ clearComposerInput();
2892
+ return transition.kind === 'cancel';
2930
2893
  };
2931
- if (controlKey('c')) {
2932
- armExitConfirmation();
2933
- return;
2934
- }
2935
- if (controlKey('z')) {
2936
- if (!processSuspendRequestedRef.current) {
2937
- processSuspendRequestedRef.current = true;
2938
- 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
+ }
2939
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);
2940
3015
  return;
2941
3016
  }
2942
- dismissExitConfirmation();
2943
3017
  const keybindingContexts = menuRef.current
2944
3018
  ? menuRef.current.kind === 'diff'
2945
3019
  ? ['DiffDialog']
@@ -2976,57 +3050,90 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
2976
3050
  if (!keybindingAction) {
2977
3051
  keybindingAction = resolveTuiKeybinding(keybindings, keybindingContexts, inputChord);
2978
3052
  }
2979
- if (!keybindingAction &&
2980
- inputChord &&
2981
- 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) {
2982
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') {
2983
3132
  return;
2984
3133
  }
2985
3134
  const isKeybinding = (action) => keybindingAction === action;
2986
- if (fixedViewport &&
2987
- menuRef.current === null &&
2988
- !permission &&
2989
- !planApproval &&
2990
- !question &&
2991
- !elicitation &&
2992
- !selectingSession) {
2993
- const page = transcriptPageRows;
2994
- const scrollDelta = key.pageUp
2995
- ? page
2996
- : key.pageDown
2997
- ? -page
2998
- : controlKey('u') || controlKey('b')
2999
- ? page / 2
3000
- : controlKey('f') || controlKey('n')
3001
- ? -page / 2
3002
- : inputRef.current.length === 0 && key.upArrow
3003
- ? 1
3004
- : inputRef.current.length === 0 && key.downArrow
3005
- ? -1
3006
- : 0;
3007
- if (scrollDelta !== 0) {
3008
- setTranscriptScrollOffset((current) => Math.min(Math.max(0, maxTranscriptScrollOffset), Math.max(0, current + Math.trunc(scrollDelta))));
3009
- return;
3010
- }
3011
- }
3012
- if (runtimeSettingsRef.current.editor === 'vim' &&
3013
- !permission &&
3014
- !planApproval &&
3015
- !question &&
3016
- !elicitation &&
3017
- menuRef.current === null) {
3018
- if (!vimInsertMode) {
3019
- if (value === 'i' || value === 'a')
3020
- setVimInsertMode(true);
3021
- else if (key.leftArrow || key.rightArrow)
3022
- editComposer();
3023
- return;
3024
- }
3025
- if (key.escape || value === '\u001B') {
3026
- setVimInsertMode(false);
3027
- return;
3028
- }
3029
- }
3135
+ if (hasPendingPrefix)
3136
+ return;
3030
3137
  if (permission) {
3031
3138
  const options = permission.kind === 'tool' && toolPermissionModel
3032
3139
  ? toolPermissionModel.options
@@ -3172,10 +3279,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3172
3279
  }
3173
3280
  permission.resolve(feedback ? { behavior: 'deny', message: feedback } : false);
3174
3281
  };
3175
- if (key.escape) {
3176
- permission.resolve(false);
3177
- }
3178
- else if (permissionFeedbackMode) {
3282
+ if (permissionFeedbackMode) {
3179
3283
  if (key.tab) {
3180
3284
  clearComposerInput();
3181
3285
  setPermissionFeedbackMode(false);
@@ -3256,10 +3360,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3256
3360
  ...(feedback ? { feedback } : {}),
3257
3361
  });
3258
3362
  };
3259
- if (key.escape) {
3260
- planApproval.resolve({ behavior: 'deny' });
3261
- }
3262
- else if (planApprovalFeedbackMode) {
3363
+ if (planApprovalFeedbackMode) {
3263
3364
  if (key.tab) {
3264
3365
  clearComposerInput();
3265
3366
  setPlanApprovalFeedbackMode(false);
@@ -3296,10 +3397,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3296
3397
  return;
3297
3398
  }
3298
3399
  if (question) {
3299
- if (key.escape) {
3300
- question.resolve(null);
3301
- }
3302
- else if (key.return) {
3400
+ if (key.return) {
3303
3401
  try {
3304
3402
  const current = question.questions[question.index];
3305
3403
  if (!current)
@@ -3353,13 +3451,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3353
3451
  setElicitationForm(null);
3354
3452
  clearComposerInput();
3355
3453
  };
3356
- if (key.escape) {
3357
- if (elicitationUrlWaiting)
3358
- dismissUrlDialog();
3359
- else
3360
- elicitation.resolve({ action: 'cancel' });
3361
- }
3362
- else if (key.leftArrow || key.rightArrow) {
3454
+ if (key.leftArrow || key.rightArrow) {
3363
3455
  setElicitationForm({
3364
3456
  ...form,
3365
3457
  focusIndex: form.focusIndex === 0 ? 1 : 0,
@@ -3385,7 +3477,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3385
3477
  return;
3386
3478
  }
3387
3479
  if (form.expandedField && currentField) {
3388
- if (key.escape || key.leftArrow) {
3480
+ if (key.leftArrow) {
3389
3481
  setElicitationForm({ ...form, expandedField: undefined });
3390
3482
  }
3391
3483
  else if (key.upArrow) {
@@ -3404,9 +3496,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3404
3496
  setElicitationForm(typeaheadElicitationOption(form, value));
3405
3497
  }
3406
3498
  }
3407
- else if (key.escape) {
3408
- elicitation.resolve({ action: 'cancel' });
3409
- }
3410
3499
  else if (key.upArrow) {
3411
3500
  move(-1);
3412
3501
  }
@@ -5175,8 +5264,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5175
5264
  void backgrounding;
5176
5265
  return;
5177
5266
  }
5178
- if (isKeybinding('chat:cancel'))
5179
- turnControllerRef.current?.abort();
5180
5267
  return;
5181
5268
  }
5182
5269
  if (isKeybinding('chat:imagePaste')) {
@@ -5192,10 +5279,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5192
5279
  return;
5193
5280
  }
5194
5281
  if (filePickerVisible) {
5195
- if (key.escape || value === '\u001B') {
5196
- setFilePickerOpen(false);
5197
- return;
5198
- }
5199
5282
  if (key.upArrow) {
5200
5283
  setFileSelection((current) => Math.max(0, current - 1));
5201
5284
  return;
@@ -5220,13 +5303,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5220
5303
  updateMenu({ kind: 'agents', agents: availableAgents, selectedIndex: 0 });
5221
5304
  return;
5222
5305
  }
5223
- if (isKeybinding('chat:cancel')) {
5224
- const now = Date.now();
5225
- if (now - lastEscapeAtRef.current <= 500)
5226
- clearComposerInput();
5227
- lastEscapeAtRef.current = now;
5228
- return;
5229
- }
5230
5306
  if (isKeybinding('chat:cycleMode')) {
5231
5307
  const currentIndex = permissionOptions.findIndex((option) => option.mode === runtimePreferences.permissionMode);
5232
5308
  const next = permissionOptions[(currentIndex + 1) % permissionOptions.length];
@@ -5235,10 +5311,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5235
5311
  return;
5236
5312
  }
5237
5313
  if (commandPaletteVisible) {
5238
- if (key.escape) {
5239
- setCommandPaletteOpen(false);
5240
- return;
5241
- }
5242
5314
  if (key.upArrow) {
5243
5315
  if (matchingSlashCommands.length > 0) {
5244
5316
  setCommandSelection((current) => Math.max(0, current - 1));
@@ -5842,24 +5914,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5842
5914
  clearComposerInput();
5843
5915
  return;
5844
5916
  }
5845
- const transition = routeComposerKey(editor(), {
5846
- value,
5847
- left: key.leftArrow,
5848
- right: key.rightArrow,
5849
- backspace: key.backspace,
5850
- delete: key.delete,
5851
- ctrl: key.ctrl,
5852
- meta: key.meta,
5853
- escape: key.escape || value === '\u001B',
5854
- });
5855
- if (transition.kind === 'cancel') {
5856
- clearComposerInput();
5857
- }
5858
- else if (transition.kind === 'edit') {
5859
- updateComposerEditor(transition.editor);
5917
+ if (filePickerVisible || commandPaletteVisible) {
5918
+ editComposer();
5919
+ return;
5860
5920
  }
5861
5921
  });
5862
- 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
5863
5923
  ? {}
5864
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
5865
5925
  ? {
@@ -5869,16 +5929,16 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5869
5929
  }
5870
5930
  : {}), children: _jsx(Transcript, { entries: transcriptEntries, activeText: activeText, activeThinking: activeThinking, activeStreamVisible: transcriptScrollOffset === 0, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }) }), externalEditorRequest !== null ||
5871
5931
  keybindingsEditing ||
5872
- 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 ||
5873
5933
  (permissionSelection === 0
5874
5934
  ? 'tell Praxis what to do next'
5875
5935
  : 'tell Praxis what to do differently')] })) : null, _jsx(Text, { dimColor: true, children: permissionFeedbackMode
5876
5936
  ? 'Enter to submit · Tab to collapse · Esc to cancel'
5877
- : '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
5878
5938
  ? 'Yes, and use auto mode'
5879
5939
  : allowDangerouslySkipPermissions
5880
5940
  ? 'Yes, and bypass permissions'
5881
- : '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 ||
5882
5942
  (planApprovalSelection === 2
5883
5943
  ? 'Tell Praxis what to change'
5884
5944
  : 'Add feedback for implementation')] })) : null, _jsx(Text, { dimColor: true, children: planApproval.request.planPath }), _jsx(Text, { dimColor: true, children: planApprovalFeedbackMode
@@ -5894,9 +5954,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5894
5954
  .slice(window.start, window.end)
5895
5955
  .map((point, offset) => {
5896
5956
  const index = window.start + offset;
5897
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { inverse: menu.selectedIndex === index, children: [menu.selectedIndex === index ? ' ❯ ' : ' ', point.prompt
5898
- .replace(/\s+/gu, ' ')
5899
- .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
5900
5960
  ? point.fileChanges
5901
5961
  .map((path) => path.startsWith(`${runtimeCwd}/`)
5902
5962
  ? path.slice(runtimeCwd.length + 1)
@@ -5906,11 +5966,11 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5906
5966
  ? 'No code changes'
5907
5967
  : '⚠ No code restore'] })] }, point.messageId));
5908
5968
  }), window.end < menu.points.length ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2193 ", menu.points.length - window.end, " more below"] })) : null] }));
5909
- })(), _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
5910
5970
  ? `restore ${menu.point.fileChanges.join(', ')}.`
5911
- : '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'
5912
5972
  ? 'Messages after this point will be summarized.'
5913
- : '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)
5914
5974
  ? ` · ${permissionRuleDescription(menu.rule)}`
5915
5975
  : ''}`, options: [
5916
5976
  {
@@ -5957,7 +6017,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
5957
6017
  label: 'Save to file',
5958
6018
  description: 'Save the conversation to a file in the current directory',
5959
6019
  },
5960
- ], 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
5961
6021
  ? {}
5962
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
5963
6023
  ? (() => {