@namzu/cli 14.1.0 → 14.2.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 +18 -9
- package/dist/integrations/providers/discover.d.ts +4 -0
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/discover.js +55 -37
- package/dist/integrations/providers/discover.js.map +1 -1
- package/dist/integrations/providers/harness-credentials.d.ts +44 -9
- package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
- package/dist/integrations/providers/harness-credentials.js +140 -11
- package/dist/integrations/providers/harness-credentials.js.map +1 -1
- package/dist/integrations/providers/identity.d.ts +15 -14
- package/dist/integrations/providers/identity.d.ts.map +1 -1
- package/dist/integrations/providers/identity.js +15 -14
- package/dist/integrations/providers/identity.js.map +1 -1
- package/dist/integrations/providers/oauth.d.ts +8 -3
- package/dist/integrations/providers/oauth.d.ts.map +1 -1
- package/dist/integrations/providers/oauth.js +37 -11
- package/dist/integrations/providers/oauth.js.map +1 -1
- package/dist/integrations/providers/subscription-login.d.ts.map +1 -1
- package/dist/integrations/providers/subscription-login.js +12 -8
- package/dist/integrations/providers/subscription-login.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +262 -34
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/ChoicePicker.d.ts +14 -0
- package/dist/tui/ChoicePicker.d.ts.map +1 -0
- package/dist/tui/ChoicePicker.js +15 -0
- package/dist/tui/ChoicePicker.js.map +1 -0
- package/dist/tui/StatusBar.d.ts +23 -39
- package/dist/tui/StatusBar.d.ts.map +1 -1
- package/dist/tui/StatusBar.js +67 -105
- package/dist/tui/StatusBar.js.map +1 -1
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +10 -8
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +8 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +20 -15
- package/dist/tui/slashCommands.js.map +1 -1
- package/package.json +4 -4
package/dist/tui/App.js
CHANGED
|
@@ -27,6 +27,7 @@ import { checkUpdates } from '../integrations/updates.js';
|
|
|
27
27
|
import { appendMemory, composeMemoryPrompt, readMemory } from '../memory/store.js';
|
|
28
28
|
import { composeSkillsPrompt, discoverSkills, loadSkillBody } from '../skills/store.js';
|
|
29
29
|
import { discoverUserCommands } from '../user-commands/store.js';
|
|
30
|
+
import { ChoicePicker } from './ChoicePicker.js';
|
|
30
31
|
import { Composer } from './Composer.js';
|
|
31
32
|
import { CopyPicker } from './CopyPicker.js';
|
|
32
33
|
import { EditPromptPicker } from './EditPromptPicker.js';
|
|
@@ -42,7 +43,7 @@ import { bottomSpacerRows } from './bottom-spacer.js';
|
|
|
42
43
|
import { keepRecentRows } from './compact-transcript.js';
|
|
43
44
|
import { approvalIsDeliberate } from './consent-timing.js';
|
|
44
45
|
import { planTurnPublication } from './conversation-history.js';
|
|
45
|
-
import { copyTargetsForResponse
|
|
46
|
+
import { copyTargetsForResponse } from './copy-targets.js';
|
|
46
47
|
import { editablePrompts } from './edit-prompts.js';
|
|
47
48
|
import { liveWindow, transcriptLines } from './live-window.js';
|
|
48
49
|
import { describeCodexDeviceLoginStart, describeLoginOutcome, describeLoginStart, describeLogout, } from './login-prompt.js';
|
|
@@ -328,6 +329,14 @@ export function App({ ctx: initialCtx }) {
|
|
|
328
329
|
copyPickerRef.current = next;
|
|
329
330
|
setCopyPickerState(next);
|
|
330
331
|
}, []);
|
|
332
|
+
/** Finite slash-command choice owned synchronously until apply or cancel. */
|
|
333
|
+
const [choicePicker, setChoicePickerState] = useState(null);
|
|
334
|
+
const choicePickerRef = useRef(null);
|
|
335
|
+
const [selectedChoice, setSelectedChoice] = useState(0);
|
|
336
|
+
const setChoicePicker = useCallback((next) => {
|
|
337
|
+
choicePickerRef.current = next;
|
|
338
|
+
setChoicePickerState(next);
|
|
339
|
+
}, []);
|
|
331
340
|
const [composerDraft, setComposerDraft] = useState(null);
|
|
332
341
|
const composerDraftTokenRef = useRef(0);
|
|
333
342
|
const exitArmedRef = useRef(false);
|
|
@@ -399,6 +408,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
399
408
|
const [goalActivation] = useState(() => new SessionGoalActivation());
|
|
400
409
|
const goalDriveInFlightRef = useRef(false);
|
|
401
410
|
const [goalDriveVersion, setGoalDriveVersion] = useState(0);
|
|
411
|
+
const [goalStatus, setGoalStatus] = useState(null);
|
|
402
412
|
const wakeGoalDriver = useCallback(() => setGoalDriveVersion((version) => version + 1), []);
|
|
403
413
|
/**
|
|
404
414
|
* Conversation writes in the order the operator produced them.
|
|
@@ -517,6 +527,70 @@ export function App({ ctx: initialCtx }) {
|
|
|
517
527
|
]);
|
|
518
528
|
return id;
|
|
519
529
|
}, [nextId]);
|
|
530
|
+
const applyPermissionMode = useCallback((mode) => {
|
|
531
|
+
if (!session?.hasProvider) {
|
|
532
|
+
pushMessage('system', 'No active session — pick a provider before changing permissions.');
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (state !== 'idle' ||
|
|
536
|
+
abortRef.current !== null ||
|
|
537
|
+
hasUnsettledTurn() ||
|
|
538
|
+
queuedRef.current.length > 0 ||
|
|
539
|
+
permissionResolveRef.current !== null ||
|
|
540
|
+
compactingRef.current) {
|
|
541
|
+
pushMessage('system', 'Permission mode was not changed: wait for the active turn, prompt, compaction, and queued work to settle.');
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (!session.resetApprovalLatch) {
|
|
545
|
+
pushMessage('system', 'Permission mode was not changed: this embedded session cannot revoke an earlier "approve all" choice. Reconnect it before changing modes.');
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
session.resetApprovalLatch();
|
|
549
|
+
permissionModeRef.current = mode;
|
|
550
|
+
permissionModeSourceRef.current = 'session';
|
|
551
|
+
setPermissionModeState(mode);
|
|
552
|
+
pushMessage('system', `Permission mode changed to ${mode}. Any earlier "approve all" choice was revoked. Declarative deny rules and the built-in safety gate still take precedence.`);
|
|
553
|
+
}, [hasUnsettledTurn, pushMessage, session, state]);
|
|
554
|
+
const applyReasoningEffort = useCallback((effort) => {
|
|
555
|
+
if (!session?.hasProvider) {
|
|
556
|
+
pushMessage('system', 'No active session — pick a provider before changing reasoning effort.');
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (state !== 'idle' ||
|
|
560
|
+
abortRef.current !== null ||
|
|
561
|
+
hasUnsettledTurn() ||
|
|
562
|
+
queuedRef.current.length > 0 ||
|
|
563
|
+
permissionResolveRef.current !== null ||
|
|
564
|
+
compactingRef.current) {
|
|
565
|
+
pushMessage('system', 'Reasoning effort was not changed: wait for the active turn, prompt, compaction, and queued work to settle.');
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (effort === undefined) {
|
|
569
|
+
setReasoningEffort(undefined);
|
|
570
|
+
pushMessage('system', 'Reasoning effort reset to the provider default for future main-query turns.');
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (!session.reasoningEffortLevels?.includes(effort)) {
|
|
574
|
+
pushMessage('system', `Reasoning effort was not changed: ${effort} is not offered by every usable provider-chain member for this model.`);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
setReasoningEffort(effort);
|
|
578
|
+
pushMessage('system', `Reasoning effort changed to ${effort} for future main-query turns in this session.`);
|
|
579
|
+
}, [hasUnsettledTurn, pushMessage, session, setReasoningEffort, state]);
|
|
580
|
+
const applyChoiceSelection = useCallback((index) => {
|
|
581
|
+
const picker = choicePickerRef.current;
|
|
582
|
+
if (!picker)
|
|
583
|
+
return;
|
|
584
|
+
const value = picker.values[index];
|
|
585
|
+
if (index < 0 || index >= picker.values.length)
|
|
586
|
+
return;
|
|
587
|
+
setChoicePicker(null);
|
|
588
|
+
if (picker.kind === 'permission-mode') {
|
|
589
|
+
applyPermissionMode(value);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
applyReasoningEffort(value);
|
|
593
|
+
}, [applyPermissionMode, applyReasoningEffort, setChoicePicker]);
|
|
520
594
|
const sendCopyRequest = useCallback((index) => {
|
|
521
595
|
const picker = copyPickerRef.current;
|
|
522
596
|
const target = picker?.targets[index];
|
|
@@ -2068,6 +2142,10 @@ export function App({ ctx: initialCtx }) {
|
|
|
2068
2142
|
setPhase('picker');
|
|
2069
2143
|
return;
|
|
2070
2144
|
case 'permission-mode': {
|
|
2145
|
+
applyPermissionMode(slash.mode);
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
case 'permission-mode-picker': {
|
|
2071
2149
|
if (!session?.hasProvider) {
|
|
2072
2150
|
pushMessage('system', 'No active session — pick a provider before changing permissions.');
|
|
2073
2151
|
return;
|
|
@@ -2077,24 +2155,34 @@ export function App({ ctx: initialCtx }) {
|
|
|
2077
2155
|
hasUnsettledTurn() ||
|
|
2078
2156
|
queuedRef.current.length > 0 ||
|
|
2079
2157
|
permissionResolveRef.current !== null ||
|
|
2080
|
-
compactingRef.current
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
}
|
|
2084
|
-
if (!session.resetApprovalLatch) {
|
|
2085
|
-
pushMessage('system', 'Permission mode was not changed: this embedded session cannot revoke an earlier "approve all" choice. Reconnect it before changing modes.');
|
|
2158
|
+
compactingRef.current ||
|
|
2159
|
+
!session.resetApprovalLatch) {
|
|
2160
|
+
pushMessage('system', 'Permission choices are unavailable until the active turn, prompt, compaction, queued work, or embedded-session approval latch can be safely settled.');
|
|
2086
2161
|
return;
|
|
2087
2162
|
}
|
|
2088
|
-
|
|
2089
|
-
permissionModeRef.current
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2163
|
+
const values = ['prompt', 'auto', 'strict'];
|
|
2164
|
+
setSelectedChoice(Math.max(0, values.indexOf(permissionModeRef.current)));
|
|
2165
|
+
setChoicePicker({
|
|
2166
|
+
kind: 'permission-mode',
|
|
2167
|
+
title: 'Select Permission Mode',
|
|
2168
|
+
notice: permissionPickerNotice(session),
|
|
2169
|
+
values,
|
|
2170
|
+
options: values.map((mode) => ({
|
|
2171
|
+
label: mode,
|
|
2172
|
+
description: permissionModeDescription(mode),
|
|
2173
|
+
current: mode === permissionModeRef.current,
|
|
2174
|
+
})),
|
|
2175
|
+
});
|
|
2093
2176
|
return;
|
|
2094
2177
|
}
|
|
2095
2178
|
case 'reasoning-effort': {
|
|
2096
|
-
|
|
2097
|
-
|
|
2179
|
+
applyReasoningEffort(slash.effort ?? undefined);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
case 'reasoning-effort-picker': {
|
|
2183
|
+
const levels = session?.reasoningEffortLevels;
|
|
2184
|
+
if (!session?.hasProvider || levels === undefined) {
|
|
2185
|
+
pushMessage('system', 'No exact reasoning-effort menu is available for the current session.');
|
|
2098
2186
|
return;
|
|
2099
2187
|
}
|
|
2100
2188
|
if (state !== 'idle' ||
|
|
@@ -2103,20 +2191,22 @@ export function App({ ctx: initialCtx }) {
|
|
|
2103
2191
|
queuedRef.current.length > 0 ||
|
|
2104
2192
|
permissionResolveRef.current !== null ||
|
|
2105
2193
|
compactingRef.current) {
|
|
2106
|
-
pushMessage('system', 'Reasoning
|
|
2107
|
-
return;
|
|
2108
|
-
}
|
|
2109
|
-
if (slash.effort === null) {
|
|
2110
|
-
setReasoningEffort(undefined);
|
|
2111
|
-
pushMessage('system', 'Reasoning effort reset to the provider default for future main-query turns.');
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
if (!session.reasoningEffortLevels?.includes(slash.effort)) {
|
|
2115
|
-
pushMessage('system', `Reasoning effort was not changed: ${slash.effort} is not offered by every usable provider-chain member for this model.`);
|
|
2194
|
+
pushMessage('system', 'Reasoning-effort choices are unavailable until the active turn, prompt, compaction, and queued work settle.');
|
|
2116
2195
|
return;
|
|
2117
2196
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2197
|
+
const values = [undefined, ...levels];
|
|
2198
|
+
const current = reasoningEffortRef.current;
|
|
2199
|
+
setSelectedChoice(Math.max(0, values.findIndex((value) => value === current)));
|
|
2200
|
+
setChoicePicker({
|
|
2201
|
+
kind: 'reasoning-effort',
|
|
2202
|
+
title: `Select Reasoning Level for ${session.modelSummary ?? 'current model'}`,
|
|
2203
|
+
values,
|
|
2204
|
+
options: values.map((effort) => ({
|
|
2205
|
+
label: effort ?? 'default',
|
|
2206
|
+
description: reasoningEffortDescription(effort),
|
|
2207
|
+
current: effort === current,
|
|
2208
|
+
})),
|
|
2209
|
+
});
|
|
2120
2210
|
return;
|
|
2121
2211
|
}
|
|
2122
2212
|
case 'login':
|
|
@@ -2587,6 +2677,8 @@ export function App({ ctx: initialCtx }) {
|
|
|
2587
2677
|
}, [
|
|
2588
2678
|
activeSkills,
|
|
2589
2679
|
advanceQueueContinuation,
|
|
2680
|
+
applyPermissionMode,
|
|
2681
|
+
applyReasoningEffort,
|
|
2590
2682
|
ctx.cwd,
|
|
2591
2683
|
doResume,
|
|
2592
2684
|
enqueueQueued,
|
|
@@ -2596,12 +2688,45 @@ export function App({ ctx: initialCtx }) {
|
|
|
2596
2688
|
pushMessage,
|
|
2597
2689
|
rawOutput,
|
|
2598
2690
|
resetTranscript,
|
|
2691
|
+
setChoicePicker,
|
|
2599
2692
|
setCopyPicker,
|
|
2600
2693
|
setReasoningEffort,
|
|
2601
2694
|
slashCtx,
|
|
2602
2695
|
startFreshConversation,
|
|
2603
2696
|
state,
|
|
2604
2697
|
]);
|
|
2698
|
+
// Keep the footer on the same durable goal record the driver mutates. A ref
|
|
2699
|
+
// alone cannot repaint React, so every goal mutation bumps goalDriveVersion;
|
|
2700
|
+
// generation/session checks stop a late disk read labelling a resumed chat.
|
|
2701
|
+
useEffect(() => {
|
|
2702
|
+
void goalDriveVersion;
|
|
2703
|
+
const sessions = sessionsRef.current;
|
|
2704
|
+
const scope = scopeRef.current;
|
|
2705
|
+
if (!sessions?.goals || !scope) {
|
|
2706
|
+
setGoalStatus(null);
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
const generation = conversationGenRef.current;
|
|
2710
|
+
const sessionId = scope.sessionId;
|
|
2711
|
+
let disposed = false;
|
|
2712
|
+
void sessions.goals
|
|
2713
|
+
.getGoal(sessionId, sessions.tenantId)
|
|
2714
|
+
.then((goal) => {
|
|
2715
|
+
if (disposed ||
|
|
2716
|
+
conversationGenRef.current !== generation ||
|
|
2717
|
+
scopeRef.current?.sessionId !== sessionId) {
|
|
2718
|
+
return;
|
|
2719
|
+
}
|
|
2720
|
+
setGoalStatus(goal);
|
|
2721
|
+
})
|
|
2722
|
+
.catch(() => {
|
|
2723
|
+
if (!disposed && conversationGenRef.current === generation)
|
|
2724
|
+
setGoalStatus(null);
|
|
2725
|
+
});
|
|
2726
|
+
return () => {
|
|
2727
|
+
disposed = true;
|
|
2728
|
+
};
|
|
2729
|
+
}, [goalDriveVersion, phase, session]);
|
|
2605
2730
|
// Admit automatic work only at a whole-App durable boundary. This effect
|
|
2606
2731
|
// reserves; it never starts a turn itself. The one queue pump below remains
|
|
2607
2732
|
// the sole starter, which is what keeps a human prompt that arrives during
|
|
@@ -2622,6 +2747,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
2622
2747
|
conversationMutationRef.current ||
|
|
2623
2748
|
exportingRef.current ||
|
|
2624
2749
|
compactingRef.current ||
|
|
2750
|
+
choicePickerRef.current ||
|
|
2625
2751
|
copyPickerRef.current ||
|
|
2626
2752
|
goalCommandInFlightRef.current) {
|
|
2627
2753
|
return;
|
|
@@ -2647,6 +2773,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
2647
2773
|
conversationMutationRef.current ||
|
|
2648
2774
|
exportingRef.current ||
|
|
2649
2775
|
compactingRef.current ||
|
|
2776
|
+
choicePickerRef.current ||
|
|
2650
2777
|
copyPickerRef.current ||
|
|
2651
2778
|
goalCommandInFlightRef.current ||
|
|
2652
2779
|
queuedRef.current.length > 0 ||
|
|
@@ -2705,6 +2832,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
2705
2832
|
conversationMutation,
|
|
2706
2833
|
session,
|
|
2707
2834
|
state,
|
|
2835
|
+
choicePicker,
|
|
2708
2836
|
copyPicker,
|
|
2709
2837
|
wakeGoalDriver,
|
|
2710
2838
|
]);
|
|
@@ -2720,6 +2848,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
2720
2848
|
conversationMutationRef.current ||
|
|
2721
2849
|
exportingRef.current ||
|
|
2722
2850
|
compactingRef.current ||
|
|
2851
|
+
choicePickerRef.current ||
|
|
2723
2852
|
copyPickerRef.current ||
|
|
2724
2853
|
goalCommandInFlightRef.current)
|
|
2725
2854
|
return;
|
|
@@ -2735,6 +2864,7 @@ export function App({ ctx: initialCtx }) {
|
|
|
2735
2864
|
phase,
|
|
2736
2865
|
queued,
|
|
2737
2866
|
queuePause,
|
|
2867
|
+
choicePicker,
|
|
2738
2868
|
copyPicker,
|
|
2739
2869
|
dequeueQueued,
|
|
2740
2870
|
hasUnsettledTurn,
|
|
@@ -2978,6 +3108,34 @@ export function App({ ctx: initialCtx }) {
|
|
|
2978
3108
|
resolvePermission({ kind: 'approve-all' });
|
|
2979
3109
|
return;
|
|
2980
3110
|
}
|
|
3111
|
+
// Finite slash-command choices own the keyboard below a live permission
|
|
3112
|
+
// request and above the ordinary composer. Their values are a captured
|
|
3113
|
+
// menu, so a provider/session change cannot silently reinterpret Enter.
|
|
3114
|
+
if (choicePickerRef.current) {
|
|
3115
|
+
const options = choicePickerRef.current.options;
|
|
3116
|
+
if (key.escape || (key.ctrl && input === 'c')) {
|
|
3117
|
+
setChoicePicker(null);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (key.upArrow) {
|
|
3121
|
+
setSelectedChoice((index) => Math.max(0, index - 1));
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
if (key.downArrow) {
|
|
3125
|
+
setSelectedChoice((index) => Math.min(options.length - 1, index + 1));
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
if (key.return) {
|
|
3129
|
+
applyChoiceSelection(selectedChoice);
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
if (/^[1-9]$/.test(input)) {
|
|
3133
|
+
const index = Number(input) - 1;
|
|
3134
|
+
if (options[index])
|
|
3135
|
+
applyChoiceSelection(index);
|
|
3136
|
+
}
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
2981
3139
|
// `/copy` holds a source snapshot, not a lifecycle phase. Permission
|
|
2982
3140
|
// stays above it in this handler so an approval request that arrives
|
|
2983
3141
|
// while the picker is open cannot have its keys stolen by the chooser.
|
|
@@ -3082,19 +3240,29 @@ export function App({ ctx: initialCtx }) {
|
|
|
3082
3240
|
// Background is left natural — we inherit the terminal's own background
|
|
3083
3241
|
// and only theme the foreground. Forcing
|
|
3084
3242
|
// a filled bg left mismatched patches around bordered areas, so we don't.
|
|
3085
|
-
return (_jsxs(Box, { flexDirection: "column", children: [phase !== 'ready' ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : null, _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onCredential: handleTypedCredential, onLogin: (provider, signal) => void startLoginFromPicker(provider, signal), keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [_jsx(TranscriptFrame, { children: _jsx(Transcript, { messages: finalized, pending: messages.find((m) => m.pending) ?? null, state: state, settled: window.settled, resetKey: resetKey, raw: rawOutput, header: phase === 'ready' ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : undefined }) }), spacerRows > 0 ? _jsx(Box, { height: spacerRows }) : null, _jsx(LiveActivity, { activeTools: activeTools, thinking: state === 'thinking' && !messages.some((m) => m.pending) }), permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls })) : copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { focus: state === 'idle' &&
|
|
3243
|
+
return (_jsxs(Box, { flexDirection: "column", children: [phase !== 'ready' ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : null, _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onCredential: handleTypedCredential, onLogin: (provider, signal) => void startLoginFromPicker(provider, signal), keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [_jsx(TranscriptFrame, { children: _jsx(Transcript, { messages: finalized, pending: messages.find((m) => m.pending) ?? null, state: state, settled: window.settled, resetKey: resetKey, raw: rawOutput, header: phase === 'ready' ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : undefined }) }), spacerRows > 0 ? _jsx(Box, { height: spacerRows }) : null, _jsx(LiveActivity, { activeTools: activeTools, thinking: state === 'thinking' && !messages.some((m) => m.pending) }), permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls })) : choicePicker ? (_jsx(ChoicePicker, { title: choicePicker.title, notice: choicePicker.notice, options: choicePicker.options, selected: selectedChoice })) : copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { focus: state === 'idle' &&
|
|
3086
3244
|
phase === 'ready' &&
|
|
3087
3245
|
conversationMutation === null &&
|
|
3088
|
-
|
|
3246
|
+
choicePicker === null &&
|
|
3247
|
+
copyPicker === null, hidden: permission !== null || choicePicker !== null || copyPicker !== null, children: [queued.length > 0 &&
|
|
3248
|
+
permission === null &&
|
|
3249
|
+
choicePicker === null &&
|
|
3250
|
+
copyPicker === null ? (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: theme.text.muted, children: [queuePause ? '⏸' : '⏎', " ", queued.length, " message", queued.length > 1 ? 's' : '', " queued \u2014", ' ', queuePause
|
|
3089
3251
|
? `paused after a ${queuePause.outcome} turn; send a message or change model to continue`
|
|
3090
3252
|
: 'sending when ready'] }) })) : null, _jsx(Composer, { disabled: phase !== 'ready' ||
|
|
3091
3253
|
state === 'awaiting-permission' ||
|
|
3092
3254
|
compacting ||
|
|
3093
|
-
conversationMutation !== null, hidden: permission !== null || copyPicker !== null,
|
|
3255
|
+
conversationMutation !== null, hidden: permission !== null || choicePicker !== null || copyPicker !== null,
|
|
3094
3256
|
// A turn is running, so Esc is the interrupt and not
|
|
3095
3257
|
// the composer's clear.
|
|
3096
|
-
escapeInterrupts: !compacting && (state === 'thinking' || state === 'tool'), onSubmit: handleSubmit, onNotice: (text) => pushMessage('system', text), onEditPrevious: openPromptEditor, draftToRestore: composerDraft, onDraftRestored: (token) => setComposerDraft((draft) => (draft?.token === token ? null : draft)), userCommands: userCommands, history: history })] })] })), _jsx(Box, { paddingTop: 1, children: _jsx(StatusBar, { cwd: ctx.cwd, provider: session?.providerSummary ?? null, model: session?.modelSummary
|
|
3097
|
-
|
|
3258
|
+
escapeInterrupts: !compacting && (state === 'thinking' || state === 'tool'), onSubmit: handleSubmit, onNotice: (text) => pushMessage('system', text), onEditPrevious: openPromptEditor, draftToRestore: composerDraft, onDraftRestored: (token) => setComposerDraft((draft) => (draft?.token === token ? null : draft)), userCommands: userCommands, history: history })] })] })), _jsx(Box, { paddingTop: 1, children: _jsx(StatusBar, { cwd: ctx.cwd, provider: session?.providerSummary ?? null, model: session?.modelSummary ?? null, effort: reasoningEffort ?? 'default', goal: phase === 'ready' &&
|
|
3259
|
+
state === 'idle' &&
|
|
3260
|
+
conversationMutation === null &&
|
|
3261
|
+
!compacting &&
|
|
3262
|
+
permission === null &&
|
|
3263
|
+
choicePicker === null &&
|
|
3264
|
+
copyPicker === null
|
|
3265
|
+
? goalStatusLabel(goalStatus)
|
|
3098
3266
|
: null, state: state, hint: conversationMutation === 'fork'
|
|
3099
3267
|
? 'forking conversation — input is paused'
|
|
3100
3268
|
: conversationMutation === 'edit'
|
|
@@ -3105,9 +3273,13 @@ export function App({ ctx: initialCtx }) {
|
|
|
3105
3273
|
? 'compacting conversation — input is paused'
|
|
3106
3274
|
: permission
|
|
3107
3275
|
? hintForPhase(phase, state, session?.hasProvider === true)
|
|
3108
|
-
:
|
|
3109
|
-
? '
|
|
3110
|
-
:
|
|
3276
|
+
: choicePicker
|
|
3277
|
+
? 'choice open — ↑↓ / 1–9 select · enter apply · esc cancel'
|
|
3278
|
+
: copyPicker
|
|
3279
|
+
? 'copy target open — ↑↓ / 1–9 select · esc cancel'
|
|
3280
|
+
: goalStatus && phase === 'ready' && state === 'idle'
|
|
3281
|
+
? undefined
|
|
3282
|
+
: hintForPhase(phase, state, session?.hasProvider === true), usage: usage, context: context }) })] })] }));
|
|
3111
3283
|
}
|
|
3112
3284
|
function Banner({ version, session, permissionMode, cwd, }) {
|
|
3113
3285
|
const cols = process.stdout.columns ?? 80;
|
|
@@ -3165,6 +3337,62 @@ canReturn = false) {
|
|
|
3165
3337
|
return 'agent is working — esc to interrupt';
|
|
3166
3338
|
return '/help · Esc×2 edit previous · @file / Ctrl+V attach · Ctrl+C ×2 to exit';
|
|
3167
3339
|
}
|
|
3340
|
+
function goalStatusLabel(goal) {
|
|
3341
|
+
if (!goal)
|
|
3342
|
+
return null;
|
|
3343
|
+
switch (goal.phase) {
|
|
3344
|
+
case 'active':
|
|
3345
|
+
return 'Pursuing goal';
|
|
3346
|
+
case 'paused':
|
|
3347
|
+
return 'Goal paused (/goal resume)';
|
|
3348
|
+
case 'blocked':
|
|
3349
|
+
return 'Goal stalled (/goal resume)';
|
|
3350
|
+
case 'complete':
|
|
3351
|
+
return 'Goal achieved';
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
function permissionModeDescription(mode) {
|
|
3355
|
+
switch (mode) {
|
|
3356
|
+
case 'prompt':
|
|
3357
|
+
return 'Ask before an undecided tool call runs';
|
|
3358
|
+
case 'auto':
|
|
3359
|
+
return 'Approve undecided calls unless a rule or safety gate refuses';
|
|
3360
|
+
case 'strict':
|
|
3361
|
+
return 'Refuse undecided calls; explicit allow rules still work';
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
function permissionPickerNotice(session) {
|
|
3365
|
+
const notices = [];
|
|
3366
|
+
if (session.approvalLatched()) {
|
|
3367
|
+
notices.push('Approve all is active; applying any mode revokes it.');
|
|
3368
|
+
}
|
|
3369
|
+
const exempt = session.promptExemptTools();
|
|
3370
|
+
if (exempt.length > 0)
|
|
3371
|
+
notices.push(`Never prompted: ${exempt.join(', ')}.`);
|
|
3372
|
+
return notices.length > 0 ? notices.join(' ') : undefined;
|
|
3373
|
+
}
|
|
3374
|
+
function reasoningEffortDescription(effort) {
|
|
3375
|
+
switch (effort) {
|
|
3376
|
+
case undefined:
|
|
3377
|
+
return 'Use the provider and model default';
|
|
3378
|
+
case 'none':
|
|
3379
|
+
return 'No deliberate reasoning where the model supports it';
|
|
3380
|
+
case 'minimal':
|
|
3381
|
+
return 'Fastest responses with minimal reasoning';
|
|
3382
|
+
case 'low':
|
|
3383
|
+
return 'Fast responses with light reasoning';
|
|
3384
|
+
case 'medium':
|
|
3385
|
+
return 'Balance speed and reasoning depth';
|
|
3386
|
+
case 'high':
|
|
3387
|
+
return 'Deeper reasoning for complex problems';
|
|
3388
|
+
case 'xhigh':
|
|
3389
|
+
return 'Very deep reasoning for hard problems';
|
|
3390
|
+
case 'max':
|
|
3391
|
+
return 'Maximum reasoning depth offered by this model';
|
|
3392
|
+
case 'ultra':
|
|
3393
|
+
return 'Highest host-level reasoning setting when supported';
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3168
3396
|
/**
|
|
3169
3397
|
* The summary text, as collapsible detail under the compaction row.
|
|
3170
3398
|
*
|