ethagent 2.2.0 → 2.4.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 +11 -0
- package/package.json +2 -1
- package/src/app/FirstRun.tsx +3 -7
- package/src/app/FirstRunTimeline.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +29 -11
- package/src/chat/ChatScreen.tsx +169 -38
- package/src/chat/ConversationStack.tsx +1 -1
- package/src/chat/MessageList.tsx +185 -72
- package/src/chat/SessionStatus.tsx +3 -1
- package/src/chat/chatScreenUtils.ts +11 -15
- package/src/chat/chatSessionState.ts +5 -2
- package/src/chat/chatTurnOrchestrator.ts +7 -9
- package/src/chat/commands.ts +26 -26
- package/src/chat/display/DiffView.tsx +193 -0
- package/src/chat/display/SyntaxText.tsx +192 -0
- package/src/chat/display/toolCallDisplay.ts +103 -0
- package/src/chat/display/toolResultDisplay.ts +19 -0
- package/src/chat/{ChatInput.tsx → input/ChatInput.tsx} +61 -25
- package/src/chat/input/imageRefs.ts +30 -0
- package/src/chat/{TranscriptView.tsx → transcript/TranscriptView.tsx} +24 -50
- package/src/chat/{transcriptViewport.ts → transcript/transcriptViewport.ts} +12 -30
- package/src/chat/{ContextLimitView.tsx → views/ContextLimitView.tsx} +3 -3
- package/src/chat/{ContinuityEditReviewView.tsx → views/ContinuityEditReviewView.tsx} +11 -3
- package/src/chat/{CopyPicker.tsx → views/CopyPicker.tsx} +4 -5
- package/src/chat/{PermissionPrompt.tsx → views/PermissionPrompt.tsx} +16 -17
- package/src/chat/{PermissionsView.tsx → views/PermissionsView.tsx} +6 -6
- package/src/chat/{PlanApprovalView.tsx → views/PlanApprovalView.tsx} +4 -4
- package/src/chat/{ResumeView.tsx → views/ResumeView.tsx} +50 -41
- package/src/chat/views/RewindView.tsx +410 -0
- package/src/identity/continuity/privateEdit/diff.ts +2 -78
- package/src/identity/hub/OperationalRoutes.tsx +21 -21
- package/src/identity/hub/Routes.tsx +13 -13
- package/src/identity/hub/{flows/continuity → continuity}/ContinuityDashboardScreen.tsx +9 -9
- package/src/identity/hub/{flows/continuity → continuity}/RebackupStorageScreen.tsx +2 -2
- package/src/identity/hub/{flows/continuity → continuity}/RecoveryConfirmScreen.tsx +5 -5
- package/src/identity/hub/{flows/continuity → continuity}/SavePromptScreen.tsx +5 -5
- package/src/identity/hub/{effects/rebackup/runRebackup.ts → continuity/effects.ts} +17 -17
- package/src/identity/hub/{effects/rebackup → continuity}/index.ts +1 -1
- package/src/identity/hub/{effects/shared → continuity}/snapshot.ts +8 -8
- package/src/identity/hub/{effects/rebackup → continuity}/vault.ts +15 -15
- package/src/identity/hub/{flows/create → create}/CreateFlow.tsx +13 -13
- package/src/identity/hub/{effects/create.ts → create/effects.ts} +4 -4
- package/src/identity/hub/{flows/custody → custody}/CustodyEditFlow.tsx +9 -9
- package/src/identity/hub/{flows/custody/custodyFlowActions.ts → custody/actions.ts} +6 -6
- package/src/identity/hub/{flows/custody/custodyFlowHelpers.ts → custody/helpers.ts} +4 -4
- package/src/identity/hub/{effects/vault → custody}/preflight.ts +5 -5
- package/src/identity/hub/{flows/custody/custodyFlowRoutes.tsx → custody/routes.tsx} +8 -8
- package/src/identity/hub/{flows/custody/custodyEffects.ts → custody/transactions.ts} +9 -9
- package/src/identity/hub/{flows/custody/custodyFlowTypes.ts → custody/types.ts} +5 -5
- package/src/identity/hub/{flows/custody/custodyFlowEffects.ts → custody/useCustodyEffects.ts} +7 -7
- package/src/identity/hub/{flows/custody → custody}/useCustodyFlow.tsx +5 -5
- package/src/identity/hub/{flows/ens → ens}/EnsEditAdvancedScreens.tsx +13 -13
- package/src/identity/hub/{flows/ens → ens}/EnsEditFlow.tsx +7 -7
- package/src/identity/hub/{flows/ens → ens}/EnsEditMaintenanceScreens.tsx +10 -10
- package/src/identity/hub/{flows/ens → ens}/EnsEditReviewScreens.tsx +12 -12
- package/src/identity/hub/{flows/ens → ens}/EnsEditRunners.tsx +5 -5
- package/src/identity/hub/{flows/ens → ens}/EnsEditShared.tsx +10 -10
- package/src/identity/hub/{flows/ens → ens}/EnsEditSimpleScreens.tsx +14 -14
- package/src/identity/hub/{flows/ens/IdentityHubEnsFlow.tsx → ens/EnsFlow.tsx} +12 -12
- package/src/identity/hub/{flows/ens/OperatorWalletsScreen.tsx → ens/EnsOperatorWalletsScreen.tsx} +17 -17
- package/src/identity/hub/{advancedEnsValidation.ts → ens/advancedEnsValidation.ts} +2 -2
- package/src/identity/hub/{flows/ens/ensEditCopy.ts → ens/editCopy.ts} +3 -3
- package/src/identity/hub/{effects/ens/flows.ts → ens/effects.ts} +7 -7
- package/src/identity/hub/{effects/ens → ens}/index.ts +1 -1
- package/src/identity/hub/{model/ens.ts → ens/state.ts} +1 -1
- package/src/identity/hub/{effects/ens → ens}/transactions.ts +239 -239
- package/src/identity/hub/{flows/ens/ensEditTypes.ts → ens/types.ts} +7 -7
- package/src/identity/hub/identityHubReducer.ts +3 -3
- package/src/identity/hub/{flows/profile → profile}/EditProfileFlow.tsx +11 -11
- package/src/identity/hub/{effects/publicProfile/runPublicProfileSave.ts → profile/effects.ts} +18 -18
- package/src/identity/hub/{model → profile}/identity.ts +3 -3
- package/src/identity/hub/{effects/profile/profileState.ts → profile/state.ts} +181 -181
- package/src/identity/hub/{flows/restore → restore}/RestoreFlow.tsx +16 -16
- package/src/identity/hub/{effects/restore → restore}/apply.ts +10 -10
- package/src/identity/hub/{effects/restore → restore}/auth.ts +7 -7
- package/src/identity/hub/{effects/restore → restore}/discover.ts +6 -6
- package/src/identity/hub/{effects/restore → restore}/envelopes.ts +2 -2
- package/src/identity/hub/{effects/restore → restore}/fetch.ts +3 -3
- package/src/identity/hub/{effects/restore/shared.ts → restore/helpers.ts} +6 -6
- package/src/identity/hub/{effects/restore → restore}/recovery.ts +10 -10
- package/src/identity/hub/{effects/restore → restore}/resolve.ts +4 -4
- package/src/identity/hub/{effects → restore}/restoreAdmin.ts +1 -1
- package/src/identity/hub/{flows/restore/useRestoreFlowEffects.ts → restore/useRestoreEffects.ts} +5 -5
- package/src/identity/hub/{flows/settings → settings}/StorageCredentialScreen.tsx +5 -5
- package/src/identity/hub/{components → shared/components}/BusyScreen.tsx +4 -4
- package/src/identity/hub/{components → shared/components}/DetailsScreen.tsx +4 -4
- package/src/identity/hub/{components → shared/components}/ErrorScreen.tsx +4 -4
- package/src/identity/hub/{components → shared/components}/FlowTimeline.tsx +1 -1
- package/src/identity/hub/{components → shared/components}/IdentitySummary.tsx +8 -8
- package/src/identity/hub/{components → shared/components}/MenuScreen.tsx +7 -7
- package/src/identity/hub/{components → shared/components}/NetworkScreen.tsx +4 -4
- package/src/identity/hub/{components → shared/components}/PinataJwtInput.tsx +4 -4
- package/src/identity/hub/{components → shared/components}/UnlinkedIdentityScreen.tsx +5 -5
- package/src/identity/hub/{components → shared/components}/WalletApprovalScreen.tsx +6 -6
- package/src/identity/hub/{components → shared/components}/menuFlagsFromReconciliation.ts +1 -1
- package/src/identity/hub/{effects/shared → shared/effects}/profilePrep.ts +1 -1
- package/src/identity/hub/{effects → shared/effects}/receipts.ts +2 -2
- package/src/identity/hub/{effects/shared → shared/effects}/sync.ts +4 -4
- package/src/identity/hub/{effects → shared/effects}/types.ts +3 -3
- package/src/identity/hub/{model → shared/model}/copy.ts +2 -2
- package/src/identity/hub/{model → shared/model}/errors.ts +5 -5
- package/src/identity/hub/{model → shared/model}/network.ts +3 -3
- package/src/identity/hub/{operatorWallets.ts → shared/operatorWallets.ts} +1 -1
- package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/hook.ts +1 -1
- package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/ownership.ts +2 -2
- package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/run.ts +6 -6
- package/src/identity/hub/{utils.ts → shared/utils.ts} +5 -5
- package/src/identity/hub/{flows/token-transfer/IdentityHubTokenTransferFlow.tsx → transfer/TokenTransferFlow.tsx} +8 -8
- package/src/identity/hub/{flows/token-transfer → transfer}/TokenTransferScreens.tsx +14 -14
- package/src/identity/hub/{effects/token-transfer/runTokenTransfer.ts → transfer/effects.ts} +16 -16
- package/src/identity/hub/{effects/token-transfer → transfer}/progress.ts +1 -1
- package/src/identity/hub/useIdentityHubController.ts +11 -11
- package/src/identity/hub/useIdentityHubSideEffects.ts +11 -11
- package/src/models/ModelPicker.tsx +143 -9
- package/src/models/catalog.ts +2 -1
- package/src/models/huggingface.ts +180 -2
- package/src/models/llamacpp.ts +110 -15
- package/src/models/llamacppPreflight.ts +30 -11
- package/src/models/modelPickerOptions.ts +16 -15
- package/src/models/providerDisplay.ts +16 -0
- package/src/providers/anthropic.ts +36 -5
- package/src/providers/contracts.ts +9 -1
- package/src/providers/errors.ts +6 -4
- package/src/providers/gemini.ts +29 -3
- package/src/providers/openai-chat.ts +83 -3
- package/src/providers/openai-responses-format.ts +29 -8
- package/src/providers/openai-responses.ts +22 -7
- package/src/providers/registry.ts +1 -0
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +3 -1
- package/src/runtime/toolExecution.ts +9 -6
- package/src/runtime/turn.ts +29 -0
- package/src/storage/config.ts +1 -0
- package/src/storage/rewind.ts +20 -0
- package/src/storage/sessions.ts +16 -3
- package/src/tools/bashSafety.ts +7 -3
- package/src/tools/bashTool.ts +1 -1
- package/src/tools/contracts.ts +3 -0
- package/src/tools/deleteFileTool.ts +8 -3
- package/src/tools/editTool.ts +10 -5
- package/src/tools/fileDiff.ts +261 -0
- package/src/tools/privateContinuityEditTool.ts +5 -1
- package/src/tools/writeFileTool.ts +8 -3
- package/src/ui/Spinner.tsx +39 -5
- package/src/ui/TextInput.tsx +2 -2
- package/src/ui/theme.ts +19 -0
- package/src/utils/clipboard.ts +10 -7
- package/src/utils/images.ts +140 -0
- package/src/utils/messages.ts +2 -0
- package/src/chat/RewindView.tsx +0 -386
- package/src/chat/toolResultDisplay.ts +0 -8
- package/src/identity/hub/effects/index.ts +0 -73
- package/src/identity/hub/effects/publicProfile/index.ts +0 -5
- package/src/identity/hub/effects/restore/restoreEffects.ts +0 -22
- package/src/identity/hub/effects/token-transfer/index.ts +0 -6
- /package/src/chat/{chatInputState.ts → input/chatInputState.ts} +0 -0
- /package/src/chat/{chatPaste.ts → input/chatPaste.ts} +0 -0
- /package/src/chat/{textCursor.ts → input/textCursor.ts} +0 -0
- /package/src/identity/hub/{model/continuity.ts → continuity/state.ts} +0 -0
- /package/src/identity/hub/{model/custody.ts → custody/state.ts} +0 -0
- /package/src/identity/hub/{effects/restore → restore}/index.ts +0 -0
- /package/src/identity/hub/{model → shared/model}/format.ts +0 -0
- /package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/types.ts +0 -0
- /package/src/identity/hub/{reconciliation → shared/reconciliation}/index.ts +0 -0
- /package/src/identity/hub/{reconciliation → shared/reconciliation}/useAgentReconciliation.ts +0 -0
- /package/src/identity/hub/{reconciliation → shared/reconciliation}/walletSetup.ts +0 -0
- /package/src/identity/hub/{txGuard.ts → shared/txGuard.ts} +0 -0
- /package/src/identity/hub/{model/transfer.ts → transfer/state.ts} +0 -0
|
@@ -2,10 +2,10 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import { Box, Text, useStdout } from 'ink'
|
|
5
|
-
import { theme } from '
|
|
6
|
-
import { readClipboardImage } from '
|
|
7
|
-
import { useAppInput } from '
|
|
8
|
-
import type { SlashSuggestion } from '
|
|
5
|
+
import { theme } from '../../ui/theme.js'
|
|
6
|
+
import { readClipboardImage } from '../../utils/clipboard.js'
|
|
7
|
+
import { useAppInput } from '../../app/input/AppInputProvider.js'
|
|
8
|
+
import type { SlashSuggestion } from '../commands.js'
|
|
9
9
|
import {
|
|
10
10
|
beginHistoryPreview,
|
|
11
11
|
canNavigateHistory as canNavigateHistoryState,
|
|
@@ -33,6 +33,12 @@ import {
|
|
|
33
33
|
shouldCollapsePastedText,
|
|
34
34
|
type PastedTextRef,
|
|
35
35
|
} from './chatPaste.js'
|
|
36
|
+
import {
|
|
37
|
+
expandImageRefs,
|
|
38
|
+
formatImageRefMarker,
|
|
39
|
+
pruneImageRefs,
|
|
40
|
+
type ImageRef,
|
|
41
|
+
} from './imageRefs.js'
|
|
36
42
|
|
|
37
43
|
type PromptInputProps = {
|
|
38
44
|
onSubmit: (value: string) => void
|
|
@@ -46,6 +52,9 @@ type PromptInputProps = {
|
|
|
46
52
|
onModeChange?: (mode: 'prompt' | 'bash') => void
|
|
47
53
|
footerRight?: React.ReactNode
|
|
48
54
|
cwd?: string
|
|
55
|
+
seedText?: string | null
|
|
56
|
+
onSeedConsumed?: () => void
|
|
57
|
+
onImagePaste?: (path: string) => void
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
const MAX_LENGTH = 32_768
|
|
@@ -66,12 +75,15 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
66
75
|
disabled,
|
|
67
76
|
placeholderHints,
|
|
68
77
|
queuedMessages = [],
|
|
69
|
-
prefix = '
|
|
78
|
+
prefix = '›',
|
|
70
79
|
slashSuggestions = [],
|
|
71
80
|
mode = 'prompt',
|
|
72
81
|
onModeChange,
|
|
73
82
|
footerRight,
|
|
74
83
|
cwd,
|
|
84
|
+
seedText,
|
|
85
|
+
onSeedConsumed,
|
|
86
|
+
onImagePaste,
|
|
75
87
|
}) => {
|
|
76
88
|
const { stdout } = useStdout()
|
|
77
89
|
const [buffer, setBuffer] = useState<ChatBuffer>(emptyBuffer)
|
|
@@ -96,6 +108,8 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
96
108
|
const pasteTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
97
109
|
const pastedTextRefsRef = useRef<Map<number, PastedTextRef>>(new Map())
|
|
98
110
|
const nextPastedTextRefIdRef = useRef(1)
|
|
111
|
+
const imageRefsRef = useRef<Map<number, ImageRef>>(new Map())
|
|
112
|
+
const nextImageRefIdRef = useRef(1)
|
|
99
113
|
|
|
100
114
|
useEffect(() => { bufferRef.current = buffer }, [buffer])
|
|
101
115
|
useEffect(() => { historyIndexRef.current = historyIndex }, [historyIndex])
|
|
@@ -103,6 +117,11 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
103
117
|
useEffect(() => { historyPreviewActiveRef.current = historyPreviewActive }, [historyPreviewActive])
|
|
104
118
|
useEffect(() => { preferredColumnRef.current = preferredColumn }, [preferredColumn])
|
|
105
119
|
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
pruneImageRefs(imageRefsRef.current, value)
|
|
122
|
+
if (imageRefsRef.current.size === 0) nextImageRefIdRef.current = 1
|
|
123
|
+
}, [value])
|
|
124
|
+
|
|
106
125
|
useEffect(() => {
|
|
107
126
|
const handleResize = () => {
|
|
108
127
|
setColumns(stdout.columns ?? process.stdout.columns ?? 80)
|
|
@@ -141,6 +160,15 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
141
160
|
applyHistoryState(exitHistoryPreview(next))
|
|
142
161
|
}, [applyHistoryState])
|
|
143
162
|
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (seedText == null) return
|
|
165
|
+
const next: ChatBuffer = { value: seedText, cursor: seedText.length }
|
|
166
|
+
bufferRef.current = next
|
|
167
|
+
setBuffer(next)
|
|
168
|
+
applyHistoryState(exitHistoryPreview(next))
|
|
169
|
+
onSeedConsumed?.()
|
|
170
|
+
}, [seedText, applyHistoryState, onSeedConsumed])
|
|
171
|
+
|
|
144
172
|
useEffect(() => {
|
|
145
173
|
if (!placeholderHints || placeholderHints.length < 2) return
|
|
146
174
|
const timer = setInterval(() => {
|
|
@@ -204,6 +232,8 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
204
232
|
})
|
|
205
233
|
pastedTextRefsRef.current.clear()
|
|
206
234
|
nextPastedTextRefIdRef.current = 1
|
|
235
|
+
imageRefsRef.current.clear()
|
|
236
|
+
nextImageRefIdRef.current = 1
|
|
207
237
|
}, [applyBuffer, applyHistoryState])
|
|
208
238
|
|
|
209
239
|
const handlePaste = useCallback((text: string) => {
|
|
@@ -244,7 +274,8 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
244
274
|
const submit = useCallback(() => {
|
|
245
275
|
const trimmed = value.trim()
|
|
246
276
|
if (!trimmed) return
|
|
247
|
-
|
|
277
|
+
const withText = expandPastedTextRefs(trimmed, pastedTextRefsRef.current)
|
|
278
|
+
onSubmit(expandImageRefs(withText, imageRefsRef.current))
|
|
248
279
|
resetBuffer()
|
|
249
280
|
}, [value, onSubmit, resetBuffer])
|
|
250
281
|
|
|
@@ -375,7 +406,12 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
375
406
|
if (key.meta && inputText === 'v') {
|
|
376
407
|
void (async () => {
|
|
377
408
|
const image = await readClipboardImage()
|
|
378
|
-
if (image.ok)
|
|
409
|
+
if (image.ok) {
|
|
410
|
+
const id = nextImageRefIdRef.current++
|
|
411
|
+
imageRefsRef.current.set(id, { path: image.path })
|
|
412
|
+
insertText(formatImageRefMarker(id))
|
|
413
|
+
onImagePaste?.(image.path)
|
|
414
|
+
}
|
|
379
415
|
})()
|
|
380
416
|
return
|
|
381
417
|
}
|
|
@@ -506,6 +542,22 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
506
542
|
|
|
507
543
|
return (
|
|
508
544
|
<Box flexDirection="column" width="100%">
|
|
545
|
+
{queuedMessages.length > 0 ? (
|
|
546
|
+
<Box marginLeft={2} marginBottom={1} flexDirection="column">
|
|
547
|
+
<Text color={theme.dim}>
|
|
548
|
+
{queuedMessages.length === 1 ? '1 message queued for next turn' : `${queuedMessages.length} messages queued for next turns`}
|
|
549
|
+
</Text>
|
|
550
|
+
{queuedMessages.slice(0, 3).map((message, i) => (
|
|
551
|
+
<Text key={`${i}-${message.slice(0, 24)}`}>
|
|
552
|
+
<Text color={theme.accentPeriwinkle}>{i === 0 ? '» ' : ' '}</Text>
|
|
553
|
+
<Text color={theme.textSubtle}>{summarizeQueuedMessage(message)}</Text>
|
|
554
|
+
</Text>
|
|
555
|
+
))}
|
|
556
|
+
{queuedMessages.length > 3 ? (
|
|
557
|
+
<Text color={theme.dim}>+{queuedMessages.length - 3} more</Text>
|
|
558
|
+
) : null}
|
|
559
|
+
</Box>
|
|
560
|
+
) : null}
|
|
509
561
|
<Box
|
|
510
562
|
borderStyle="round"
|
|
511
563
|
borderColor={borderColor}
|
|
@@ -545,7 +597,7 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
545
597
|
<Box marginLeft={2} flexDirection="column">
|
|
546
598
|
{filteredSuggestions.map((s, i) => (
|
|
547
599
|
<Text key={s.name} color={i === suggestionIdx ? theme.accentPeriwinkle : theme.dim}>
|
|
548
|
-
{i === suggestionIdx ? '
|
|
600
|
+
{i === suggestionIdx ? '› ' : ' '}/{s.name}
|
|
549
601
|
<Text color={theme.dim}> {s.summary}{i === suggestionIdx ? (s.executeOnEnter ? ' · enter runs' : ' · enter fills') : ''}</Text>
|
|
550
602
|
</Text>
|
|
551
603
|
))}
|
|
@@ -555,7 +607,7 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
555
607
|
<Box marginLeft={2} flexDirection="column">
|
|
556
608
|
{fileSuggestions.slice(0, 8).map((s, i) => (
|
|
557
609
|
<Text key={s.path} color={i === fileSuggestionIdx ? theme.accentPeriwinkle : theme.dim}>
|
|
558
|
-
{i === fileSuggestionIdx ? '
|
|
610
|
+
{i === fileSuggestionIdx ? '› ' : ' '}@{s.path}
|
|
559
611
|
<Text color={theme.dim}> {i === fileSuggestionIdx ? 'tab/enter completes' : s.hint}</Text>
|
|
560
612
|
</Text>
|
|
561
613
|
))}
|
|
@@ -564,22 +616,6 @@ export const ChatInput: React.FC<PromptInputProps> = ({
|
|
|
564
616
|
) : null}
|
|
565
617
|
</Box>
|
|
566
618
|
) : null}
|
|
567
|
-
{queuedMessages.length > 0 ? (
|
|
568
|
-
<Box marginLeft={2} flexDirection="column">
|
|
569
|
-
<Text color={theme.dim}>
|
|
570
|
-
{queuedMessages.length === 1 ? '1 message queued for next turn' : `${queuedMessages.length} messages queued for next turns`}
|
|
571
|
-
</Text>
|
|
572
|
-
{queuedMessages.slice(0, 3).map((message, i) => (
|
|
573
|
-
<Text key={`${i}-${message.slice(0, 24)}`}>
|
|
574
|
-
<Text color={theme.accentPeriwinkle}>{i === 0 ? '» ' : ' '}</Text>
|
|
575
|
-
<Text color={theme.textSubtle}>{summarizeQueuedMessage(message)}</Text>
|
|
576
|
-
</Text>
|
|
577
|
-
))}
|
|
578
|
-
{queuedMessages.length > 3 ? (
|
|
579
|
-
<Text color={theme.dim}>+{queuedMessages.length - 3} more</Text>
|
|
580
|
-
) : null}
|
|
581
|
-
</Box>
|
|
582
|
-
) : null}
|
|
583
619
|
{footerRight ? (
|
|
584
620
|
<Box marginLeft={2}>
|
|
585
621
|
{footerRight}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type ImageRef = { path: string; mimeType?: string }
|
|
2
|
+
|
|
3
|
+
const IMAGE_REF_MARKER_RE = /\[Image\s+#(\d+)\]/g
|
|
4
|
+
|
|
5
|
+
export function expandImageRefs(text: string, refs: Map<number, ImageRef>): string {
|
|
6
|
+
return text.replace(IMAGE_REF_MARKER_RE, (full, raw: string) => {
|
|
7
|
+
const ref = refs.get(Number(raw))
|
|
8
|
+
return ref ? `[image: ${ref.path}]` : full
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function referencedImageIds(text: string): Set<number> {
|
|
13
|
+
const out = new Set<number>()
|
|
14
|
+
for (const match of text.matchAll(IMAGE_REF_MARKER_RE)) {
|
|
15
|
+
const id = Number(match[1])
|
|
16
|
+
if (Number.isFinite(id)) out.add(id)
|
|
17
|
+
}
|
|
18
|
+
return out
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function pruneImageRefs(refs: Map<number, ImageRef>, text: string): void {
|
|
22
|
+
const referenced = referencedImageIds(text)
|
|
23
|
+
for (const id of [...refs.keys()]) {
|
|
24
|
+
if (!referenced.has(id)) refs.delete(id)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function formatImageRefMarker(id: number): string {
|
|
29
|
+
return `[Image #${id}]`
|
|
30
|
+
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import { Box, Text, useStdout } from 'ink'
|
|
3
|
-
import { useAppInput } from '
|
|
4
|
-
import { MessageList, type MessageRow } from '
|
|
5
|
-
import { theme } from '
|
|
3
|
+
import { useAppInput } from '../../app/input/AppInputProvider.js'
|
|
4
|
+
import { MessageList, type MessageRow } from '../MessageList.js'
|
|
5
|
+
import { theme } from '../../ui/theme.js'
|
|
6
6
|
import {
|
|
7
7
|
anchorForScrollTop,
|
|
8
8
|
buildLineOffsets,
|
|
9
9
|
clampLine,
|
|
10
10
|
estimateMessageRowHeight,
|
|
11
|
-
promptScrollTopForPageDown,
|
|
12
|
-
promptScrollTopForPageUp,
|
|
13
11
|
resolveScrollTopFromAnchor,
|
|
12
|
+
scrollTopForPageDown,
|
|
13
|
+
scrollTopForPageUp,
|
|
14
14
|
selectRowsForScrollTop,
|
|
15
|
-
type TranscriptWindowSelection,
|
|
16
15
|
type TranscriptViewportState,
|
|
17
16
|
} from './transcriptViewport.js'
|
|
18
17
|
|
|
@@ -24,10 +23,10 @@ type TranscriptViewProps = {
|
|
|
24
23
|
onScrollabilityChange?: (canScroll: boolean) => void
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
const PROMPT_RESERVED_LINES =
|
|
28
|
-
const OVERLAY_RESERVED_LINES =
|
|
26
|
+
const PROMPT_RESERVED_LINES = 7
|
|
27
|
+
const OVERLAY_RESERVED_LINES = 12
|
|
29
28
|
const MIN_TRANSCRIPT_LINES = 6
|
|
30
|
-
const MAX_TRANSCRIPT_LINES =
|
|
29
|
+
const MAX_TRANSCRIPT_LINES = 240
|
|
31
30
|
|
|
32
31
|
export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
33
32
|
rows,
|
|
@@ -64,15 +63,11 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
64
63
|
[metrics, viewportState],
|
|
65
64
|
)
|
|
66
65
|
const selection = useMemo(
|
|
67
|
-
() =>
|
|
68
|
-
selectRowsForScrollTop(
|
|
69
|
-
rows,
|
|
70
|
-
maxLines,
|
|
71
|
-
resolvedViewportState.scrollTopLine,
|
|
72
|
-
row => estimateMessageRowHeight(row, columns),
|
|
73
|
-
),
|
|
66
|
+
() => selectRowsForScrollTop(
|
|
74
67
|
rows,
|
|
75
|
-
|
|
68
|
+
maxLines,
|
|
69
|
+
resolvedViewportState.scrollTopLine,
|
|
70
|
+
row => estimateMessageRowHeight(row, columns),
|
|
76
71
|
),
|
|
77
72
|
[columns, maxLines, resolvedViewportState, rows],
|
|
78
73
|
)
|
|
@@ -97,12 +92,10 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
97
92
|
|
|
98
93
|
useAppInput((_input, key) => {
|
|
99
94
|
if (key.pageUp) {
|
|
100
|
-
const target =
|
|
101
|
-
rows,
|
|
102
|
-
metrics.offsets,
|
|
95
|
+
const target = scrollTopForPageUp(
|
|
103
96
|
resolvedViewportState.scrollTopLine,
|
|
104
97
|
metrics.maxScrollTop,
|
|
105
|
-
|
|
98
|
+
maxLines,
|
|
106
99
|
)
|
|
107
100
|
setViewportState(viewportForScrollTop(
|
|
108
101
|
target,
|
|
@@ -111,11 +104,10 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
111
104
|
metrics.maxScrollTop,
|
|
112
105
|
))
|
|
113
106
|
} else if (key.pageDown) {
|
|
114
|
-
const target =
|
|
115
|
-
rows,
|
|
116
|
-
metrics.offsets,
|
|
107
|
+
const target = scrollTopForPageDown(
|
|
117
108
|
resolvedViewportState.scrollTopLine,
|
|
118
109
|
metrics.maxScrollTop,
|
|
110
|
+
maxLines,
|
|
119
111
|
)
|
|
120
112
|
setViewportState(viewportForScrollTop(
|
|
121
113
|
target,
|
|
@@ -128,18 +120,21 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
128
120
|
|
|
129
121
|
return (
|
|
130
122
|
<Box flexDirection="column">
|
|
131
|
-
<Box marginBottom={1}>
|
|
132
|
-
<Text> </Text>
|
|
133
|
-
</Box>
|
|
134
123
|
{selection.hiddenBefore > 0 ? (
|
|
135
124
|
<Text color={theme.dim}>
|
|
136
|
-
{` ${selection.hiddenBefore} earlier message${selection.hiddenBefore === 1 ? '' : 's'} above
|
|
125
|
+
{` ${selection.hiddenBefore} earlier message${selection.hiddenBefore === 1 ? '' : 's'} above · `}
|
|
126
|
+
<Text color={theme.accentPeriwinkle}>pgup</Text>
|
|
127
|
+
{` to scroll · `}
|
|
128
|
+
<Text color={theme.accentPeriwinkle}>/export</Text>
|
|
129
|
+
{` saves the full transcript`}
|
|
137
130
|
</Text>
|
|
138
131
|
) : null}
|
|
139
132
|
<MessageList rows={selection.rows} />
|
|
140
133
|
{selection.hiddenAfter > 0 ? (
|
|
141
134
|
<Text color={theme.dim}>
|
|
142
|
-
{` ${selection.hiddenAfter} later message${selection.hiddenAfter === 1 ? '' : 's'} below ·
|
|
135
|
+
{` ${selection.hiddenAfter} later message${selection.hiddenAfter === 1 ? '' : 's'} below · `}
|
|
136
|
+
<Text color={theme.accentPeriwinkle}>pgdn</Text>
|
|
137
|
+
{` to return`}
|
|
143
138
|
</Text>
|
|
144
139
|
) : null}
|
|
145
140
|
</Box>
|
|
@@ -179,27 +174,6 @@ function viewportForScrollTop(
|
|
|
179
174
|
}
|
|
180
175
|
}
|
|
181
176
|
|
|
182
|
-
function trimSelectionToFocusedTurn(
|
|
183
|
-
selection: TranscriptWindowSelection<MessageRow>,
|
|
184
|
-
rows: MessageRow[],
|
|
185
|
-
state: TranscriptViewportState,
|
|
186
|
-
): TranscriptWindowSelection<MessageRow> {
|
|
187
|
-
if (state.followTail || state.anchor?.offset !== 0) return selection
|
|
188
|
-
const focusedIndex = rows.findIndex(row => row.id === state.anchor?.rowId)
|
|
189
|
-
if (focusedIndex === -1 || rows[focusedIndex]?.role !== 'user') return selection
|
|
190
|
-
const firstSelected = selection.rows[0]
|
|
191
|
-
if (!firstSelected || firstSelected.id !== state.anchor.rowId) return selection
|
|
192
|
-
|
|
193
|
-
const nextPromptIndex = selection.rows.findIndex((row, index) => index > 0 && row.role === 'user')
|
|
194
|
-
if (nextPromptIndex === -1) return selection
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
...selection,
|
|
198
|
-
rows: selection.rows.slice(0, nextPromptIndex),
|
|
199
|
-
hiddenAfter: selection.hiddenAfter + selection.rows.length - nextPromptIndex,
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
177
|
function sameViewportState(left: TranscriptViewportState, right: TranscriptViewportState): boolean {
|
|
204
178
|
return left.scrollTopLine === right.scrollTopLine
|
|
205
179
|
&& left.followTail === right.followTail
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MessageRow } from '
|
|
1
|
+
import type { MessageRow } from '../MessageList.js'
|
|
2
2
|
|
|
3
3
|
export type TranscriptAnchor = {
|
|
4
4
|
rowId: string
|
|
@@ -141,45 +141,25 @@ export function selectRowsForScrollTop<T>(
|
|
|
141
141
|
return selectRowsForLineWindow(rows, offsets, budget, startLine, totalLines, maxScrollOffset)
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
export function
|
|
145
|
-
rows: MessageRow[],
|
|
146
|
-
offsets: number[],
|
|
144
|
+
export function scrollTopForPageUp(
|
|
147
145
|
scrollTopLine: number,
|
|
148
146
|
maxScrollTop: number,
|
|
149
|
-
|
|
147
|
+
viewportLines: number,
|
|
150
148
|
): number {
|
|
151
|
-
|
|
152
|
-
if (promptStarts.length === 0) return clampLine(scrollTopLine, maxScrollTop)
|
|
153
|
-
if (followTail) return clampLine(promptStarts[promptStarts.length - 1]!, maxScrollTop)
|
|
154
|
-
|
|
155
|
-
const currentLine = clampLine(scrollTopLine, maxScrollTop)
|
|
156
|
-
for (let index = promptStarts.length - 1; index >= 0; index -= 1) {
|
|
157
|
-
const line = promptStarts[index]!
|
|
158
|
-
if (line < currentLine) return clampLine(line, maxScrollTop)
|
|
159
|
-
}
|
|
160
|
-
return 0
|
|
149
|
+
return clampLine(scrollTopLine - pageScrollDistance(viewportLines), maxScrollTop)
|
|
161
150
|
}
|
|
162
151
|
|
|
163
|
-
export function
|
|
164
|
-
rows: MessageRow[],
|
|
165
|
-
offsets: number[],
|
|
152
|
+
export function scrollTopForPageDown(
|
|
166
153
|
scrollTopLine: number,
|
|
167
154
|
maxScrollTop: number,
|
|
155
|
+
viewportLines: number,
|
|
168
156
|
): number {
|
|
169
|
-
|
|
170
|
-
if (promptStarts.length === 0) return clampLine(scrollTopLine, maxScrollTop)
|
|
171
|
-
|
|
172
|
-
const currentLine = clampLine(scrollTopLine, maxScrollTop)
|
|
173
|
-
const next = promptStarts.find(line => line > currentLine)
|
|
174
|
-
return next === undefined ? maxScrollTop : clampLine(next, maxScrollTop)
|
|
157
|
+
return clampLine(scrollTopLine + pageScrollDistance(viewportLines), maxScrollTop)
|
|
175
158
|
}
|
|
176
159
|
|
|
177
|
-
function
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
if (rows[index]?.role === 'user') starts.push(offsets[index] ?? 0)
|
|
181
|
-
}
|
|
182
|
-
return starts
|
|
160
|
+
function pageScrollDistance(viewportLines: number): number {
|
|
161
|
+
const viewport = Math.max(1, Math.floor(viewportLines))
|
|
162
|
+
return Math.max(1, Math.floor(viewport / 2))
|
|
183
163
|
}
|
|
184
164
|
|
|
185
165
|
function selectRowsForLineWindow<T>(
|
|
@@ -224,6 +204,8 @@ export function estimateMessageRowHeight(row: MessageRow, columns = 80): number
|
|
|
224
204
|
return 1 + wrappedLineCount(row.content, contentWidth)
|
|
225
205
|
case 'progress':
|
|
226
206
|
return 4
|
|
207
|
+
case 'splash':
|
|
208
|
+
return 28
|
|
227
209
|
}
|
|
228
210
|
}
|
|
229
211
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import type { ContextUsage } from '
|
|
4
|
-
import { useAppInput } from '
|
|
5
|
-
import { theme } from '
|
|
3
|
+
import type { ContextUsage } from '../../runtime/compaction.js'
|
|
4
|
+
import { useAppInput } from '../../app/input/AppInputProvider.js'
|
|
5
|
+
import { theme } from '../../ui/theme.js'
|
|
6
6
|
|
|
7
7
|
export type ContextLimitAction = 'compact' | 'switchModel' | 'send' | 'cancel'
|
|
8
8
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { Surface } from '
|
|
4
|
-
import { Select } from '
|
|
5
|
-
import { theme } from '
|
|
3
|
+
import { Surface } from '../../ui/Surface.js'
|
|
4
|
+
import { Select } from '../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../ui/theme.js'
|
|
6
|
+
import { DiffView } from '../display/DiffView.js'
|
|
6
7
|
|
|
7
8
|
export type ContinuityEditReviewState = {
|
|
8
9
|
file: 'SOUL.md' | 'MEMORY.md'
|
|
9
10
|
filePath: string
|
|
10
11
|
summary: string
|
|
12
|
+
diff?: string
|
|
11
13
|
editorOpened?: boolean
|
|
12
14
|
}
|
|
13
15
|
|
|
@@ -23,6 +25,12 @@ export const ContinuityEditReviewView: React.FC<{
|
|
|
23
25
|
footer="enter select · esc dismiss"
|
|
24
26
|
>
|
|
25
27
|
<Text color={theme.accentPeriwinkle}>{displayContinuityReviewText(review.summary)}</Text>
|
|
28
|
+
{review.diff ? (
|
|
29
|
+
<Box flexDirection="column" marginTop={1}>
|
|
30
|
+
<Text color={theme.accentPeriwinkle}>diff</Text>
|
|
31
|
+
<DiffView diff={review.diff} />
|
|
32
|
+
</Box>
|
|
33
|
+
) : null}
|
|
26
34
|
<Box marginTop={1}>
|
|
27
35
|
<Select<ContinuityEditReviewAction>
|
|
28
36
|
options={[
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { Surface } from '
|
|
3
|
-
import { Select } from '
|
|
4
|
-
import { parseSegments, type Segment } from '
|
|
5
|
-
import { copyToClipboard, type CopyResult } from '
|
|
2
|
+
import { Surface } from '../../ui/Surface.js'
|
|
3
|
+
import { Select } from '../../ui/Select.js'
|
|
4
|
+
import { parseSegments, type Segment } from '../../utils/markdownSegments.js'
|
|
5
|
+
import { copyToClipboard, type CopyResult } from '../../utils/clipboard.js'
|
|
6
6
|
|
|
7
7
|
type CopyPickerProps = {
|
|
8
8
|
turnText: string
|
|
@@ -48,4 +48,3 @@ export const CopyPicker: React.FC<CopyPickerProps> = ({ turnText, turnLabel, onD
|
|
|
48
48
|
</Surface>
|
|
49
49
|
)
|
|
50
50
|
}
|
|
51
|
-
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React, { useMemo } from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { Surface } from '
|
|
4
|
-
import { Select } from '
|
|
5
|
-
import { theme } from '
|
|
6
|
-
import
|
|
3
|
+
import { Surface } from '../../ui/Surface.js'
|
|
4
|
+
import { Select } from '../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../ui/theme.js'
|
|
6
|
+
import { DiffView, diffLineColor } from '../display/DiffView.js'
|
|
7
|
+
import type { PermissionDecision, PermissionRequest } from '../../tools/contracts.js'
|
|
8
|
+
|
|
9
|
+
export { diffLineColor as permissionDiffLineColor }
|
|
7
10
|
|
|
8
11
|
type PermissionPromptProps = {
|
|
9
12
|
request: PermissionRequest
|
|
@@ -34,7 +37,7 @@ export const PermissionPrompt: React.FC<PermissionPromptProps> = ({ request, onD
|
|
|
34
37
|
<Box marginTop={1}>
|
|
35
38
|
<Text color={theme.accentPeriwinkle}>diff</Text>
|
|
36
39
|
</Box>
|
|
37
|
-
<
|
|
40
|
+
<DiffView diff={request.diff} />
|
|
38
41
|
</Box>
|
|
39
42
|
) : null}
|
|
40
43
|
{request.kind === 'private-continuity-read' ? (
|
|
@@ -51,13 +54,9 @@ export const PermissionPrompt: React.FC<PermissionPromptProps> = ({ request, onD
|
|
|
51
54
|
<Box flexDirection="column" marginBottom={1}>
|
|
52
55
|
<Text color={theme.accentPeriwinkle}>{displayPermissionText(request.changeSummary)}</Text>
|
|
53
56
|
<Box marginTop={1}>
|
|
54
|
-
<Text color={theme.
|
|
55
|
-
</Box>
|
|
56
|
-
<Text color={theme.textSubtle}>{request.before || '(empty)'}</Text>
|
|
57
|
-
<Box marginTop={1}>
|
|
58
|
-
<Text color={theme.accentPeriwinkle}>after</Text>
|
|
57
|
+
<Text color={theme.accentPeriwinkle}>diff</Text>
|
|
59
58
|
</Box>
|
|
60
|
-
<
|
|
59
|
+
<DiffView diff={request.diff} />
|
|
61
60
|
</Box>
|
|
62
61
|
) : null}
|
|
63
62
|
{request.kind === 'bash' && request.warning ? (
|
|
@@ -73,20 +72,20 @@ export const PermissionPrompt: React.FC<PermissionPromptProps> = ({ request, onD
|
|
|
73
72
|
export function permissionOptionsForRequest(request: PermissionRequest): Array<{ value: PermissionDecision; label: string; hint?: string; disabled?: boolean }> {
|
|
74
73
|
if (request.kind === 'bash') {
|
|
75
74
|
return [
|
|
76
|
-
{ value: 'allow-once', label: '
|
|
75
|
+
{ value: 'allow-once', label: 'Allow Once', hint: 'Approve only this command execution' },
|
|
77
76
|
{
|
|
78
77
|
value: 'allow-command-project',
|
|
79
|
-
label: '
|
|
80
|
-
hint: '
|
|
78
|
+
label: 'Allow Exact Command',
|
|
79
|
+
hint: 'Remember this exact command for this project',
|
|
81
80
|
disabled: !request.canPersistExact,
|
|
82
81
|
},
|
|
83
82
|
{
|
|
84
83
|
value: 'allow-command-prefix-project',
|
|
85
|
-
label: request.commandPrefix ? `
|
|
86
|
-
hint: '
|
|
84
|
+
label: request.commandPrefix ? `Allow ${request.commandPrefix} Commands` : 'Allow Command Family',
|
|
85
|
+
hint: 'Remember this base command in this working directory for this project',
|
|
87
86
|
disabled: !request.canPersistPrefix,
|
|
88
87
|
},
|
|
89
|
-
{ value: 'deny', label: '
|
|
88
|
+
{ value: 'deny', label: 'Deny', hint: 'Return a denial to the model' },
|
|
90
89
|
]
|
|
91
90
|
}
|
|
92
91
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { clearPermissionRules, deletePermissionRule, loadPermissionRules } from '
|
|
4
|
-
import type { SessionPermissionRule } from '
|
|
5
|
-
import { Select, type SelectOption } from '
|
|
6
|
-
import { Spinner } from '
|
|
7
|
-
import { Surface } from '
|
|
8
|
-
import { theme } from '
|
|
3
|
+
import { clearPermissionRules, deletePermissionRule, loadPermissionRules } from '../../storage/permissions.js'
|
|
4
|
+
import type { SessionPermissionRule } from '../../tools/contracts.js'
|
|
5
|
+
import { Select, type SelectOption } from '../../ui/Select.js'
|
|
6
|
+
import { Spinner } from '../../ui/Spinner.js'
|
|
7
|
+
import { Surface } from '../../ui/Surface.js'
|
|
8
|
+
import { theme } from '../../ui/theme.js'
|
|
9
9
|
|
|
10
10
|
type PermissionsViewProps = {
|
|
11
11
|
cwd: string
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { Surface } from '
|
|
4
|
-
import { theme } from '
|
|
5
|
-
import { useAppInput } from '
|
|
3
|
+
import { Surface } from '../../ui/Surface.js'
|
|
4
|
+
import { theme } from '../../ui/theme.js'
|
|
5
|
+
import { useAppInput } from '../../app/input/AppInputProvider.js'
|
|
6
6
|
|
|
7
7
|
export type PlanApprovalAction = 'apply' | 'apply-summary' | 'continue'
|
|
8
8
|
|
|
@@ -32,7 +32,7 @@ export const PLAN_APPROVAL_OPTIONS: Array<{
|
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
value: 'continue',
|
|
35
|
-
label: 'No, stay in
|
|
35
|
+
label: 'No, stay in plan mode',
|
|
36
36
|
title: 'Continue planning with the model.',
|
|
37
37
|
detail: () => 'No files will be changed.',
|
|
38
38
|
},
|