praxis-agent 0.35.0 → 0.37.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.
- package/README.md +20 -2
- package/dist/application/background-agent-manager.d.ts +16 -6
- package/dist/application/background-agent-manager.js +139 -97
- package/dist/application/context-engine.d.ts +56 -0
- package/dist/application/context-engine.js +129 -0
- package/dist/application/durable-follow-up.d.ts +14 -0
- package/dist/application/durable-follow-up.js +52 -0
- package/dist/application/native-session-transcript.d.ts +72 -0
- package/dist/application/native-session-transcript.js +351 -0
- package/dist/application/native-sidechain-transcript.d.ts +34 -0
- package/dist/application/native-sidechain-transcript.js +156 -0
- package/dist/application/session-service.d.ts +23 -4
- package/dist/application/session-service.js +1130 -494
- package/dist/application/subagent-service.d.ts +5 -1
- package/dist/application/subagent-service.js +749 -320
- package/dist/application/team-agent-runtime.d.ts +29 -0
- package/dist/application/team-agent-runtime.js +169 -0
- package/dist/application/team-capability.d.ts +27 -0
- package/dist/application/team-capability.js +41 -0
- package/dist/application/team-lead-operations.d.ts +38 -0
- package/dist/application/team-lead-operations.js +155 -0
- package/dist/application/team-mailbox.d.ts +54 -0
- package/dist/application/team-mailbox.js +196 -0
- package/dist/application/team-manager.d.ts +113 -0
- package/dist/application/team-manager.js +681 -0
- package/dist/application/team-workspace.d.ts +29 -0
- package/dist/application/team-workspace.js +197 -0
- package/dist/application/top-level-agent-manager.d.ts +1 -0
- package/dist/application/top-level-agent-manager.js +1025 -403
- package/dist/application/transcript-projection.d.ts +35 -0
- package/dist/application/transcript-projection.js +139 -0
- package/dist/application/turn-lifecycle.d.ts +2 -0
- package/dist/application/turn-memory-coordinator.d.ts +43 -0
- package/dist/application/turn-memory-coordinator.js +98 -0
- package/dist/cli/agents-dashboard.js +10 -5
- package/dist/cli/interactive.d.ts +7 -0
- package/dist/cli/interactive.js +645 -334
- package/dist/cli/tui/claude-style.d.ts +8 -25
- package/dist/cli/tui/claude-style.js +299 -277
- package/dist/cli/tui/decision-surface-model.d.ts +71 -0
- package/dist/cli/tui/decision-surface-model.js +131 -0
- package/dist/cli/tui/decision-surface.d.ts +7 -0
- package/dist/cli/tui/decision-surface.js +41 -0
- package/dist/cli/tui/help-surface-model.d.ts +68 -0
- package/dist/cli/tui/help-surface-model.js +131 -0
- package/dist/cli/tui/mcp-panel.js +1 -2
- package/dist/cli/tui/permission-surface-model.d.ts +195 -0
- package/dist/cli/tui/permission-surface-model.js +360 -0
- package/dist/cli/tui/permission-surface.d.ts +7 -0
- package/dist/cli/tui/permission-surface.js +69 -0
- package/dist/cli/tui/presentation-environment.d.ts +32 -0
- package/dist/cli/tui/presentation-environment.js +149 -0
- package/dist/cli/tui/task-panel.js +10 -1
- package/dist/cli/tui/theme.d.ts +46 -3
- package/dist/cli/tui/theme.js +414 -56
- package/dist/cli/tui/tool-permission.js +16 -8
- package/dist/cli/tui/transcript-presentation.d.ts +19 -1
- package/dist/cli/tui/transcript-presentation.js +26 -6
- package/dist/cli/tui/transcript-viewport.d.ts +18 -0
- package/dist/cli/tui/transcript-viewport.js +517 -111
- package/dist/cli/tui/transcript-window-model.d.ts +108 -0
- package/dist/cli/tui/transcript-window-model.js +772 -0
- package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
- package/dist/cli/tui/tui-interaction-router.js +127 -0
- package/dist/cli/tui/tui-screen-model.d.ts +114 -0
- package/dist/cli/tui/tui-screen-model.js +210 -0
- package/dist/cli/tui/tui-view-model.d.ts +2 -49
- package/dist/cli/tui/tui-view-model.js +1 -68
- package/dist/cli-runtime.d.ts +4 -2
- package/dist/cli-runtime.js +397 -32
- package/dist/compatibility/claude/transcript-codec.js +6 -0
- package/dist/core/agent-orchestration.d.ts +31 -0
- package/dist/core/agent-orchestration.js +235 -0
- package/dist/core/permission-policy.d.ts +4 -0
- package/dist/core/permission-policy.js +21 -0
- package/dist/core/runtime.d.ts +6 -0
- package/dist/core/runtime.js +20 -2
- package/dist/core/team-mailbox.d.ts +54 -0
- package/dist/core/team-mailbox.js +212 -0
- package/dist/core/team-ownership.d.ts +84 -0
- package/dist/core/team-ownership.js +505 -0
- package/dist/core/tool-execution-scheduler.js +6 -1
- package/dist/core/transcript-event.d.ts +5 -1
- package/dist/core/transcript-event.js +12 -0
- package/dist/persistence/claude-job-store.d.ts +58 -0
- package/dist/persistence/claude-job-store.js +483 -1
- package/dist/persistence/claude-transcript-store.d.ts +2 -1
- package/dist/persistence/claude-transcript-store.js +2 -10
- package/dist/persistence/native-team-mailbox.d.ts +68 -0
- package/dist/persistence/native-team-mailbox.js +542 -0
- package/dist/persistence/native-team-store.d.ts +34 -0
- package/dist/persistence/native-team-store.js +257 -0
- package/dist/persistence/native-transcript-reader.d.ts +53 -0
- package/dist/persistence/native-transcript-reader.js +283 -0
- package/dist/persistence/native-transcript-store.d.ts +69 -0
- package/dist/persistence/native-transcript-store.js +321 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
- package/dist/persistence/subagent-lifecycle-store.js +373 -146
- package/dist/persistence/transcript-file-append.d.ts +2 -0
- package/dist/persistence/transcript-file-append.js +10 -0
- package/dist/platform/exclusive-file-lease.d.ts +3 -0
- package/dist/platform/exclusive-file-lease.js +3 -0
- package/dist/tools/claude-capabilities.d.ts +4 -0
- package/dist/tools/claude-capabilities.js +18 -2
- package/dist/tools/team-lead-tools.d.ts +19 -0
- package/dist/tools/team-lead-tools.js +479 -0
- package/dist/tools/team-member-tools.d.ts +21 -0
- package/dist/tools/team-member-tools.js +259 -0
- package/package.json +1 -1
- package/dist/cli/tui/fullscreen-renderer.d.ts +0 -14
- package/dist/cli/tui/fullscreen-renderer.js +0 -13
package/dist/cli/interactive.js
CHANGED
|
@@ -12,13 +12,16 @@ import { resolveDataPlane, resolveDataPlaneRoot, } from '../persistence/data-pla
|
|
|
12
12
|
import { AgentRunCancelledError } from '../core/runtime.js';
|
|
13
13
|
import { claudePermissionActionKey, claudePermissionRuleMatches, } from '../permissions/claude-permission-resolver.js';
|
|
14
14
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
15
|
-
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu,
|
|
15
|
+
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu, SelectionMenu, SessionPicker, ThemePicker, CustomThemeEditor, SessionIdentity, Transcript, WelcomePanel, } from './tui/claude-style.js';
|
|
16
16
|
import { loadTuiMemoryFiles, openTuiMemoryFolder, } from './tui/memory-files.js';
|
|
17
17
|
import { loadClaudeReleaseNotes } from './tui/release-notes.js';
|
|
18
|
-
import {
|
|
18
|
+
import { tuiInkRenderOptions, useTuiPresentationEnvironment, } from './tui/presentation-environment.js';
|
|
19
19
|
import { StreamingFrameBuffer } from './tui/streaming-frame-buffer.js';
|
|
20
20
|
import { createClaudeStatusLineInput, StatusLine } from './tui/status-line.js';
|
|
21
|
-
import {
|
|
21
|
+
import { createTuiAppendHistoryChange } from './tui/transcript-window-model.js';
|
|
22
|
+
import { createTuiHistoryChange, resolveTuiRenderer, } from './tui/tui-view-model.js';
|
|
23
|
+
import { projectTuiScreen, } from './tui/tui-screen-model.js';
|
|
24
|
+
import { projectTuiHelpSurface, } from './tui/help-surface-model.js';
|
|
22
25
|
import { loadGitDiff, visiblePatchLines, } from './tui/git-diff.js';
|
|
23
26
|
import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule, } from './tui/permission-settings.js';
|
|
24
27
|
import { createRecentlyDeniedStore, } from './tui/recently-denied.js';
|
|
@@ -26,23 +29,28 @@ import { agentColorMessage, parseAgentColorInput, } from '../compatibility/claud
|
|
|
26
29
|
import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
|
|
27
30
|
import { runDoctor, } from '../maintenance/doctor.js';
|
|
28
31
|
import { canonicalClaudeCostModelName, formatCostSummary, } from './tui/cost-summary.js';
|
|
29
|
-
import { createComposerEditor, deleteComposerBackward, deleteComposerForward,
|
|
32
|
+
import { createComposerEditor, deleteComposerBackward, deleteComposerForward, insertComposerText, moveComposerCursor, } from './tui/composer-editor.js';
|
|
30
33
|
import { routeComposerKey, } from './tui/composer-key-router.js';
|
|
34
|
+
import { routeTuiInteraction, } from './tui/tui-interaction-router.js';
|
|
31
35
|
import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
|
|
32
36
|
import { editTuiPrompt, openTuiEditorFile, } from './tui/external-editor.js';
|
|
33
37
|
import { suspendTuiProcess } from './tui/terminal-suspend.js';
|
|
34
38
|
import { readTuiClipboard, writeTuiClipboard, writeTuiOsc52Clipboard, } from './tui/clipboard.js';
|
|
35
|
-
import { composerImageIds,
|
|
39
|
+
import { composerImageIds, insertComposerImageMarker, } from './tui/composer-images.js';
|
|
36
40
|
import { defaultTuiKeybindings, ensureTuiKeybindingsFile, hasTuiKeybindingPrefix, loadTuiKeybindings, resolveTuiKeybinding, tuiKeyChord, } from './tui/keybindings.js';
|
|
37
41
|
import { completeTuiWorkspaceDirectory, resolveTuiWorkspaceDirectory, } from './tui/workspace-directories.js';
|
|
38
42
|
import { conversationExportPath, conversationExportText, defaultConversationExportFilename, writeConversationExport, } from './tui/conversation-export.js';
|
|
39
43
|
import { loadTuiThemeSettings, saveTuiThemeSettings, themeSettingsWithCustomTheme, } from './tui/theme-settings.js';
|
|
40
|
-
import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider,
|
|
44
|
+
import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider, useTuiTheme, } from './tui/theme.js';
|
|
41
45
|
import { CUSTOM_THEME_TOKENS, createTuiCustomTheme, deleteTuiCustomTheme, loadTuiCustomThemes, updateTuiCustomTheme, } from './tui/custom-themes.js';
|
|
42
46
|
import { setupTuiTerminal, terminalSetupTuiSlashCommand, } from './tui/terminal-setup.js';
|
|
43
47
|
import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid, elicitationTextValue, expandElicitationOptions, focusedElicitationField, McpElicitationForm, McpElicitationUrl, moveElicitationFocus, moveElicitationOption, selectElicitationOption, toggleElicitationBoolean, typeaheadElicitationOption, unsetElicitationField, validateTuiElicitationForm, } from './tui/mcp-elicitation.js';
|
|
44
48
|
import { openTuiUrl } from './tui/open-url.js';
|
|
45
|
-
import { projectTuiToolPermission
|
|
49
|
+
import { projectTuiToolPermission } from './tui/tool-permission.js';
|
|
50
|
+
import { projectTuiPermissionSurface, } from './tui/permission-surface-model.js';
|
|
51
|
+
import { PermissionSurface } from './tui/permission-surface.js';
|
|
52
|
+
import { projectTuiDecisionSurface, } from './tui/decision-surface-model.js';
|
|
53
|
+
import { DecisionSurface } from './tui/decision-surface.js';
|
|
46
54
|
import { ConfigDashboard, projectConfigRows } from './tui/config-dashboard.js';
|
|
47
55
|
import { DoctorDashboard } from './tui/doctor-dashboard.js';
|
|
48
56
|
import { SandboxDashboard, tuiSandboxTabs } from './tui/sandbox-dashboard.js';
|
|
@@ -111,11 +119,6 @@ const ZERO_COST_SUMMARY = {
|
|
|
111
119
|
hasUnknownModelCost: false,
|
|
112
120
|
modelUsage: [],
|
|
113
121
|
};
|
|
114
|
-
function selectionPrefix(selected, screenReader) {
|
|
115
|
-
if (selected)
|
|
116
|
-
return screenReader ? 'Selected: ' : '❯ ';
|
|
117
|
-
return screenReader ? '' : ' ';
|
|
118
|
-
}
|
|
119
122
|
function rewindActions(point) {
|
|
120
123
|
const shared = [
|
|
121
124
|
{ action: 'summarize-from', label: 'Summarize from here' },
|
|
@@ -187,20 +190,6 @@ function questionAnswer(question, input) {
|
|
|
187
190
|
})
|
|
188
191
|
.join(', ');
|
|
189
192
|
}
|
|
190
|
-
function permissionDeleteTitle(behavior) {
|
|
191
|
-
return `Delete ${behavior === 'allow' ? 'allowed' : behavior === 'deny' ? 'denied' : 'ask'} tool?`;
|
|
192
|
-
}
|
|
193
|
-
function permissionScopeLabel(scope) {
|
|
194
|
-
return scope === 'local'
|
|
195
|
-
? 'From project local settings'
|
|
196
|
-
: scope === 'project'
|
|
197
|
-
? 'From project settings'
|
|
198
|
-
: 'From user settings';
|
|
199
|
-
}
|
|
200
|
-
function permissionRuleDescription(rule) {
|
|
201
|
-
const bashPrefix = /^Bash\((.*?)(?::\*| \*)\)$/u.exec(rule)?.[1]?.trim();
|
|
202
|
-
return bashPrefix ? `Any Bash command starting with ${bashPrefix}` : undefined;
|
|
203
|
-
}
|
|
204
193
|
function describeTool(call, sensitiveValues) {
|
|
205
194
|
const name = redactSensitiveText(call.name, sensitiveValues);
|
|
206
195
|
return `${name} ${describeToolInput(call, sensitiveValues)}`;
|
|
@@ -266,10 +255,41 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
|
|
|
266
255
|
'update',
|
|
267
256
|
'usage',
|
|
268
257
|
]);
|
|
258
|
+
function RewindWarning() {
|
|
259
|
+
const theme = useTuiTheme();
|
|
260
|
+
return (_jsx(Text, { ...theme.text.warning, children: "\u26A0 Rewinding does not affect files edited manually or via bash." }));
|
|
261
|
+
}
|
|
262
|
+
function ExitWarning() {
|
|
263
|
+
const theme = useTuiTheme();
|
|
264
|
+
return _jsx(Text, { ...theme.text.warning, children: "Press Ctrl-C again to exit" });
|
|
265
|
+
}
|
|
266
|
+
function DecisionOption({ selected, screenReader, children, selectedPrefix = '❯ ', unselectedPrefix = ' ', }) {
|
|
267
|
+
const theme = useTuiTheme();
|
|
268
|
+
const accessible = screenReader || theme.noColor;
|
|
269
|
+
return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [selected
|
|
270
|
+
? accessible
|
|
271
|
+
? 'Selected: '
|
|
272
|
+
: selectedPrefix
|
|
273
|
+
: accessible
|
|
274
|
+
? ''
|
|
275
|
+
: unselectedPrefix, children] }));
|
|
276
|
+
}
|
|
277
|
+
/** Advances the React-owned transcript plus its exact local mutation fact. */
|
|
278
|
+
export function advanceInteractiveHistoryState(current, items, changedFrom) {
|
|
279
|
+
if (!Number.isInteger(changedFrom) ||
|
|
280
|
+
changedFrom < 0 ||
|
|
281
|
+
changedFrom > items.length)
|
|
282
|
+
throw new RangeError('changedFrom must be a valid next-history index');
|
|
283
|
+
const revision = current.change.revision + 1;
|
|
284
|
+
return {
|
|
285
|
+
items,
|
|
286
|
+
change: changedFrom === current.items.length && items.length > changedFrom
|
|
287
|
+
? createTuiAppendHistoryChange(revision, current.items, items)
|
|
288
|
+
: createTuiHistoryChange(revision, changedFrom, items, current.items),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
269
291
|
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
292
|
const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
|
|
271
|
-
const width = useTerminalWidth(terminalWidth);
|
|
272
|
-
const rows = useTerminalRows();
|
|
273
293
|
const settingsDirectory = dataPlane === 'native' ? '.praxis' : '.claude';
|
|
274
294
|
const keybindingsRoot = useMemo(() => resolve(suppliedConfigRoot ??
|
|
275
295
|
keybindingsConfigRoot ??
|
|
@@ -340,14 +360,20 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
340
360
|
const [themeSettings, setThemeSettings] = useState(initialThemeSettings ?? DEFAULT_TUI_THEME_SETTINGS);
|
|
341
361
|
const [runtimeSettings, setRuntimeSettings] = useState(suppliedRuntimeSettings ??
|
|
342
362
|
projectRuntimeSettings({ settings: {}, state: {} }));
|
|
343
|
-
const
|
|
363
|
+
const presentation = useTuiPresentationEnvironment({
|
|
364
|
+
renderer: runtimeSettings.tui,
|
|
365
|
+
screenReader: axScreenReader,
|
|
366
|
+
...(terminalWidth === undefined
|
|
367
|
+
? {}
|
|
368
|
+
: { viewportOverride: { columns: terminalWidth } }),
|
|
369
|
+
});
|
|
370
|
+
const width = presentation.viewport.columns;
|
|
371
|
+
const rows = presentation.viewport.rows;
|
|
372
|
+
const fixedViewport = presentation.fixedViewport;
|
|
344
373
|
const runtimeSettingsRef = useRef(runtimeSettings);
|
|
345
374
|
runtimeSettingsRef.current = runtimeSettings;
|
|
346
375
|
runtimeGitignoreRef.current = runtimeSettings.gitignore;
|
|
347
376
|
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
377
|
const choices = useMemo(() => allowNewSession ? [null, ...initialSessions] : initialSessions, [allowNewSession, initialSessions]);
|
|
352
378
|
const [pickerIncludesNewSession, setPickerIncludesNewSession] = useState(allowNewSession);
|
|
353
379
|
const pickerIncludesNewSessionRef = useRef(allowNewSession);
|
|
@@ -447,46 +473,36 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
447
473
|
const [usage, setUsage] = useState();
|
|
448
474
|
const [costUsd, setCostUsd] = useState();
|
|
449
475
|
const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
|
|
450
|
-
const [
|
|
476
|
+
const [historyState, setHistoryState] = useState(() => {
|
|
477
|
+
const items = [...initialHistory];
|
|
478
|
+
return {
|
|
479
|
+
items,
|
|
480
|
+
change: createTuiHistoryChange(0, 0, items),
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
const history = historyState.items;
|
|
484
|
+
const setHistory = (update, knownChangedFrom) => {
|
|
485
|
+
setHistoryState((current) => {
|
|
486
|
+
const next = typeof update === 'function' ? update(current.items) : update;
|
|
487
|
+
let changedFrom = typeof knownChangedFrom === 'function'
|
|
488
|
+
? knownChangedFrom(current.items, next)
|
|
489
|
+
: (knownChangedFrom ?? 0);
|
|
490
|
+
if (knownChangedFrom === undefined) {
|
|
491
|
+
const limit = Math.min(next.length, current.items.length);
|
|
492
|
+
while (changedFrom < limit &&
|
|
493
|
+
next[changedFrom] === current.items[changedFrom])
|
|
494
|
+
changedFrom += 1;
|
|
495
|
+
}
|
|
496
|
+
return advanceInteractiveHistoryState(current, next, changedFrom);
|
|
497
|
+
});
|
|
498
|
+
};
|
|
451
499
|
const activeAttemptThinkingItemsRef = useRef([]);
|
|
452
|
-
const [transcriptScrollOffset,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// context, tool, shell, notices, results, and so on) is operational
|
|
459
|
-
// bookkeeping that must not hide the fresh-session welcome. The original
|
|
460
|
-
// loaded transcript decides whether the session was resumed, separately from
|
|
461
|
-
// the live history that grows while the session runs. A session is resumed
|
|
462
|
-
// only when it was opened through `resume` and the original transcript
|
|
463
|
-
// already contained real conversation content. Fullscreen projects only the
|
|
464
|
-
// newest transcript tail that fits the fixed viewport, leaving the
|
|
465
|
-
// composer/status chrome intact and keeping the active stream visible.
|
|
466
|
-
// Classic and screen-reader modes always render the full history exactly as
|
|
467
|
-
// before.
|
|
468
|
-
const { transcriptEntries, transcriptPageRows, maxTranscriptScrollOffset, resumed, freshSession, hasConversationHistory, } = useMemo(() => projectTuiView({
|
|
469
|
-
initialHistory,
|
|
470
|
-
history,
|
|
471
|
-
resume: resume !== undefined,
|
|
472
|
-
fixedViewport,
|
|
473
|
-
screenReader: axScreenReader,
|
|
474
|
-
rows,
|
|
475
|
-
width,
|
|
476
|
-
scrollOffset: transcriptScrollOffset,
|
|
477
|
-
detailedTranscript: thinkingExpanded || runtimeSettings.verbose,
|
|
478
|
-
}), [
|
|
479
|
-
initialHistory,
|
|
480
|
-
history,
|
|
481
|
-
resume,
|
|
482
|
-
fixedViewport,
|
|
483
|
-
axScreenReader,
|
|
484
|
-
rows,
|
|
485
|
-
width,
|
|
486
|
-
transcriptScrollOffset,
|
|
487
|
-
thinkingExpanded,
|
|
488
|
-
runtimeSettings.verbose,
|
|
489
|
-
]);
|
|
500
|
+
const [transcriptScrollOffset, setTranscriptScrollOffsetState] = useState(0);
|
|
501
|
+
const transcriptScrollOffsetRef = useRef(0);
|
|
502
|
+
const setTranscriptScrollOffset = (offset) => {
|
|
503
|
+
transcriptScrollOffsetRef.current = offset;
|
|
504
|
+
setTranscriptScrollOffsetState(offset);
|
|
505
|
+
};
|
|
490
506
|
const sessionLoadRef = useRef(0);
|
|
491
507
|
const [turnDiffs, setTurnDiffs] = useState([]);
|
|
492
508
|
const turnNumberRef = useRef(0);
|
|
@@ -521,6 +537,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
521
537
|
const runtimePreferencesRef = useRef(runtimePreferences);
|
|
522
538
|
const [permission, setPermission] = useState(null);
|
|
523
539
|
const permissionRef = useRef(null);
|
|
540
|
+
const permissionQueueRef = useRef([]);
|
|
524
541
|
const [permissionSelection, setPermissionSelection] = useState(0);
|
|
525
542
|
const [permissionFeedbackMode, setPermissionFeedbackMode] = useState(false);
|
|
526
543
|
const [permissionRuleEditor, setPermissionRuleEditor] = useState(null);
|
|
@@ -528,6 +545,59 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
528
545
|
const toolPermissionModel = useMemo(() => permission?.kind === 'tool'
|
|
529
546
|
? projectTuiToolPermission(permission.call, runtimeCwd, sensitiveValues, permission.decision, dataPlane)
|
|
530
547
|
: null, [dataPlane, permission, runtimeCwd, sensitiveValues]);
|
|
548
|
+
const activatePermission = (pending) => {
|
|
549
|
+
const projected = pending.kind === 'tool'
|
|
550
|
+
? projectTuiToolPermission(pending.call, runtimeCwdRef.current, sensitiveValues, pending.decision, dataPlane)
|
|
551
|
+
: null;
|
|
552
|
+
const editableRule = projected?.options.find((option) => option.editableRule)?.editableRule;
|
|
553
|
+
clearComposerInput();
|
|
554
|
+
setPermissionSelection(0);
|
|
555
|
+
setPermissionFeedbackMode(false);
|
|
556
|
+
setPermissionRuleEditor(editableRule ? createComposerEditor(editableRule.initialValue) : null);
|
|
557
|
+
permissionRef.current = pending;
|
|
558
|
+
setPermission(pending);
|
|
559
|
+
pending.notificationTimer = setTimeout(() => {
|
|
560
|
+
if (pending.settled)
|
|
561
|
+
return;
|
|
562
|
+
serviceRef.current?.notify?.(sessionIdRef.current ?? undefined, `Approval required for ${pending.call.name}`, 'permission_prompt', 'Praxis');
|
|
563
|
+
}, notificationDelayMs);
|
|
564
|
+
};
|
|
565
|
+
const resolvePermission = (pending, approval) => {
|
|
566
|
+
if (pending.settled)
|
|
567
|
+
return;
|
|
568
|
+
pending.settled = true;
|
|
569
|
+
if (pending.notificationTimer) {
|
|
570
|
+
clearTimeout(pending.notificationTimer);
|
|
571
|
+
pending.notificationTimer = null;
|
|
572
|
+
}
|
|
573
|
+
const queue = permissionQueueRef.current;
|
|
574
|
+
const index = queue.indexOf(pending);
|
|
575
|
+
if (index >= 0)
|
|
576
|
+
queue.splice(index, 1);
|
|
577
|
+
if (permissionRef.current === pending) {
|
|
578
|
+
permissionRef.current = null;
|
|
579
|
+
setPermission((current) => (current === pending ? null : current));
|
|
580
|
+
}
|
|
581
|
+
pending.settle(approval);
|
|
582
|
+
const next = queue[0];
|
|
583
|
+
if (next)
|
|
584
|
+
activatePermission(next);
|
|
585
|
+
};
|
|
586
|
+
const drainPermissionQueue = () => {
|
|
587
|
+
const queued = permissionQueueRef.current.splice(0);
|
|
588
|
+
permissionRef.current = null;
|
|
589
|
+
setPermission(null);
|
|
590
|
+
for (const pending of queued) {
|
|
591
|
+
if (pending.settled)
|
|
592
|
+
continue;
|
|
593
|
+
pending.settled = true;
|
|
594
|
+
if (pending.notificationTimer) {
|
|
595
|
+
clearTimeout(pending.notificationTimer);
|
|
596
|
+
pending.notificationTimer = null;
|
|
597
|
+
}
|
|
598
|
+
pending.settle(false);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
531
601
|
const [elicitation, setElicitation] = useState(null);
|
|
532
602
|
const elicitationRef = useRef(null);
|
|
533
603
|
const [elicitationForm, setElicitationForm] = useState(null);
|
|
@@ -617,6 +687,245 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
617
687
|
permissionMode: runtimePreferences.permissionMode,
|
|
618
688
|
...(contextWindowTokens === undefined ? {} : { contextWindowTokens }),
|
|
619
689
|
};
|
|
690
|
+
const workspaceDirectories = useMemo(() => [
|
|
691
|
+
runtimeCwd,
|
|
692
|
+
...runtimePreferences.additionalDirectories.filter((path) => path !== runtimeCwd),
|
|
693
|
+
].map((path) => ({ path, original: path === runtimeCwd })), [runtimeCwd, runtimePreferences.additionalDirectories]);
|
|
694
|
+
const permissionPrioritySurface = useMemo(() => {
|
|
695
|
+
if (!permission)
|
|
696
|
+
return null;
|
|
697
|
+
if (permission.kind === 'tool' && toolPermissionModel) {
|
|
698
|
+
return projectTuiPermissionSurface({
|
|
699
|
+
kind: 'tool-request',
|
|
700
|
+
model: toolPermissionModel,
|
|
701
|
+
selection: permissionSelection,
|
|
702
|
+
feedbackMode: permissionFeedbackMode,
|
|
703
|
+
feedback: input,
|
|
704
|
+
ruleEditor: permissionRuleEditor,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
return projectTuiPermissionSurface({
|
|
708
|
+
kind: 'recovery-request',
|
|
709
|
+
heading: `Retry interrupted ${permission.call.name}?`,
|
|
710
|
+
display: describeTool(permission.call, sensitiveValues),
|
|
711
|
+
selection: permissionSelection,
|
|
712
|
+
feedbackMode: permissionFeedbackMode,
|
|
713
|
+
feedback: input,
|
|
714
|
+
});
|
|
715
|
+
}, [
|
|
716
|
+
input,
|
|
717
|
+
permission,
|
|
718
|
+
permissionFeedbackMode,
|
|
719
|
+
permissionRuleEditor,
|
|
720
|
+
permissionSelection,
|
|
721
|
+
sensitiveValues,
|
|
722
|
+
toolPermissionModel,
|
|
723
|
+
]);
|
|
724
|
+
const permissionManagementSurface = useMemo(() => {
|
|
725
|
+
if (!menu)
|
|
726
|
+
return null;
|
|
727
|
+
switch (menu.kind) {
|
|
728
|
+
case 'permission-dashboard':
|
|
729
|
+
return projectTuiPermissionSurface({
|
|
730
|
+
kind: menu.kind,
|
|
731
|
+
tabIndex: menu.tabIndex,
|
|
732
|
+
selectedIndex: menu.selectedIndex,
|
|
733
|
+
query: menu.query,
|
|
734
|
+
rules: menu.rules,
|
|
735
|
+
recentDenied,
|
|
736
|
+
retryingDeniedId,
|
|
737
|
+
workspaceDirectories,
|
|
738
|
+
});
|
|
739
|
+
case 'permission-rule-input':
|
|
740
|
+
return projectTuiPermissionSurface({
|
|
741
|
+
kind: menu.kind,
|
|
742
|
+
behavior: menu.behavior,
|
|
743
|
+
value: input,
|
|
744
|
+
});
|
|
745
|
+
case 'permission-scope':
|
|
746
|
+
return projectTuiPermissionSurface({
|
|
747
|
+
kind: menu.kind,
|
|
748
|
+
behavior: menu.behavior,
|
|
749
|
+
rule: menu.rule,
|
|
750
|
+
selectedIndex: menu.selectedIndex,
|
|
751
|
+
settingsDirectory,
|
|
752
|
+
});
|
|
753
|
+
case 'permission-delete':
|
|
754
|
+
return projectTuiPermissionSurface({
|
|
755
|
+
kind: menu.kind,
|
|
756
|
+
rule: menu.rule,
|
|
757
|
+
selectedIndex: menu.selectedIndex,
|
|
758
|
+
});
|
|
759
|
+
case 'workspace-directory-input':
|
|
760
|
+
return projectTuiPermissionSurface({
|
|
761
|
+
kind: menu.kind,
|
|
762
|
+
value: input,
|
|
763
|
+
});
|
|
764
|
+
case 'workspace-directory-delete':
|
|
765
|
+
return projectTuiPermissionSurface({
|
|
766
|
+
kind: menu.kind,
|
|
767
|
+
path: menu.path,
|
|
768
|
+
selectedIndex: menu.selectedIndex,
|
|
769
|
+
});
|
|
770
|
+
default:
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
}, [
|
|
774
|
+
input,
|
|
775
|
+
menu,
|
|
776
|
+
recentDenied,
|
|
777
|
+
retryingDeniedId,
|
|
778
|
+
settingsDirectory,
|
|
779
|
+
workspaceDirectories,
|
|
780
|
+
]);
|
|
781
|
+
const legacySecondarySurface = useMemo(() => ({ kind: 'legacy-secondary' }), []);
|
|
782
|
+
const helpMenu = menu?.kind === 'help' ? menu : null;
|
|
783
|
+
const helpSurface = useMemo(() => helpMenu === null
|
|
784
|
+
? null
|
|
785
|
+
: projectTuiHelpSurface({
|
|
786
|
+
invocation: helpMenu.invocation,
|
|
787
|
+
tabIndex: helpMenu.tabIndex,
|
|
788
|
+
selectedIndex: helpMenu.selectedIndex,
|
|
789
|
+
builtinCommands: builtinSlashCommands,
|
|
790
|
+
customCommands: customSlashCommands,
|
|
791
|
+
}), [helpMenu, builtinSlashCommands, customSlashCommands]);
|
|
792
|
+
const shortcutHelpSurface = useMemo(() => projectTuiHelpSurface({
|
|
793
|
+
invocation: '?',
|
|
794
|
+
tabIndex: 0,
|
|
795
|
+
selectedIndex: 0,
|
|
796
|
+
builtinCommands: builtinSlashCommands,
|
|
797
|
+
customCommands: customSlashCommands,
|
|
798
|
+
}), [builtinSlashCommands, customSlashCommands]);
|
|
799
|
+
const decisionElevatedMode = runtimeSettings.useAutoModeDuringPlan
|
|
800
|
+
? 'auto'
|
|
801
|
+
: allowDangerouslySkipPermissions
|
|
802
|
+
? 'bypassPermissions'
|
|
803
|
+
: 'acceptEdits';
|
|
804
|
+
const planDecisionSurface = useMemo(() => planApproval
|
|
805
|
+
? projectTuiDecisionSurface({
|
|
806
|
+
kind: 'plan-approval',
|
|
807
|
+
request: planApproval.request,
|
|
808
|
+
selectedIndex: planApprovalSelection,
|
|
809
|
+
feedbackMode: planApprovalFeedbackMode,
|
|
810
|
+
feedback: input,
|
|
811
|
+
elevatedMode: decisionElevatedMode,
|
|
812
|
+
})
|
|
813
|
+
: null, [
|
|
814
|
+
planApproval,
|
|
815
|
+
planApprovalFeedbackMode,
|
|
816
|
+
planApprovalSelection,
|
|
817
|
+
input,
|
|
818
|
+
decisionElevatedMode,
|
|
819
|
+
]);
|
|
820
|
+
const questionDecisionSurface = useMemo(() => question
|
|
821
|
+
? projectTuiDecisionSurface({
|
|
822
|
+
kind: 'question',
|
|
823
|
+
questions: question.questions,
|
|
824
|
+
questionIndex: question.index,
|
|
825
|
+
answer: input,
|
|
826
|
+
})
|
|
827
|
+
: null, [question, input]);
|
|
828
|
+
const secondarySurface = menu === null
|
|
829
|
+
? undefined
|
|
830
|
+
: menu.kind === 'help'
|
|
831
|
+
? (helpSurface ?? undefined)
|
|
832
|
+
: (permissionManagementSurface ?? legacySecondarySurface);
|
|
833
|
+
const screenSurfaces = useMemo(() => ({
|
|
834
|
+
...(selectingSession
|
|
835
|
+
? { sessionPicker: { kind: 'session-picker' } }
|
|
836
|
+
: {}),
|
|
837
|
+
...(externalEditorRequest !== null ||
|
|
838
|
+
keybindingsEditing ||
|
|
839
|
+
memoryEditorRequest !== null
|
|
840
|
+
? { priority: { kind: 'editor-wait' } }
|
|
841
|
+
: permission !== null && permissionPrioritySurface !== null
|
|
842
|
+
? {
|
|
843
|
+
priority: {
|
|
844
|
+
kind: 'permission',
|
|
845
|
+
surface: permissionPrioritySurface,
|
|
846
|
+
},
|
|
847
|
+
}
|
|
848
|
+
: planApproval !== null && planDecisionSurface !== null
|
|
849
|
+
? { priority: planDecisionSurface }
|
|
850
|
+
: question !== null && questionDecisionSurface !== null
|
|
851
|
+
? { priority: questionDecisionSurface }
|
|
852
|
+
: elicitation !== null
|
|
853
|
+
? { priority: { kind: 'elicitation' } }
|
|
854
|
+
: {}),
|
|
855
|
+
...(secondarySurface === undefined
|
|
856
|
+
? {}
|
|
857
|
+
: { secondary: secondarySurface }),
|
|
858
|
+
overlays: [
|
|
859
|
+
...(commandPaletteVisible
|
|
860
|
+
? [{ kind: 'command-palette' }]
|
|
861
|
+
: []),
|
|
862
|
+
...(filePickerVisible ? [{ kind: 'file-picker' }] : []),
|
|
863
|
+
...(exitConfirmation ? [{ kind: 'exit-confirmation' }] : []),
|
|
864
|
+
],
|
|
865
|
+
}), [
|
|
866
|
+
selectingSession,
|
|
867
|
+
externalEditorRequest,
|
|
868
|
+
keybindingsEditing,
|
|
869
|
+
memoryEditorRequest,
|
|
870
|
+
permission,
|
|
871
|
+
permissionPrioritySurface,
|
|
872
|
+
planApproval,
|
|
873
|
+
question,
|
|
874
|
+
planDecisionSurface,
|
|
875
|
+
questionDecisionSurface,
|
|
876
|
+
elicitation,
|
|
877
|
+
secondarySurface,
|
|
878
|
+
commandPaletteVisible,
|
|
879
|
+
filePickerVisible,
|
|
880
|
+
exitConfirmation,
|
|
881
|
+
]);
|
|
882
|
+
const previousTuiScreenRef = useRef(undefined);
|
|
883
|
+
const screen = useMemo(() => projectTuiScreen({
|
|
884
|
+
presentation,
|
|
885
|
+
conversation: {
|
|
886
|
+
initialHistory,
|
|
887
|
+
history,
|
|
888
|
+
resumeRequested: resume !== undefined,
|
|
889
|
+
scrollOffset: transcriptScrollOffset,
|
|
890
|
+
detailed: thinkingExpanded || runtimeSettings.verbose,
|
|
891
|
+
activeText,
|
|
892
|
+
activeThinking,
|
|
893
|
+
historyChange: historyState.change,
|
|
894
|
+
},
|
|
895
|
+
sessionId,
|
|
896
|
+
surfaces: screenSurfaces,
|
|
897
|
+
}, previousTuiScreenRef.current), [
|
|
898
|
+
presentation,
|
|
899
|
+
initialHistory,
|
|
900
|
+
history,
|
|
901
|
+
resume,
|
|
902
|
+
transcriptScrollOffset,
|
|
903
|
+
thinkingExpanded,
|
|
904
|
+
runtimeSettings.verbose,
|
|
905
|
+
activeText,
|
|
906
|
+
activeThinking,
|
|
907
|
+
historyState.change,
|
|
908
|
+
sessionId,
|
|
909
|
+
screenSurfaces,
|
|
910
|
+
]);
|
|
911
|
+
useEffect(() => {
|
|
912
|
+
previousTuiScreenRef.current = screen;
|
|
913
|
+
}, [screen]);
|
|
914
|
+
const conversationScreen = screen.body.kind === 'conversation' ? screen.body : undefined;
|
|
915
|
+
const selectedPriority = conversationScreen?.foreground.kind === 'priority'
|
|
916
|
+
? conversationScreen.foreground.surface
|
|
917
|
+
: undefined;
|
|
918
|
+
const selectedSecondarySurface = conversationScreen?.foreground.kind === 'secondary'
|
|
919
|
+
? (conversationScreen.foreground.surface ?? null)
|
|
920
|
+
: null;
|
|
921
|
+
const selectedOverlays = conversationScreen?.foreground.kind === 'compose'
|
|
922
|
+
? conversationScreen.foreground.overlays
|
|
923
|
+
: [];
|
|
924
|
+
const selectedCommandPalette = selectedOverlays.find((overlay) => overlay.kind === 'command-palette');
|
|
925
|
+
const selectedFilePicker = selectedOverlays.find((overlay) => overlay.kind === 'file-picker');
|
|
926
|
+
const selectedExitConfirmation = selectedOverlays.find((overlay) => overlay.kind === 'exit-confirmation');
|
|
927
|
+
const transcriptPageRows = conversationScreen?.transcript.pageRows ?? 2;
|
|
928
|
+
const maxTranscriptScrollOffset = conversationScreen?.transcript.maxScrollOffset ?? 0;
|
|
620
929
|
const statusAuthSource = process.env.PRAXIS_API_KEY
|
|
621
930
|
? 'PRAXIS_API_KEY'
|
|
622
931
|
: process.env.ANTHROPIC_API_KEY
|
|
@@ -631,10 +940,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
631
940
|
const projectSettings = existsSync(join(runtimeCwd, dataPlane === 'native' ? '.praxis' : '.claude', 'settings.json')) || existsSync(join(runtimeCwd, 'settings.json'));
|
|
632
941
|
return projectSettings ? 'User settings, Project settings' : 'User settings';
|
|
633
942
|
})();
|
|
634
|
-
const workspaceDirectories = [
|
|
635
|
-
runtimeCwd,
|
|
636
|
-
...runtimePreferences.additionalDirectories.filter((path) => path !== runtimeCwd),
|
|
637
|
-
].map((path) => ({ path, original: path === runtimeCwd }));
|
|
638
943
|
const permissionOptions = useMemo(() => [
|
|
639
944
|
...PERMISSION_OPTIONS,
|
|
640
945
|
...(allowDangerouslySkipPermissions
|
|
@@ -722,7 +1027,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
722
1027
|
if (!signal)
|
|
723
1028
|
return;
|
|
724
1029
|
const cancel = () => {
|
|
725
|
-
|
|
1030
|
+
drainPermissionQueue();
|
|
726
1031
|
elicitationRef.current?.resolve({ action: 'cancel' });
|
|
727
1032
|
questionRef.current?.resolve(null);
|
|
728
1033
|
planApprovalRef.current?.resolve({ behavior: 'deny' });
|
|
@@ -737,7 +1042,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
737
1042
|
useEffect(() => () => {
|
|
738
1043
|
componentMountedRef.current = false;
|
|
739
1044
|
streamingFrameRef.current?.dispose();
|
|
740
|
-
|
|
1045
|
+
drainPermissionQueue();
|
|
741
1046
|
elicitationRef.current?.resolve({ action: 'cancel' });
|
|
742
1047
|
questionRef.current?.resolve(null);
|
|
743
1048
|
planApprovalRef.current?.resolve({ behavior: 'deny' });
|
|
@@ -760,7 +1065,10 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
760
1065
|
// thinking, or completion entry that it textually precedes.
|
|
761
1066
|
streamingFrameRef.current?.flush();
|
|
762
1067
|
setTranscriptScrollOffset(0);
|
|
763
|
-
|
|
1068
|
+
setHistoryState((current) => {
|
|
1069
|
+
const items = [...current.items, line];
|
|
1070
|
+
return advanceInteractiveHistoryState(current, items, current.items.length);
|
|
1071
|
+
});
|
|
764
1072
|
};
|
|
765
1073
|
useEffect(() => {
|
|
766
1074
|
if (initialThemeSettings !== undefined) {
|
|
@@ -1152,25 +1460,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
1152
1460
|
exitConfirmationTimerRef.current = null;
|
|
1153
1461
|
setExitConfirmation(false);
|
|
1154
1462
|
};
|
|
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
1463
|
useEffect(() => {
|
|
1175
1464
|
setCommandSelection((current) => Math.min(current, Math.max(0, matchingSlashCommands.length - 1)));
|
|
1176
1465
|
}, [matchingSlashCommands.length]);
|
|
@@ -1371,37 +1660,18 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
1371
1660
|
}
|
|
1372
1661
|
};
|
|
1373
1662
|
const requestApproval = (call, kind, decision) => new Promise((resolveApproval) => {
|
|
1374
|
-
const projected = kind === 'tool'
|
|
1375
|
-
? projectTuiToolPermission(call, runtimeCwdRef.current, sensitiveValues, decision, dataPlane)
|
|
1376
|
-
: null;
|
|
1377
|
-
const editableRule = projected?.options.find((option) => option.editableRule)?.editableRule;
|
|
1378
|
-
let settled = false;
|
|
1379
|
-
const notificationTimer = setTimeout(() => {
|
|
1380
|
-
if (settled)
|
|
1381
|
-
return;
|
|
1382
|
-
serviceRef.current?.notify?.(sessionIdRef.current ?? undefined, `Approval required for ${call.name}`, 'permission_prompt', 'Praxis');
|
|
1383
|
-
}, notificationDelayMs);
|
|
1384
1663
|
const pending = {
|
|
1385
1664
|
kind,
|
|
1386
1665
|
call,
|
|
1387
1666
|
...(decision ? { decision } : {}),
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
clearTimeout(notificationTimer);
|
|
1393
|
-
if (permissionRef.current === pending)
|
|
1394
|
-
permissionRef.current = null;
|
|
1395
|
-
setPermission((current) => (current === pending ? null : current));
|
|
1396
|
-
resolveApproval(approved);
|
|
1397
|
-
},
|
|
1667
|
+
notificationTimer: null,
|
|
1668
|
+
settled: false,
|
|
1669
|
+
settle: resolveApproval,
|
|
1670
|
+
resolve: (approved) => resolvePermission(pending, approved),
|
|
1398
1671
|
};
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
setPermissionRuleEditor(editableRule ? createComposerEditor(editableRule.initialValue) : null);
|
|
1403
|
-
permissionRef.current = pending;
|
|
1404
|
-
setPermission(pending);
|
|
1672
|
+
permissionQueueRef.current.push(pending);
|
|
1673
|
+
if (permissionQueueRef.current.length === 1)
|
|
1674
|
+
activatePermission(pending);
|
|
1405
1675
|
});
|
|
1406
1676
|
const approveTool = (call, _originalCall, decision) => requestApproval(call, 'tool', decision);
|
|
1407
1677
|
const requestElicitation = (request) => new Promise((resolveResult) => {
|
|
@@ -1514,6 +1784,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
1514
1784
|
const retireService = () => {
|
|
1515
1785
|
serviceEpochRef.current += 1;
|
|
1516
1786
|
scheduledWaitRef.current?.abort();
|
|
1787
|
+
drainPermissionQueue();
|
|
1517
1788
|
const current = serviceRef.current;
|
|
1518
1789
|
serviceRef.current = null;
|
|
1519
1790
|
if (!current?.close)
|
|
@@ -2872,74 +3143,150 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
2872
3143
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
2873
3144
|
return codePoint >= 32 && codePoint !== 127;
|
|
2874
3145
|
});
|
|
3146
|
+
const composerKey = {
|
|
3147
|
+
value,
|
|
3148
|
+
left: key.leftArrow,
|
|
3149
|
+
right: key.rightArrow,
|
|
3150
|
+
backspace: key.backspace,
|
|
3151
|
+
delete: key.delete,
|
|
3152
|
+
ctrl: key.ctrl,
|
|
3153
|
+
meta: key.meta,
|
|
3154
|
+
escape: key.escape || value === '\u001B',
|
|
3155
|
+
};
|
|
2875
3156
|
const editor = () => createComposerEditor(inputRef.current, inputCursorRef.current);
|
|
2876
3157
|
const editComposer = () => {
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
: moveComposerCursorAcrossImages(editor(), -1));
|
|
3158
|
+
const transition = routeComposerKey(editor(), composerKey);
|
|
3159
|
+
if (transition.kind === 'edit') {
|
|
3160
|
+
updateComposerEditor(transition.editor);
|
|
2881
3161
|
return true;
|
|
2882
3162
|
}
|
|
2883
|
-
if (
|
|
2884
|
-
|
|
2885
|
-
|
|
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()));
|
|
2919
|
-
return true;
|
|
2920
|
-
}
|
|
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;
|
|
3163
|
+
if (transition.kind === 'cancel')
|
|
3164
|
+
clearComposerInput();
|
|
3165
|
+
return transition.kind === 'cancel';
|
|
2930
3166
|
};
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
3167
|
+
const interactionComposer = () => ({
|
|
3168
|
+
mode: runtimeSettingsRef.current.editor === 'vim'
|
|
3169
|
+
? vimInsertMode
|
|
3170
|
+
? 'vim-insert'
|
|
3171
|
+
: 'vim-normal'
|
|
3172
|
+
: 'readline',
|
|
3173
|
+
editor: editor(),
|
|
3174
|
+
lastCancelAtMs: lastEscapeAtRef.current,
|
|
3175
|
+
});
|
|
3176
|
+
const applyInteractionEffects = (effects) => {
|
|
3177
|
+
for (const effect of effects) {
|
|
3178
|
+
if (effect.kind === 'request-process-suspension') {
|
|
3179
|
+
processSuspendRequestedRef.current = true;
|
|
3180
|
+
setProcessSuspendRequested(true);
|
|
3181
|
+
}
|
|
3182
|
+
else if (effect.kind === 'set-transcript-scroll-offset') {
|
|
3183
|
+
setTranscriptScrollOffset(effect.offset);
|
|
3184
|
+
}
|
|
3185
|
+
else if (effect.kind === 'interrupt-turn') {
|
|
3186
|
+
turnControllerRef.current?.abort();
|
|
3187
|
+
}
|
|
3188
|
+
else if (effect.kind === 'arm-exit-confirmation') {
|
|
3189
|
+
setExitConfirmation(true);
|
|
3190
|
+
if (exitConfirmationTimerRef.current)
|
|
3191
|
+
clearTimeout(exitConfirmationTimerRef.current);
|
|
3192
|
+
exitConfirmationTimerRef.current = setTimeout(() => {
|
|
3193
|
+
exitConfirmationTimerRef.current = null;
|
|
3194
|
+
setExitConfirmation(false);
|
|
3195
|
+
}, 1_500);
|
|
3196
|
+
}
|
|
3197
|
+
else if (effect.kind === 'dismiss-exit-confirmation') {
|
|
3198
|
+
dismissExitConfirmation();
|
|
3199
|
+
}
|
|
3200
|
+
else if (effect.kind === 'exit-application') {
|
|
3201
|
+
drainPermissionQueue();
|
|
3202
|
+
elicitationRef.current?.resolve({ action: 'cancel' });
|
|
3203
|
+
questionRef.current?.resolve(null);
|
|
3204
|
+
planApprovalRef.current?.resolve({ behavior: 'deny' });
|
|
3205
|
+
onCancel?.();
|
|
3206
|
+
exit();
|
|
3207
|
+
}
|
|
3208
|
+
else if (effect.kind === 'set-vim-insert-mode') {
|
|
3209
|
+
setVimInsertMode(effect.insert);
|
|
3210
|
+
}
|
|
3211
|
+
else if (effect.kind === 'set-composer-editor') {
|
|
3212
|
+
updateComposerEditor(effect.editor);
|
|
3213
|
+
}
|
|
3214
|
+
else if (effect.kind === 'clear-composer') {
|
|
3215
|
+
clearComposerInput();
|
|
3216
|
+
}
|
|
3217
|
+
else if (effect.kind === 'record-composer-cancel') {
|
|
3218
|
+
lastEscapeAtRef.current = effect.timestamp;
|
|
3219
|
+
}
|
|
3220
|
+
else if (effect.kind === 'cancel-tui-layer') {
|
|
3221
|
+
switch (effect.target) {
|
|
3222
|
+
case 'permission':
|
|
3223
|
+
permissionRef.current?.resolve(false);
|
|
3224
|
+
break;
|
|
3225
|
+
case 'plan-approval':
|
|
3226
|
+
planApprovalRef.current?.resolve({ behavior: 'deny' });
|
|
3227
|
+
break;
|
|
3228
|
+
case 'question':
|
|
3229
|
+
questionRef.current?.resolve(null);
|
|
3230
|
+
break;
|
|
3231
|
+
case 'elicitation-url-waiting':
|
|
3232
|
+
if (elicitationUrlWaitingRef.current) {
|
|
3233
|
+
elicitationUrlWaitingRef.current = null;
|
|
3234
|
+
setElicitationUrlWaiting(false);
|
|
3235
|
+
setElicitation(null);
|
|
3236
|
+
setElicitationForm(null);
|
|
3237
|
+
clearComposerInput();
|
|
3238
|
+
}
|
|
3239
|
+
break;
|
|
3240
|
+
case 'elicitation-options':
|
|
3241
|
+
setElicitationForm((current) => current?.expandedField
|
|
3242
|
+
? { ...current, expandedField: undefined }
|
|
3243
|
+
: current);
|
|
3244
|
+
break;
|
|
3245
|
+
case 'elicitation':
|
|
3246
|
+
elicitationRef.current?.resolve({ action: 'cancel' });
|
|
3247
|
+
break;
|
|
3248
|
+
case 'file-picker':
|
|
3249
|
+
setFilePickerOpen(false);
|
|
3250
|
+
break;
|
|
3251
|
+
case 'command-palette':
|
|
3252
|
+
setCommandPaletteOpen(false);
|
|
3253
|
+
break;
|
|
3254
|
+
default: {
|
|
3255
|
+
const unhandledTarget = effect.target;
|
|
3256
|
+
return unhandledTarget;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
else {
|
|
3261
|
+
const unhandledEffect = effect;
|
|
3262
|
+
return unhandledEffect;
|
|
3263
|
+
}
|
|
2939
3264
|
}
|
|
3265
|
+
};
|
|
3266
|
+
if (controlKey('c') || controlKey('z')) {
|
|
3267
|
+
const globalInteraction = routeTuiInteraction({
|
|
3268
|
+
suspensionPending: processSuspendRequestedRef.current,
|
|
3269
|
+
exitConfirmationArmed: exitConfirmation,
|
|
3270
|
+
layer: { kind: 'none' },
|
|
3271
|
+
busy,
|
|
3272
|
+
viewport: {
|
|
3273
|
+
enabled: false,
|
|
3274
|
+
offset: 0,
|
|
3275
|
+
maxOffset: 0,
|
|
3276
|
+
pageRows: 0,
|
|
3277
|
+
},
|
|
3278
|
+
composer: interactionComposer(),
|
|
3279
|
+
}, {
|
|
3280
|
+
globalIntent: controlKey('c') ? 'exit' : 'suspend',
|
|
3281
|
+
scrollIntent: 'none',
|
|
3282
|
+
action: undefined,
|
|
3283
|
+
composerKey,
|
|
3284
|
+
timestamp: Date.now(),
|
|
3285
|
+
callerIntent: 'none',
|
|
3286
|
+
});
|
|
3287
|
+
applyInteractionEffects(globalInteraction.effects);
|
|
2940
3288
|
return;
|
|
2941
3289
|
}
|
|
2942
|
-
dismissExitConfirmation();
|
|
2943
3290
|
const keybindingContexts = menuRef.current
|
|
2944
3291
|
? menuRef.current.kind === 'diff'
|
|
2945
3292
|
? ['DiffDialog']
|
|
@@ -2976,57 +3323,90 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
2976
3323
|
if (!keybindingAction) {
|
|
2977
3324
|
keybindingAction = resolveTuiKeybinding(keybindings, keybindingContexts, inputChord);
|
|
2978
3325
|
}
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3326
|
+
const scrollIntent = key.pageUp
|
|
3327
|
+
? 'page-older'
|
|
3328
|
+
: key.pageDown
|
|
3329
|
+
? 'page-newer'
|
|
3330
|
+
: controlKey('u') || controlKey('b')
|
|
3331
|
+
? 'half-page-older'
|
|
3332
|
+
: controlKey('f') || controlKey('n')
|
|
3333
|
+
? 'half-page-newer'
|
|
3334
|
+
: inputRef.current.length === 0 && key.upArrow
|
|
3335
|
+
? 'line-older'
|
|
3336
|
+
: inputRef.current.length === 0 && key.downArrow
|
|
3337
|
+
? 'line-newer'
|
|
3338
|
+
: 'none';
|
|
3339
|
+
const hasPendingPrefix = !keybindingAction &&
|
|
3340
|
+
inputChord !== null &&
|
|
3341
|
+
hasTuiKeybindingPrefix(keybindings, keybindingContexts, inputChord);
|
|
3342
|
+
if (hasPendingPrefix) {
|
|
2982
3343
|
keySequenceRef.current = { chord: inputChord, at: Date.now() };
|
|
3344
|
+
}
|
|
3345
|
+
const layer = hasPendingPrefix
|
|
3346
|
+
? { kind: 'pending-prefix' }
|
|
3347
|
+
: permission
|
|
3348
|
+
? { kind: 'cancelable', target: 'permission' }
|
|
3349
|
+
: planApproval
|
|
3350
|
+
? { kind: 'cancelable', target: 'plan-approval' }
|
|
3351
|
+
: question
|
|
3352
|
+
? { kind: 'cancelable', target: 'question' }
|
|
3353
|
+
: elicitation
|
|
3354
|
+
? {
|
|
3355
|
+
kind: 'cancelable',
|
|
3356
|
+
target: elicitationUrlWaiting
|
|
3357
|
+
? 'elicitation-url-waiting'
|
|
3358
|
+
: elicitationForm?.expandedField
|
|
3359
|
+
? 'elicitation-options'
|
|
3360
|
+
: 'elicitation',
|
|
3361
|
+
}
|
|
3362
|
+
: selectingSession
|
|
3363
|
+
? { kind: 'delegated', target: 'session-picker' }
|
|
3364
|
+
: menuRef.current
|
|
3365
|
+
? { kind: 'delegated', target: 'menu' }
|
|
3366
|
+
: filePickerVisible
|
|
3367
|
+
? { kind: 'cancelable', target: 'file-picker' }
|
|
3368
|
+
: commandPaletteVisible
|
|
3369
|
+
? { kind: 'cancelable', target: 'command-palette' }
|
|
3370
|
+
: { kind: 'none' };
|
|
3371
|
+
const callerIntent = !busy && value === '?' && inputRef.current.length === 0
|
|
3372
|
+
? 'toggle-shortcuts'
|
|
3373
|
+
: runtimeSettingsRef.current.leftArrowOpensAgents &&
|
|
3374
|
+
key.leftArrow &&
|
|
3375
|
+
inputRef.current.length === 0 &&
|
|
3376
|
+
availableAgents.length > 0
|
|
3377
|
+
? 'open-agents'
|
|
3378
|
+
: key.return &&
|
|
3379
|
+
(key.shift || key.meta) &&
|
|
3380
|
+
keybindingAction === undefined
|
|
3381
|
+
? 'implicit-newline'
|
|
3382
|
+
: 'none';
|
|
3383
|
+
const interaction = routeTuiInteraction({
|
|
3384
|
+
suspensionPending: processSuspendRequestedRef.current,
|
|
3385
|
+
exitConfirmationArmed: exitConfirmation,
|
|
3386
|
+
layer,
|
|
3387
|
+
busy,
|
|
3388
|
+
viewport: {
|
|
3389
|
+
enabled: fixedViewport,
|
|
3390
|
+
offset: transcriptScrollOffsetRef.current,
|
|
3391
|
+
maxOffset: maxTranscriptScrollOffset,
|
|
3392
|
+
pageRows: transcriptPageRows,
|
|
3393
|
+
},
|
|
3394
|
+
composer: interactionComposer(),
|
|
3395
|
+
}, {
|
|
3396
|
+
globalIntent: 'none',
|
|
3397
|
+
scrollIntent,
|
|
3398
|
+
action: keybindingAction,
|
|
3399
|
+
composerKey,
|
|
3400
|
+
timestamp: Date.now(),
|
|
3401
|
+
callerIntent,
|
|
3402
|
+
});
|
|
3403
|
+
applyInteractionEffects(interaction.effects);
|
|
3404
|
+
if (interaction.disposition === 'handled') {
|
|
2983
3405
|
return;
|
|
2984
3406
|
}
|
|
2985
3407
|
const isKeybinding = (action) => keybindingAction === action;
|
|
2986
|
-
if (
|
|
2987
|
-
|
|
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
|
-
}
|
|
3408
|
+
if (hasPendingPrefix)
|
|
3409
|
+
return;
|
|
3030
3410
|
if (permission) {
|
|
3031
3411
|
const options = permission.kind === 'tool' && toolPermissionModel
|
|
3032
3412
|
? toolPermissionModel.options
|
|
@@ -3172,10 +3552,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3172
3552
|
}
|
|
3173
3553
|
permission.resolve(feedback ? { behavior: 'deny', message: feedback } : false);
|
|
3174
3554
|
};
|
|
3175
|
-
if (
|
|
3176
|
-
permission.resolve(false);
|
|
3177
|
-
}
|
|
3178
|
-
else if (permissionFeedbackMode) {
|
|
3555
|
+
if (permissionFeedbackMode) {
|
|
3179
3556
|
if (key.tab) {
|
|
3180
3557
|
clearComposerInput();
|
|
3181
3558
|
setPermissionFeedbackMode(false);
|
|
@@ -3256,10 +3633,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3256
3633
|
...(feedback ? { feedback } : {}),
|
|
3257
3634
|
});
|
|
3258
3635
|
};
|
|
3259
|
-
if (
|
|
3260
|
-
planApproval.resolve({ behavior: 'deny' });
|
|
3261
|
-
}
|
|
3262
|
-
else if (planApprovalFeedbackMode) {
|
|
3636
|
+
if (planApprovalFeedbackMode) {
|
|
3263
3637
|
if (key.tab) {
|
|
3264
3638
|
clearComposerInput();
|
|
3265
3639
|
setPlanApprovalFeedbackMode(false);
|
|
@@ -3296,10 +3670,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3296
3670
|
return;
|
|
3297
3671
|
}
|
|
3298
3672
|
if (question) {
|
|
3299
|
-
if (key.
|
|
3300
|
-
question.resolve(null);
|
|
3301
|
-
}
|
|
3302
|
-
else if (key.return) {
|
|
3673
|
+
if (key.return) {
|
|
3303
3674
|
try {
|
|
3304
3675
|
const current = question.questions[question.index];
|
|
3305
3676
|
if (!current)
|
|
@@ -3353,13 +3724,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3353
3724
|
setElicitationForm(null);
|
|
3354
3725
|
clearComposerInput();
|
|
3355
3726
|
};
|
|
3356
|
-
if (key.
|
|
3357
|
-
if (elicitationUrlWaiting)
|
|
3358
|
-
dismissUrlDialog();
|
|
3359
|
-
else
|
|
3360
|
-
elicitation.resolve({ action: 'cancel' });
|
|
3361
|
-
}
|
|
3362
|
-
else if (key.leftArrow || key.rightArrow) {
|
|
3727
|
+
if (key.leftArrow || key.rightArrow) {
|
|
3363
3728
|
setElicitationForm({
|
|
3364
3729
|
...form,
|
|
3365
3730
|
focusIndex: form.focusIndex === 0 ? 1 : 0,
|
|
@@ -3385,7 +3750,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3385
3750
|
return;
|
|
3386
3751
|
}
|
|
3387
3752
|
if (form.expandedField && currentField) {
|
|
3388
|
-
if (key.
|
|
3753
|
+
if (key.leftArrow) {
|
|
3389
3754
|
setElicitationForm({ ...form, expandedField: undefined });
|
|
3390
3755
|
}
|
|
3391
3756
|
else if (key.upArrow) {
|
|
@@ -3404,9 +3769,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3404
3769
|
setElicitationForm(typeaheadElicitationOption(form, value));
|
|
3405
3770
|
}
|
|
3406
3771
|
}
|
|
3407
|
-
else if (key.escape) {
|
|
3408
|
-
elicitation.resolve({ action: 'cancel' });
|
|
3409
|
-
}
|
|
3410
3772
|
else if (key.upArrow) {
|
|
3411
3773
|
move(-1);
|
|
3412
3774
|
}
|
|
@@ -3973,13 +4335,14 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3973
4335
|
return;
|
|
3974
4336
|
}
|
|
3975
4337
|
if (activeMenu.kind === 'help') {
|
|
3976
|
-
if (key.escape) {
|
|
4338
|
+
if (key.escape || value === '\u001B') {
|
|
3977
4339
|
updateMenu(null);
|
|
3978
4340
|
return;
|
|
3979
4341
|
}
|
|
3980
4342
|
if (key.leftArrow || key.rightArrow) {
|
|
3981
4343
|
updateMenu({
|
|
3982
4344
|
kind: 'help',
|
|
4345
|
+
invocation: activeMenu.invocation,
|
|
3983
4346
|
tabIndex: Math.max(0, Math.min(2, activeMenu.tabIndex + (key.leftArrow ? -1 : 1))),
|
|
3984
4347
|
selectedIndex: 0,
|
|
3985
4348
|
});
|
|
@@ -5175,8 +5538,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5175
5538
|
void backgrounding;
|
|
5176
5539
|
return;
|
|
5177
5540
|
}
|
|
5178
|
-
if (isKeybinding('chat:cancel'))
|
|
5179
|
-
turnControllerRef.current?.abort();
|
|
5180
5541
|
return;
|
|
5181
5542
|
}
|
|
5182
5543
|
if (isKeybinding('chat:imagePaste')) {
|
|
@@ -5192,10 +5553,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5192
5553
|
return;
|
|
5193
5554
|
}
|
|
5194
5555
|
if (filePickerVisible) {
|
|
5195
|
-
if (key.escape || value === '\u001B') {
|
|
5196
|
-
setFilePickerOpen(false);
|
|
5197
|
-
return;
|
|
5198
|
-
}
|
|
5199
5556
|
if (key.upArrow) {
|
|
5200
5557
|
setFileSelection((current) => Math.max(0, current - 1));
|
|
5201
5558
|
return;
|
|
@@ -5220,13 +5577,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5220
5577
|
updateMenu({ kind: 'agents', agents: availableAgents, selectedIndex: 0 });
|
|
5221
5578
|
return;
|
|
5222
5579
|
}
|
|
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
5580
|
if (isKeybinding('chat:cycleMode')) {
|
|
5231
5581
|
const currentIndex = permissionOptions.findIndex((option) => option.mode === runtimePreferences.permissionMode);
|
|
5232
5582
|
const next = permissionOptions[(currentIndex + 1) % permissionOptions.length];
|
|
@@ -5235,10 +5585,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5235
5585
|
return;
|
|
5236
5586
|
}
|
|
5237
5587
|
if (commandPaletteVisible) {
|
|
5238
|
-
if (key.escape) {
|
|
5239
|
-
setCommandPaletteOpen(false);
|
|
5240
|
-
return;
|
|
5241
|
-
}
|
|
5242
5588
|
if (key.upArrow) {
|
|
5243
5589
|
if (matchingSlashCommands.length > 0) {
|
|
5244
5590
|
setCommandSelection((current) => Math.max(0, current - 1));
|
|
@@ -5300,7 +5646,12 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5300
5646
|
exit();
|
|
5301
5647
|
}
|
|
5302
5648
|
else if (prompt === '/help' || prompt === '?') {
|
|
5303
|
-
updateMenu({
|
|
5649
|
+
updateMenu({
|
|
5650
|
+
kind: 'help',
|
|
5651
|
+
invocation: prompt,
|
|
5652
|
+
tabIndex: 0,
|
|
5653
|
+
selectedIndex: 0,
|
|
5654
|
+
});
|
|
5304
5655
|
}
|
|
5305
5656
|
else if (prompt === '/new') {
|
|
5306
5657
|
const previousSessionId = sessionIdRef.current;
|
|
@@ -5678,7 +6029,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5678
6029
|
...current,
|
|
5679
6030
|
{ kind: 'user', text: '/context' },
|
|
5680
6031
|
contextEntry,
|
|
5681
|
-
]);
|
|
6032
|
+
], (current) => current.length);
|
|
5682
6033
|
const loading = (async () => {
|
|
5683
6034
|
try {
|
|
5684
6035
|
const files = await loadMemoryFiles(keybindingsRoot, runtimeCwdRef.current);
|
|
@@ -5695,7 +6046,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5695
6046
|
next[next.length - 1] = { ...last, memoryFiles };
|
|
5696
6047
|
}
|
|
5697
6048
|
return next;
|
|
5698
|
-
});
|
|
6049
|
+
}, (_current, next) => Math.max(0, next.length - 1));
|
|
5699
6050
|
}
|
|
5700
6051
|
catch {
|
|
5701
6052
|
// Leave the Memory files section empty if files cannot be read.
|
|
@@ -5842,50 +6193,20 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5842
6193
|
clearComposerInput();
|
|
5843
6194
|
return;
|
|
5844
6195
|
}
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
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);
|
|
6196
|
+
if (filePickerVisible || commandPaletteVisible) {
|
|
6197
|
+
editComposer();
|
|
6198
|
+
return;
|
|
5860
6199
|
}
|
|
5861
6200
|
});
|
|
5862
|
-
return (_jsx(TuiThemeProvider, { settings: themeSettings, children: _jsx(Box, { flexDirection: "column", ...(!fixedViewport
|
|
6201
|
+
return (_jsx(TuiThemeProvider, { settings: themeSettings, screenReader: axScreenReader, children: _jsx(Box, { flexDirection: "column", ...(!fixedViewport
|
|
5863
6202
|
? {}
|
|
5864
|
-
: { height: rows, overflowY: 'hidden' }), children:
|
|
6203
|
+
: { height: rows, overflowY: 'hidden' }), children: screen.body.kind === 'session-picker' ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [screen.body.intro === 'welcome' ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width, showTips: runtimeSettings.tips, dataPlane: dataPlane })) : null, screen.body.sessionLabel ? (_jsxs(Text, { dimColor: true, children: ["Session ", screen.body.sessionLabel] })) : null, screen.body.intro === 'identity' ? (_jsx(SessionIdentity, { display: runtimeDisplay, width: width })) : null, _jsx(Box, { ...(fixedViewport
|
|
5865
6204
|
? {
|
|
5866
6205
|
flexShrink: 1,
|
|
5867
6206
|
minHeight: 0,
|
|
5868
6207
|
overflowY: 'hidden',
|
|
5869
6208
|
}
|
|
5870
|
-
: {}), children: _jsx(Transcript, { entries:
|
|
5871
|
-
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 ||
|
|
5873
|
-
(permissionSelection === 0
|
|
5874
|
-
? 'tell Praxis what to do next'
|
|
5875
|
-
: 'tell Praxis what to do differently')] })) : null, _jsx(Text, { dimColor: true, children: permissionFeedbackMode
|
|
5876
|
-
? '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
|
|
5878
|
-
? 'Yes, and use auto mode'
|
|
5879
|
-
: allowDangerouslySkipPermissions
|
|
5880
|
-
? '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 ||
|
|
5882
|
-
(planApprovalSelection === 2
|
|
5883
|
-
? 'Tell Praxis what to change'
|
|
5884
|
-
: 'Add feedback for implementation')] })) : null, _jsx(Text, { dimColor: true, children: planApproval.request.planPath }), _jsx(Text, { dimColor: true, children: planApprovalFeedbackMode
|
|
5885
|
-
? 'Enter to submit · Tab to collapse · Esc to cancel'
|
|
5886
|
-
: 'Enter to confirm · Tab to add feedback · Esc to cancel' })] })) : question ? (_jsxs(DialogFrame, { title: `${question.questions[question.index]?.header}: ${question.questions[question.index]?.question}`, screenReader: axScreenReader, children: [question.questions[question.index]?.options.map((option, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [index + 1, ". ", option.label, " \u2014 ", option.description] }), option.preview ? (_jsx(Text, { dimColor: true, children: option.preview })) : null] }, `${index}-${option.label}`))), _jsxs(Text, { children: [axScreenReader ? 'Current answer: ' : '› ', input || (axScreenReader ? '(empty)' : '')] }), _jsx(Text, { dimColor: true, children: question.questions[question.index]?.multiSelect
|
|
5887
|
-
? 'Enter comma-separated option numbers or custom text · Esc cancels'
|
|
5888
|
-
: 'Enter one option number or custom text · Esc cancels' })] })) : elicitation ? (elicitation.request.mode === 'url' ? (_jsx(McpElicitationUrl, { serverName: elicitation.request.serverName, message: elicitation.request.message, url: elicitation.request.url ?? '', waiting: elicitationUrlWaiting, actionLabel: elicitation.request.elicitationId
|
|
6209
|
+
: {}), children: _jsx(Transcript, { entries: screen.body.transcript.entries, activeText: screen.body.transcript.active.text, activeThinking: screen.body.transcript.active.thinking, activeStreamVisible: screen.body.transcript.active.visible, thinkingExpanded: thinkingExpanded, detailedTranscript: screen.body.transcript.readingMode !== 'normal', screenReader: axScreenReader }) }), selectedPriority?.kind === 'editor-wait' ? (_jsx(ExternalEditorWait, { screenReader: axScreenReader })) : selectedPriority?.kind === 'permission' ? (_jsx(PermissionSurface, { model: selectedPriority.surface, width: width, screenReader: axScreenReader })) : selectedPriority?.kind === 'plan-approval' ? (_jsx(DecisionSurface, { model: selectedPriority, width: width, screenReader: axScreenReader })) : selectedPriority?.kind === 'question' ? (_jsx(DecisionSurface, { model: selectedPriority, width: width, screenReader: axScreenReader })) : selectedPriority?.kind === 'elicitation' && elicitation ? (elicitation.request.mode === 'url' ? (_jsx(McpElicitationUrl, { serverName: elicitation.request.serverName, message: elicitation.request.message, url: elicitation.request.url ?? '', waiting: elicitationUrlWaiting, actionLabel: elicitation.request.elicitationId
|
|
5889
6210
|
? 'Skip confirmation'
|
|
5890
6211
|
: 'Continue without waiting', selection: elicitationForm?.focusIndex ?? 0, screenReader: axScreenReader })) : (_jsx(McpElicitationForm, { serverName: elicitation.request.serverName, message: elicitation.request.message, state: elicitationForm ??
|
|
5891
6212
|
createTuiElicitationForm(elicitation.request.requestedSchema), input: input, screenReader: axScreenReader }))) : menu?.kind === 'model-input' ? (_jsxs(DialogFrame, { title: "Enter model ID", screenReader: axScreenReader, children: [_jsx(Text, { dimColor: true, children: "Enter a model ID supported by the configured provider." }), _jsxs(Text, { children: ["\u203A ", input] }), _jsx(Text, { dimColor: true, children: "Enter confirms \u00B7 Esc cancels" })] })) : menu?.kind === 'export-filename' ? (_jsxs(DialogFrame, { title: "Enter filename:", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["> ", input] }), _jsx(Text, { dimColor: true, children: "Enter to save \u00B7 Esc to go back" })] })) : menu?.kind === 'compact-progress' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "\u273B Compacting conversation\u2026" }), _jsxs(Text, { children: ['▰'.repeat(Math.floor(compactProgress / 2)), '▱'.repeat(50 - Math.floor(compactProgress / 2)), ' ', compactProgress, "%"] })] })) : menu?.kind === 'btw' ? (_jsx(BtwPanel, { entries: btwHistory, selectedIndex: menu.selectedIndex, scrollOffset: menu.scrollOffset, copied: btwCopied, width: width, screenReader: axScreenReader })) : menu?.kind === 'rewind' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: " Rewind" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', "Restore the code and/or conversation to the point before\u2026"] }), _jsx(Text, { children: " " }), (() => {
|
|
@@ -5894,9 +6215,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5894
6215
|
.slice(window.start, window.end)
|
|
5895
6216
|
.map((point, offset) => {
|
|
5896
6217
|
const index = window.start + offset;
|
|
5897
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
5898
|
-
|
|
5899
|
-
|
|
6218
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(DecisionOption, { selected: menu.selectedIndex === index, screenReader: axScreenReader, selectedPrefix: " \u276F ", unselectedPrefix: " ", children: point.prompt
|
|
6219
|
+
.replace(/\s+/gu, ' ')
|
|
6220
|
+
.slice(0, 72) }), _jsxs(Text, { dimColor: true, children: [' ', point.fileChanges.length > 0
|
|
5900
6221
|
? point.fileChanges
|
|
5901
6222
|
.map((path) => path.startsWith(`${runtimeCwd}/`)
|
|
5902
6223
|
? path.slice(runtimeCwd.length + 1)
|
|
@@ -5906,26 +6227,16 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5906
6227
|
? 'No code changes'
|
|
5907
6228
|
: '⚠ No code restore'] })] }, point.messageId));
|
|
5908
6229
|
}), window.end < menu.points.length ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2193 ", menu.points.length - window.end, " more below"] })) : null] }));
|
|
5909
|
-
})(),
|
|
6230
|
+
})(), _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
6231
|
? `restore ${menu.point.fileChanges.join(', ')}.`
|
|
5911
|
-
: 'be unchanged.'] }), _jsx(Text, { children: " " }), rewindActions(menu.point).map((option, index) => (_jsxs(
|
|
6232
|
+
: '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
6233
|
? '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" })] })) :
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
},
|
|
5920
|
-
{
|
|
5921
|
-
label: 'Project settings',
|
|
5922
|
-
description: `Checked in at ${settingsDirectory}/settings.json`,
|
|
5923
|
-
},
|
|
5924
|
-
{
|
|
5925
|
-
label: 'User settings',
|
|
5926
|
-
description: `Saved in ~/${settingsDirectory}/settings.json`,
|
|
5927
|
-
},
|
|
5928
|
-
], selectedIndex: menu.selectedIndex, footer: "Enter to confirm \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'agents' ? (_jsx(ListDashboard, { title: "Agents", rows: menu.agents.map((agent) => ({
|
|
6234
|
+
: '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" })] })) : selectedSecondarySurface !== null && menu !== null ? (selectedSecondarySurface.kind === 'permission-dashboard' ||
|
|
6235
|
+
selectedSecondarySurface.kind === 'permission-rule-input' ||
|
|
6236
|
+
selectedSecondarySurface.kind === 'permission-scope' ||
|
|
6237
|
+
selectedSecondarySurface.kind === 'permission-delete' ||
|
|
6238
|
+
selectedSecondarySurface.kind === 'workspace-directory-input' ||
|
|
6239
|
+
selectedSecondarySurface.kind === 'workspace-directory-delete' ? (_jsx(PermissionSurface, { model: selectedSecondarySurface, width: width, screenReader: axScreenReader })) : selectedSecondarySurface.kind === 'help' ? (_jsx(HelpMenu, { model: selectedSecondarySurface, 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 === 'sandbox' ? (_jsx(SandboxDashboard, { snapshot: menu.snapshot, tab: menu.tab, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'agents' ? (_jsx(ListDashboard, { title: "Agents", rows: menu.agents.map((agent) => ({
|
|
5929
6240
|
label: agent.name,
|
|
5930
6241
|
description: agent.description,
|
|
5931
6242
|
})), emptyText: "No agents configured", selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'config' ? (_jsx(ConfigDashboard, { tab: menu.tab, snapshot: menu.snapshot, query: menu.query, selectedIndex: menu.selectedIndex, searchFocused: menu.searchFocused, status: {
|
|
@@ -5957,7 +6268,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5957
6268
|
label: 'Save to file',
|
|
5958
6269
|
description: 'Save the conversation to a file in the current directory',
|
|
5959
6270
|
},
|
|
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: [
|
|
6271
|
+
], 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: [selectedCommandPalette !== undefined ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, selectedFilePicker !== undefined ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, selectedExitConfirmation !== undefined ? (_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
6272
|
? {}
|
|
5962
6273
|
: { 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
6274
|
? (() => {
|
|
@@ -5969,7 +6280,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
5969
6280
|
: {}), editorMode: runtimeSettings.editor, ...(runtimeSettings.prStatus &&
|
|
5970
6281
|
activeSessionSummary?.prNumber
|
|
5971
6282
|
? { prStatus: `PR #${activeSessionSummary.prNumber}` }
|
|
5972
|
-
: {}), shortcutsVisible: shortcutsVisible, ...(editorFooterMessage === undefined
|
|
6283
|
+
: {}), shortcutsVisible: shortcutsVisible, shortcutHelp: shortcutHelpSurface, ...(editorFooterMessage === undefined
|
|
5973
6284
|
? {}
|
|
5974
6285
|
: { footerMessage: editorFooterMessage }) }), _jsx(StatusLine, { configRoot: keybindingsRoot, cwd: runtimeCwd, dataPlane: dataPlane, input: createClaudeStatusLineInput({
|
|
5975
6286
|
configRoot: keybindingsRoot,
|
|
@@ -6176,7 +6487,7 @@ export async function runInteractive(options) {
|
|
|
6176
6487
|
? { allowDangerouslySkipPermissions: true }
|
|
6177
6488
|
: {}) }), {
|
|
6178
6489
|
exitOnCtrlC: false,
|
|
6179
|
-
...
|
|
6490
|
+
...tuiInkRenderOptions(currentRenderer, options.axScreenReader === true),
|
|
6180
6491
|
interactive: true,
|
|
6181
6492
|
});
|
|
6182
6493
|
await instance.waitUntilExit();
|