ethagent 1.1.1 → 2.0.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/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
package/src/chat/ChatScreen.tsx
CHANGED
|
@@ -18,7 +18,7 @@ import { SessionStatus, formatTokens } from './SessionStatus.js'
|
|
|
18
18
|
import { formatModelDisplayName } from '../models/modelDisplay.js'
|
|
19
19
|
import { toggleReasoningRow, type MessageRow } from './MessageList.js'
|
|
20
20
|
import { ConversationStack } from './ConversationStack.js'
|
|
21
|
-
import {
|
|
21
|
+
import type { ModelPickerSelection } from '../models/ModelPicker.js'
|
|
22
22
|
import type { ModelPickerContextFit } from '../models/modelPickerOptions.js'
|
|
23
23
|
import type { CopyResult } from '../utils/clipboard.js'
|
|
24
24
|
import { useKeybinding, useRegisterKeybindingContext } from '../app/keybindings/KeybindingProvider.js'
|
|
@@ -55,13 +55,15 @@ import type {
|
|
|
55
55
|
} from '../tools/contracts.js'
|
|
56
56
|
import {
|
|
57
57
|
buildBaseMessages,
|
|
58
|
-
formatBytes,
|
|
59
58
|
sessionMessagesToRows,
|
|
60
59
|
type TurnCheckpoint,
|
|
61
60
|
} from './chatScreenUtils.js'
|
|
62
61
|
import { ChatBottomPane, type ContextLimitState, type CopyPickerState, type IdentityOverlayState, type Overlay } from './ChatBottomPane.js'
|
|
63
62
|
import { setTokenIdentity, getIdentityStatus } from '../storage/identity.js'
|
|
64
63
|
import type { IdentityHubResult } from '../identity/hub/IdentityHub.js'
|
|
64
|
+
import { continuityWorkingTreeStatus } from '../identity/continuity/storage.js'
|
|
65
|
+
import { listPublishedContinuitySnapshots } from '../identity/continuity/snapshots.js'
|
|
66
|
+
import { localChangeStatusView } from '../identity/hub/model/continuity.js'
|
|
65
67
|
import {
|
|
66
68
|
buildResumedSessionState,
|
|
67
69
|
promptHistoryFromSessionMessages,
|
|
@@ -143,6 +145,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
143
145
|
const [mode, setMode] = useState<SessionMode>('chat')
|
|
144
146
|
const [pendingPlan, setPendingPlan] = useState<PendingPlan | null>(null)
|
|
145
147
|
const [compactionUi, setCompactionUi] = useState<CompactionUiState | null>(null)
|
|
148
|
+
const [canScrollTranscript, setCanScrollTranscript] = useState(false)
|
|
146
149
|
const [sessionId, setSessionId] = useState<string>(() => newSessionId())
|
|
147
150
|
const [sessionKey, setSessionKey] = useState<number>(0)
|
|
148
151
|
const [cwd, setCwd] = useState<string>(() => syncCwdFromProcess())
|
|
@@ -180,6 +183,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
180
183
|
const pendingContinuityEditReviewRef = useRef<ContinuityEditReviewState | null>(null)
|
|
181
184
|
const contextModelSwitchPromptRef = useRef<string | null>(null)
|
|
182
185
|
const mcpManagerRef = useRef<McpManager | null>(null)
|
|
186
|
+
const savePromptShownRef = useRef<boolean>(false)
|
|
183
187
|
|
|
184
188
|
useEffect(() => { rowsRef.current = rows }, [rows])
|
|
185
189
|
useEffect(() => { overlayRef.current = overlay }, [overlay])
|
|
@@ -205,6 +209,29 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
205
209
|
})()
|
|
206
210
|
}, [])
|
|
207
211
|
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (savePromptShownRef.current) return
|
|
214
|
+
savePromptShownRef.current = true
|
|
215
|
+
void (async () => {
|
|
216
|
+
try {
|
|
217
|
+
const identity = configRef.current.identity
|
|
218
|
+
if (!identity) return
|
|
219
|
+
const [latest] = await listPublishedContinuitySnapshots(identity, 1)
|
|
220
|
+
const status = await continuityWorkingTreeStatus(identity, latest)
|
|
221
|
+
if (!localChangeStatusView(status).hasLocalChanges) return
|
|
222
|
+
if (overlayRef.current !== 'none') return
|
|
223
|
+
setIdentityOverlay({
|
|
224
|
+
initialAction: 'save-prompt',
|
|
225
|
+
existing: { address: identity.address },
|
|
226
|
+
})
|
|
227
|
+
overlayRef.current = 'identity'
|
|
228
|
+
setOverlay('identity')
|
|
229
|
+
} catch {
|
|
230
|
+
// best-effort; skip prompt on any error
|
|
231
|
+
}
|
|
232
|
+
})()
|
|
233
|
+
}, [])
|
|
234
|
+
|
|
208
235
|
useEffect(() => {
|
|
209
236
|
void (async () => {
|
|
210
237
|
try {
|
|
@@ -1094,6 +1121,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1094
1121
|
: `open failed: ${result.error}`,
|
|
1095
1122
|
result.ok ? 'dim' : 'error',
|
|
1096
1123
|
)
|
|
1124
|
+
if (result.ok) setContinuityEditReview(prev => prev ? { ...prev, editorOpened: true } : null)
|
|
1097
1125
|
return
|
|
1098
1126
|
}
|
|
1099
1127
|
setContinuityEditReview(null)
|
|
@@ -1105,7 +1133,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1105
1133
|
})
|
|
1106
1134
|
overlayRef.current = 'identity'
|
|
1107
1135
|
setOverlay('identity')
|
|
1108
|
-
pushNote('opening snapshot
|
|
1136
|
+
pushNote('opening snapshot signature.', 'dim')
|
|
1109
1137
|
return
|
|
1110
1138
|
}
|
|
1111
1139
|
overlayRef.current = 'none'
|
|
@@ -1361,8 +1389,8 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1361
1389
|
|
|
1362
1390
|
const contextLine = `${config.provider} · ${formatModelDisplayName(config.provider, config.model, { maxLength: 24 })} · ${compressHome(cwd)}`
|
|
1363
1391
|
const tipLine = streaming
|
|
1364
|
-
? '
|
|
1365
|
-
: '
|
|
1392
|
+
? 'Tip: You can keep typing and press enter to queue the next message · shift+enter for newline'
|
|
1393
|
+
: 'Tip: type /help to get started · shift+enter for newline'
|
|
1366
1394
|
|
|
1367
1395
|
const placeholderHints = useMemo(() => {
|
|
1368
1396
|
if (compactionUi) return ['compaction in progress · esc to cancel']
|
|
@@ -1371,36 +1399,28 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1371
1399
|
|
|
1372
1400
|
const exitHint = exitState.pending ? 'ctrl+c again to quit' : null
|
|
1373
1401
|
const runtimeModeLabel = sessionModeLabel(mode)
|
|
1374
|
-
const modeColor =
|
|
1375
|
-
mode === 'plan'
|
|
1376
|
-
? theme.accentLavender
|
|
1377
|
-
: mode === 'accept-edits'
|
|
1378
|
-
? theme.accentPeach
|
|
1379
|
-
: theme.accentMint
|
|
1380
1402
|
const footerRight = (
|
|
1381
1403
|
<Box flexDirection="row">
|
|
1382
1404
|
{exitHint ? (
|
|
1383
1405
|
<>
|
|
1384
|
-
<Text color={theme.
|
|
1406
|
+
<Text color={theme.text}>{exitHint}</Text>
|
|
1385
1407
|
<Text color={theme.dim}> · </Text>
|
|
1386
1408
|
</>
|
|
1387
1409
|
) : null}
|
|
1388
1410
|
{runtimeModeLabel ? (
|
|
1389
1411
|
<>
|
|
1390
|
-
<Text
|
|
1412
|
+
<Text bold>{runtimeModeLabel}</Text>
|
|
1391
1413
|
<Text color={theme.dim}> (</Text>
|
|
1392
|
-
<Text color={theme.
|
|
1414
|
+
<Text color={theme.accentPeriwinkle}>shift+tab to cycle</Text>
|
|
1393
1415
|
<Text color={theme.dim}>) · </Text>
|
|
1394
1416
|
</>
|
|
1395
1417
|
) : (
|
|
1396
1418
|
<>
|
|
1397
|
-
<Text color={theme.
|
|
1419
|
+
<Text color={theme.accentPeriwinkle}>shift+tab to cycle</Text>
|
|
1398
1420
|
<Text color={theme.dim}> · </Text>
|
|
1399
1421
|
</>
|
|
1400
1422
|
)}
|
|
1401
|
-
<Text color={theme.dim}>
|
|
1402
|
-
{'pgup/pgdn scroll · alt+p model · alt+i identity'}
|
|
1403
|
-
</Text>
|
|
1423
|
+
<Text color={theme.dim}>{chatFooterShortcutText(canScrollTranscript)}</Text>
|
|
1404
1424
|
</Box>
|
|
1405
1425
|
)
|
|
1406
1426
|
const header = <BrandSplash contextLine={contextLine} tipLine={tipLine} updateNotice={updateNotice ?? null} />
|
|
@@ -1466,10 +1486,15 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1466
1486
|
)}
|
|
1467
1487
|
sessionKey={sessionKey}
|
|
1468
1488
|
onVisibleReasoningIdsChange={updateVisibleReasoningIds}
|
|
1489
|
+
onTranscriptScrollabilityChange={setCanScrollTranscript}
|
|
1469
1490
|
/>
|
|
1470
1491
|
)
|
|
1471
1492
|
}
|
|
1472
1493
|
|
|
1494
|
+
export function chatFooterShortcutText(canScrollTranscript: boolean): string {
|
|
1495
|
+
return `${canScrollTranscript ? 'pgup/pgdn scroll · ' : ''}alt+p model · alt+i identity`
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1473
1498
|
function formatContextLabel(usage: ContextUsage): string {
|
|
1474
1499
|
if (!Number.isFinite(usage.usedTokens) || usage.usedTokens <= 0) return 'Estimated context: empty'
|
|
1475
1500
|
return `Estimated context: ${usage.percent}% used`
|
|
@@ -64,20 +64,20 @@ export const ContextLimitView: React.FC<ContextLimitViewProps> = ({
|
|
|
64
64
|
})
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
|
-
<Box flexDirection="column" borderStyle="round" borderColor={theme.
|
|
68
|
-
<Text color={theme.
|
|
67
|
+
<Box flexDirection="column" borderStyle="round" borderColor={theme.accentPeriwinkle} paddingX={1}>
|
|
68
|
+
<Text color={theme.accentPeriwinkle} bold>context limit</Text>
|
|
69
69
|
<Text color={theme.dim}>
|
|
70
70
|
{`Context ${usage.percent}% · ~${formatTokens(usage.usedTokens)} / ${formatTokens(usage.windowTokens)} tokens (${usage.source}).`}
|
|
71
71
|
</Text>
|
|
72
72
|
{usage.percent >= 100 ? (
|
|
73
|
-
<Text color={theme.
|
|
73
|
+
<Text color={theme.accentPeriwinkle}>
|
|
74
74
|
This transcript is over the selected model's estimated window. You can still send, but summarizing first is safer.
|
|
75
75
|
</Text>
|
|
76
76
|
) : null}
|
|
77
77
|
<Text color={theme.textSubtle}>{`Pending: ${promptPreview || '(empty)'}`}</Text>
|
|
78
78
|
<Box flexDirection="column" marginTop={1}>
|
|
79
79
|
{CONTEXT_LIMIT_OPTIONS.map((option, index) => (
|
|
80
|
-
<Text key={option.action} color={index === selected ? theme.
|
|
80
|
+
<Text key={option.action} color={index === selected ? theme.accentPeriwinkle : theme.text}>
|
|
81
81
|
{index === selected ? '> ' : ' '}
|
|
82
82
|
{option.label}
|
|
83
83
|
<Text color={theme.dim}>{` · ${option.detail}`}</Text>
|
|
@@ -8,6 +8,7 @@ export type ContinuityEditReviewState = {
|
|
|
8
8
|
file: 'SOUL.md' | 'MEMORY.md'
|
|
9
9
|
filePath: string
|
|
10
10
|
summary: string
|
|
11
|
+
editorOpened?: boolean
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export type ContinuityEditReviewAction = 'open' | 'save-publish' | 'later'
|
|
@@ -18,27 +19,16 @@ export const ContinuityEditReviewView: React.FC<{
|
|
|
18
19
|
onCancel: () => void
|
|
19
20
|
}> = ({ review, onSelect, onCancel }) => (
|
|
20
21
|
<Surface
|
|
21
|
-
title=
|
|
22
|
-
|
|
23
|
-
footer="enter select · esc later"
|
|
22
|
+
title={`${review.file} Updated`}
|
|
23
|
+
footer="enter select · esc dismiss"
|
|
24
24
|
>
|
|
25
|
-
<
|
|
26
|
-
<Text color={theme.accentMint}>{review.summary}</Text>
|
|
27
|
-
<Box marginTop={1} flexDirection="column">
|
|
28
|
-
<Text color={theme.textSubtle}>review file</Text>
|
|
29
|
-
<Text color={theme.text}>{review.filePath}</Text>
|
|
30
|
-
</Box>
|
|
31
|
-
<Box marginTop={1} flexDirection="column">
|
|
32
|
-
<Text color={theme.textSubtle}>saved locally</Text>
|
|
33
|
-
<Text color={theme.dim}>Previous version saved in identity history. /rewind does not restore identity continuity.</Text>
|
|
34
|
-
</Box>
|
|
35
|
-
</Box>
|
|
25
|
+
<Text color={theme.accentPeriwinkle}>{displayContinuityReviewText(review.summary)}</Text>
|
|
36
26
|
<Box marginTop={1}>
|
|
37
27
|
<Select<ContinuityEditReviewAction>
|
|
38
28
|
options={[
|
|
39
|
-
{ value: 'open', label: `
|
|
40
|
-
{ value: 'save-publish', label: '
|
|
41
|
-
{ value: 'later', label: '
|
|
29
|
+
{ value: 'open', label: `Open ${review.file}`, hint: 'Review in editor' },
|
|
30
|
+
{ value: 'save-publish', label: 'Save Snapshot', hint: 'Wallet signature' },
|
|
31
|
+
{ value: 'later', label: 'Dismiss', hint: 'Save later from Identity Hub' },
|
|
42
32
|
]}
|
|
43
33
|
onSubmit={onSelect}
|
|
44
34
|
onCancel={onCancel}
|
|
@@ -46,3 +36,7 @@ export const ContinuityEditReviewView: React.FC<{
|
|
|
46
36
|
</Box>
|
|
47
37
|
</Surface>
|
|
48
38
|
)
|
|
39
|
+
|
|
40
|
+
function displayContinuityReviewText(value: string): string {
|
|
41
|
+
return value ? value[0]!.toUpperCase() + value.slice(1) : value
|
|
42
|
+
}
|
|
@@ -12,6 +12,7 @@ type ConversationStackProps = {
|
|
|
12
12
|
status?: React.ReactNode
|
|
13
13
|
sessionKey: number
|
|
14
14
|
onVisibleReasoningIdsChange?: (ids: string[]) => void
|
|
15
|
+
onTranscriptScrollabilityChange?: (canScroll: boolean) => void
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const ConversationStack: React.FC<ConversationStackProps> = ({
|
|
@@ -23,6 +24,7 @@ export const ConversationStack: React.FC<ConversationStackProps> = ({
|
|
|
23
24
|
status,
|
|
24
25
|
sessionKey,
|
|
25
26
|
onVisibleReasoningIdsChange,
|
|
27
|
+
onTranscriptScrollabilityChange,
|
|
26
28
|
}) => {
|
|
27
29
|
return (
|
|
28
30
|
<Box flexDirection="column" padding={1}>
|
|
@@ -33,6 +35,7 @@ export const ConversationStack: React.FC<ConversationStackProps> = ({
|
|
|
33
35
|
active={transcriptActive}
|
|
34
36
|
bottomVariant={bottomVariant}
|
|
35
37
|
onVisibleReasoningIdsChange={onVisibleReasoningIdsChange}
|
|
38
|
+
onScrollabilityChange={onTranscriptScrollabilityChange}
|
|
36
39
|
/>
|
|
37
40
|
<Box marginTop={1} width="100%">
|
|
38
41
|
{bottom}
|
package/src/chat/CopyPicker.tsx
CHANGED
package/src/chat/MessageList.tsx
CHANGED
|
@@ -3,14 +3,25 @@ import { Box, Text } from 'ink'
|
|
|
3
3
|
import { theme } from '../ui/theme.js'
|
|
4
4
|
import { ProgressBar } from '../ui/ProgressBar.js'
|
|
5
5
|
import { Spinner } from '../ui/Spinner.js'
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
export type ToolCallResult = {
|
|
8
|
+
content: string
|
|
9
|
+
summary: string
|
|
10
|
+
isError: boolean
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export type MessageRow =
|
|
9
14
|
| { role: 'user'; id: string; content: string }
|
|
10
15
|
| { role: 'assistant'; id: string; content: string; liveTail?: string; streaming?: boolean }
|
|
11
16
|
| { role: 'thinking'; id: string; content: string; liveTail?: string; streaming?: boolean; expanded?: boolean; showCursor?: boolean }
|
|
12
|
-
| {
|
|
13
|
-
|
|
17
|
+
| {
|
|
18
|
+
role: 'tool_call'
|
|
19
|
+
id: string
|
|
20
|
+
name: string
|
|
21
|
+
summary: string
|
|
22
|
+
input?: string
|
|
23
|
+
result?: ToolCallResult
|
|
24
|
+
}
|
|
14
25
|
| { role: 'note'; id: string; kind: 'info' | 'error' | 'dim'; content: string }
|
|
15
26
|
| {
|
|
16
27
|
role: 'progress'
|
|
@@ -43,7 +54,7 @@ type InlineToken =
|
|
|
43
54
|
|
|
44
55
|
const MAX_RENDERED_MESSAGE_CHARS = 12_000
|
|
45
56
|
const MAX_RENDERED_REASONING_CHARS = 10_000
|
|
46
|
-
const ASSISTANT_ACCENT = theme.
|
|
57
|
+
const ASSISTANT_ACCENT = theme.accentPeriwinkle
|
|
47
58
|
const UNREADABLE_REASONING_TEXT = 'reasoning output was not readable text'
|
|
48
59
|
|
|
49
60
|
const MessageListInner: React.FC<MessageListProps> = ({ rows }) => (
|
|
@@ -54,18 +65,27 @@ const MessageListInner: React.FC<MessageListProps> = ({ rows }) => (
|
|
|
54
65
|
|
|
55
66
|
export const MessageList = React.memo(MessageListInner)
|
|
56
67
|
|
|
68
|
+
function isInspectableRole(role: MessageRow['role']): boolean {
|
|
69
|
+
return role === 'thinking'
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
export function toggleLatestReasoningRow(rows: MessageRow[]): MessageRow[] {
|
|
58
|
-
return
|
|
73
|
+
return toggleInspectableRow(rows)
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
export function toggleReasoningRow(rows: MessageRow[], rowId?: string): MessageRow[] {
|
|
77
|
+
return toggleInspectableRow(rows, rowId)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function toggleInspectableRow(rows: MessageRow[], rowId?: string): MessageRow[] {
|
|
62
81
|
let index = -1
|
|
63
82
|
if (rowId) {
|
|
64
|
-
index = rows.findIndex(row => row.id === rowId && row.role
|
|
83
|
+
index = rows.findIndex(row => row.id === rowId && isInspectableRole(row.role))
|
|
65
84
|
}
|
|
66
85
|
if (index === -1) {
|
|
67
86
|
for (let cursor = rows.length - 1; cursor >= 0; cursor -= 1) {
|
|
68
|
-
|
|
87
|
+
const role = rows[cursor]?.role
|
|
88
|
+
if (role && isInspectableRole(role)) {
|
|
69
89
|
index = cursor
|
|
70
90
|
break
|
|
71
91
|
}
|
|
@@ -73,10 +93,13 @@ export function toggleReasoningRow(rows: MessageRow[], rowId?: string): MessageR
|
|
|
73
93
|
}
|
|
74
94
|
if (index === -1) return rows
|
|
75
95
|
const row = rows[index]
|
|
76
|
-
if (!row
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
if (!row) return rows
|
|
97
|
+
if (row.role === 'thinking') {
|
|
98
|
+
const next = rows.slice()
|
|
99
|
+
next[index] = { ...row, expanded: !row.expanded }
|
|
100
|
+
return next
|
|
101
|
+
}
|
|
102
|
+
return rows
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
@@ -90,7 +113,7 @@ const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
|
90
113
|
) : null}
|
|
91
114
|
{lines.map((line, i) => (
|
|
92
115
|
<Text key={i}>
|
|
93
|
-
<Text color={i === 0 ? theme.
|
|
116
|
+
<Text color={i === 0 ? theme.accentPeriwinkle : theme.dim}>{i === 0 ? '> ' : ' '}</Text>
|
|
94
117
|
<Text color={theme.textSubtle}>{line}</Text>
|
|
95
118
|
</Text>
|
|
96
119
|
))}
|
|
@@ -115,7 +138,7 @@ const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
|
115
138
|
return (
|
|
116
139
|
<Box flexDirection="column" marginTop={1} borderStyle="round" borderColor={borderColor} paddingX={1}>
|
|
117
140
|
<Text>
|
|
118
|
-
<Text color={theme.
|
|
141
|
+
<Text color={theme.accentPeriwinkle} bold>reasoning</Text>
|
|
119
142
|
<Text color={theme.dim}> · expanded · alt+t collapse</Text>
|
|
120
143
|
</Text>
|
|
121
144
|
<ReasoningBody content={text} showCursor={showCursor} />
|
|
@@ -125,7 +148,7 @@ const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
|
125
148
|
return (
|
|
126
149
|
<Box flexDirection="column" marginTop={1} borderStyle="round" borderColor={borderColor} paddingX={1}>
|
|
127
150
|
<Text>
|
|
128
|
-
<Text color={theme.
|
|
151
|
+
<Text color={theme.accentPeriwinkle} bold>reasoning</Text>
|
|
129
152
|
<Text color={theme.dim}> · collapsed · alt+t inspect</Text>
|
|
130
153
|
</Text>
|
|
131
154
|
<Text color={theme.textSubtle}>
|
|
@@ -136,39 +159,27 @@ const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
|
136
159
|
)
|
|
137
160
|
}
|
|
138
161
|
|
|
139
|
-
if (row.role === '
|
|
162
|
+
if (row.role === 'tool_call') {
|
|
163
|
+
const result = row.result
|
|
164
|
+
const inputPreview = row.input ? truncateToolInputForLine(row.input, 60) : ''
|
|
140
165
|
return (
|
|
141
|
-
<Box
|
|
142
|
-
<Text
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<Box
|
|
153
|
-
flexDirection="column"
|
|
154
|
-
marginTop={1}
|
|
155
|
-
borderStyle="round"
|
|
156
|
-
borderColor={row.isError ? '#a84c4c' : theme.border}
|
|
157
|
-
paddingX={1}
|
|
158
|
-
>
|
|
159
|
-
<Text color={row.isError ? '#e87070' : theme.accentSecondary} bold>{`result · ${row.name}`}</Text>
|
|
160
|
-
<Text color={theme.dim}>{row.summary}</Text>
|
|
161
|
-
{row.isError ? (
|
|
162
|
-
<Text color="#f1b0b0">{row.content}</Text>
|
|
163
|
-
) : hideContent || !row.content ? null : (
|
|
164
|
-
<AssistantBody content={row.content} />
|
|
165
|
-
)}
|
|
166
|
+
<Box marginTop={1}>
|
|
167
|
+
<Text>
|
|
168
|
+
<Text color={theme.dim}>{'· '}</Text>
|
|
169
|
+
<Text color={theme.accentPeriwinkle} bold>{row.name}</Text>
|
|
170
|
+
{inputPreview ? <Text color={theme.textSubtle}>{` ${inputPreview}`}</Text> : null}
|
|
171
|
+
{result ? (
|
|
172
|
+
<Text color={result.isError ? theme.accentError : theme.dim}>{` ${result.summary}`}</Text>
|
|
173
|
+
) : (
|
|
174
|
+
<Text color={theme.dim}>{' running…'}</Text>
|
|
175
|
+
)}
|
|
176
|
+
</Text>
|
|
166
177
|
</Box>
|
|
167
178
|
)
|
|
168
179
|
}
|
|
169
180
|
|
|
170
181
|
if (row.role === 'note') {
|
|
171
|
-
const color = row.kind === 'error' ?
|
|
182
|
+
const color = row.kind === 'error' ? theme.accentError : row.kind === 'dim' ? theme.dim : theme.accentPeriwinkle
|
|
172
183
|
return (
|
|
173
184
|
<Box marginTop={1}>
|
|
174
185
|
<Text color={color}>{row.content}</Text>
|
|
@@ -178,7 +189,7 @@ const RowViewInner: React.FC<{ row: MessageRow }> = ({ row }) => {
|
|
|
178
189
|
|
|
179
190
|
return (
|
|
180
191
|
<Box flexDirection="column" marginTop={1}>
|
|
181
|
-
<Text color={theme.
|
|
192
|
+
<Text color={theme.accentPeriwinkle} bold>{row.title}</Text>
|
|
182
193
|
{row.indeterminate ? (
|
|
183
194
|
<ProgressSpinner row={row} />
|
|
184
195
|
) : (
|
|
@@ -198,7 +209,13 @@ const ProgressSpinner: React.FC<{ row: Extract<MessageRow, { role: 'progress' }>
|
|
|
198
209
|
}
|
|
199
210
|
|
|
200
211
|
export function reasoningBorderColor(row: Extract<MessageRow, { role: 'thinking' }>): string {
|
|
201
|
-
return row.streaming ? theme.
|
|
212
|
+
return row.streaming ? theme.accentPeriwinkle : theme.border
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function truncateToolInputForLine(input: string, max: number): string {
|
|
216
|
+
const flat = input.replace(/\s+/g, ' ').trim()
|
|
217
|
+
if (flat.length <= max) return flat
|
|
218
|
+
return `${flat.slice(0, Math.max(1, max - 1))}…`
|
|
202
219
|
}
|
|
203
220
|
|
|
204
221
|
export function reasoningCursorVisible(row: Extract<MessageRow, { role: 'thinking' }>): boolean {
|
|
@@ -370,7 +387,7 @@ const InlineText: React.FC<{ text: string; color: string; bold?: boolean }> = ({
|
|
|
370
387
|
const ThinkingCursor: React.FC<{ active: boolean; hasPreview: boolean }> = ({ active, hasPreview }) => {
|
|
371
388
|
if (!active) return null
|
|
372
389
|
return (
|
|
373
|
-
<Text color={theme.
|
|
390
|
+
<Text color={theme.accentPeriwinkle}>
|
|
374
391
|
{hasPreview ? ' ' : ''}
|
|
375
392
|
<StreamCursor active />
|
|
376
393
|
</Text>
|
|
@@ -488,7 +505,7 @@ function parseMarkdownBlocks(markdown: string): MarkdownBlock[] {
|
|
|
488
505
|
return blocks
|
|
489
506
|
}
|
|
490
507
|
|
|
491
|
-
function codeAccent(
|
|
508
|
+
function codeAccent(_lang: string | null): string {
|
|
492
509
|
return ASSISTANT_ACCENT
|
|
493
510
|
}
|
|
494
511
|
|
|
@@ -23,23 +23,23 @@ export const PermissionPrompt: React.FC<PermissionPromptProps> = ({ request, onD
|
|
|
23
23
|
>
|
|
24
24
|
{request.kind === 'private-continuity-edit' ? (
|
|
25
25
|
<Box flexDirection="column" marginBottom={1}>
|
|
26
|
-
<Text color={theme.
|
|
26
|
+
<Text color={theme.accentPeriwinkle}>{displayPermissionText(request.changeSummary)}</Text>
|
|
27
27
|
<Text color={theme.textSubtle}>
|
|
28
|
-
Not reversible by /rewind. A private identity-history snapshot is saved before
|
|
28
|
+
Not reversible by /rewind. A private identity-history snapshot is saved before writing.
|
|
29
29
|
</Text>
|
|
30
30
|
<Box marginTop={1}>
|
|
31
31
|
<Text color={theme.textSubtle}>target</Text>
|
|
32
32
|
</Box>
|
|
33
33
|
<Text color={theme.text}>{request.file}</Text>
|
|
34
34
|
<Box marginTop={1}>
|
|
35
|
-
<Text color={theme.
|
|
35
|
+
<Text color={theme.accentPeriwinkle}>diff</Text>
|
|
36
36
|
</Box>
|
|
37
37
|
<Text color={theme.text}>{request.diff}</Text>
|
|
38
38
|
</Box>
|
|
39
39
|
) : null}
|
|
40
40
|
{request.kind === 'private-continuity-read' ? (
|
|
41
41
|
<Box flexDirection="column" marginBottom={1}>
|
|
42
|
-
<Text color={theme.
|
|
42
|
+
<Text color={theme.accentPeriwinkle}>read private {request.file}</Text>
|
|
43
43
|
<Text color={theme.textSubtle}>This reveals private identity continuity to the model for this turn.</Text>
|
|
44
44
|
<Box marginTop={1}>
|
|
45
45
|
<Text color={theme.textSubtle}>range</Text>
|
|
@@ -49,20 +49,20 @@ export const PermissionPrompt: React.FC<PermissionPromptProps> = ({ request, onD
|
|
|
49
49
|
) : null}
|
|
50
50
|
{request.kind === 'edit' || request.kind === 'write' || request.kind === 'delete' ? (
|
|
51
51
|
<Box flexDirection="column" marginBottom={1}>
|
|
52
|
-
<Text color={theme.
|
|
52
|
+
<Text color={theme.accentPeriwinkle}>{displayPermissionText(request.changeSummary)}</Text>
|
|
53
53
|
<Box marginTop={1}>
|
|
54
54
|
<Text color={theme.textSubtle}>before</Text>
|
|
55
55
|
</Box>
|
|
56
56
|
<Text color={theme.textSubtle}>{request.before || '(empty)'}</Text>
|
|
57
57
|
<Box marginTop={1}>
|
|
58
|
-
<Text color={theme.
|
|
58
|
+
<Text color={theme.accentPeriwinkle}>after</Text>
|
|
59
59
|
</Box>
|
|
60
60
|
<Text color={theme.text}>{request.after || '(empty)'}</Text>
|
|
61
61
|
</Box>
|
|
62
62
|
) : null}
|
|
63
63
|
{request.kind === 'bash' && request.warning ? (
|
|
64
64
|
<Box marginBottom={1}>
|
|
65
|
-
<Text color=
|
|
65
|
+
<Text color={theme.accentError}>{request.warning}</Text>
|
|
66
66
|
</Box>
|
|
67
67
|
) : null}
|
|
68
68
|
<Select options={options} onSubmit={onDecision} onCancel={onCancel} />
|
|
@@ -127,8 +127,8 @@ export function permissionOptionsForRequest(request: PermissionRequest): Array<{
|
|
|
127
127
|
|
|
128
128
|
if (request.kind === 'private-continuity-edit') {
|
|
129
129
|
return [
|
|
130
|
-
{ value: 'allow-once', label: '
|
|
131
|
-
{ value: 'deny', label: '
|
|
130
|
+
{ value: 'allow-once', label: 'Approve Once', hint: `Apply this edit to ${request.file}` },
|
|
131
|
+
{ value: 'deny', label: 'Deny', hint: 'Keep private continuity unchanged' },
|
|
132
132
|
]
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -151,3 +151,7 @@ export function permissionOptionsForRequest(request: PermissionRequest): Array<{
|
|
|
151
151
|
{ value: 'deny', label: 'deny', hint: 'return a denial back to the model' },
|
|
152
152
|
]
|
|
153
153
|
}
|
|
154
|
+
|
|
155
|
+
function displayPermissionText(value: string): string {
|
|
156
|
+
return value ? value[0]!.toUpperCase() + value.slice(1) : value
|
|
157
|
+
}
|
|
@@ -70,11 +70,11 @@ export const PlanApprovalView: React.FC<PlanApprovalViewProps> = ({
|
|
|
70
70
|
const active = optionIndex === index
|
|
71
71
|
return (
|
|
72
72
|
<Box key={option.value} flexDirection="row">
|
|
73
|
-
<Text color={active ? theme.
|
|
73
|
+
<Text color={active ? theme.accentPeriwinkle : theme.dim}>
|
|
74
74
|
{active ? '> ' : ' '}
|
|
75
75
|
{optionIndex + 1}.{' '}
|
|
76
76
|
</Text>
|
|
77
|
-
<Text color={active ? theme.
|
|
77
|
+
<Text color={active ? theme.accentPeriwinkle : theme.text} bold={active}>
|
|
78
78
|
{option.label}
|
|
79
79
|
</Text>
|
|
80
80
|
</Box>
|
|
@@ -82,7 +82,7 @@ export const PlanApprovalView: React.FC<PlanApprovalViewProps> = ({
|
|
|
82
82
|
})}
|
|
83
83
|
</Box>
|
|
84
84
|
<Box flexDirection="column" marginLeft={4} flexShrink={1}>
|
|
85
|
-
<Text color={theme.
|
|
85
|
+
<Text color={theme.accentPeriwinkle} bold>{selected.title}</Text>
|
|
86
86
|
<Text color={theme.dim}>{selected.detail(contextLabel)}</Text>
|
|
87
87
|
</Box>
|
|
88
88
|
</Box>
|
package/src/chat/ResumeView.tsx
CHANGED
|
@@ -26,8 +26,6 @@ export const CLEAR_ALL_SESSIONS_VALUE = '__clear_all_sessions__'
|
|
|
26
26
|
export const ResumeView: React.FC<ResumeViewProps> = ({ currentSessionId, onResume, onClearAll, onCancel }) => {
|
|
27
27
|
const [state, setState] = useState<State>({ kind: 'loading' })
|
|
28
28
|
|
|
29
|
-
// Allow ESC to close the view during loading / error states
|
|
30
|
-
// (Select handles ESC only when it's rendered in the 'ready' state)
|
|
31
29
|
const escActive = state.kind === 'loading' || state.kind === 'error' || (state.kind === 'ready' && state.sessions.length === 0)
|
|
32
30
|
useAppInput((_input, key) => {
|
|
33
31
|
if (key.escape) onCancel()
|
|
@@ -75,7 +73,7 @@ export const ResumeView: React.FC<ResumeViewProps> = ({ currentSessionId, onResu
|
|
|
75
73
|
<Box flexDirection="column" marginBottom={1}>
|
|
76
74
|
<Text color={theme.dim}>Removes saved chats and resume context from this machine.</Text>
|
|
77
75
|
<Text color={theme.dim}>Config, identities, keys, and local models stay.</Text>
|
|
78
|
-
{state.error ? <Text color=
|
|
76
|
+
{state.error ? <Text color={theme.accentError}>{state.error}</Text> : null}
|
|
79
77
|
</Box>
|
|
80
78
|
<Select<'back' | 'clear'>
|
|
81
79
|
options={[
|
|
@@ -212,7 +210,6 @@ export function buildResumeOptions(
|
|
|
212
210
|
}
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
// Utility section sits below all session groups with a visual gap
|
|
216
213
|
options.push(manageSpacer)
|
|
217
214
|
options.push(clearOption)
|
|
218
215
|
|
package/src/chat/RewindView.tsx
CHANGED
|
@@ -312,7 +312,7 @@ function buildActionOptions(canRestoreConversation: boolean): Array<SelectOption
|
|
|
312
312
|
|
|
313
313
|
const CompactPreview: React.FC<{ entry: RewindEntry }> = ({ entry }) => (
|
|
314
314
|
<Box flexDirection="column" marginTop={1}>
|
|
315
|
-
<Text color={theme.
|
|
315
|
+
<Text color={theme.accentPeriwinkle}>{entry.relativePath}</Text>
|
|
316
316
|
<Text color={theme.dim}>{entry.promptSnippet || '(prompt snippet unavailable for older checkpoints)'}</Text>
|
|
317
317
|
</Box>
|
|
318
318
|
)
|
|
@@ -323,7 +323,7 @@ const ActionPreview: React.FC<{
|
|
|
323
323
|
canRestoreConversation: boolean
|
|
324
324
|
}> = ({ entry, selectedAction, canRestoreConversation }) => (
|
|
325
325
|
<Box flexDirection="column" marginTop={1}>
|
|
326
|
-
<Text color={theme.
|
|
326
|
+
<Text color={theme.accentPeriwinkle}>{entry.relativePath}</Text>
|
|
327
327
|
<Text color={theme.dim}>{formatTimestamp(entry.createdAt)} · {entry.changeSummary}</Text>
|
|
328
328
|
<Text color={theme.textSubtle}>
|
|
329
329
|
{selectedAction === 'both'
|
|
@@ -21,6 +21,7 @@ type TranscriptViewProps = {
|
|
|
21
21
|
active?: boolean
|
|
22
22
|
bottomVariant?: 'prompt' | 'overlay'
|
|
23
23
|
onVisibleReasoningIdsChange?: (ids: string[]) => void
|
|
24
|
+
onScrollabilityChange?: (canScroll: boolean) => void
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const PROMPT_RESERVED_LINES = 11
|
|
@@ -33,6 +34,7 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
33
34
|
active = true,
|
|
34
35
|
bottomVariant = 'prompt',
|
|
35
36
|
onVisibleReasoningIdsChange,
|
|
37
|
+
onScrollabilityChange,
|
|
36
38
|
}) => {
|
|
37
39
|
const { stdout } = useStdout()
|
|
38
40
|
const columns = stdout.columns ?? process.stdout.columns ?? 80
|
|
@@ -89,6 +91,10 @@ export const TranscriptView: React.FC<TranscriptViewProps> = ({
|
|
|
89
91
|
onVisibleReasoningIdsChange?.(visibleReasoningIds)
|
|
90
92
|
}, [onVisibleReasoningIdsChange, visibleReasoningIds])
|
|
91
93
|
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
onScrollabilityChange?.(metrics.maxScrollTop > 0)
|
|
96
|
+
}, [metrics.maxScrollTop, onScrollabilityChange])
|
|
97
|
+
|
|
92
98
|
useAppInput((_input, key) => {
|
|
93
99
|
if (key.pageUp) {
|
|
94
100
|
const target = promptScrollTopForPageUp(
|
|
@@ -73,7 +73,7 @@ export function moveThroughHistory(
|
|
|
73
73
|
historyIndex: number,
|
|
74
74
|
direction: 1 | -1,
|
|
75
75
|
draftBuffer: ChatBuffer,
|
|
76
|
-
|
|
76
|
+
_preferredColumn: number | null,
|
|
77
77
|
): { preview: HistoryPreviewState; buffer: ChatBuffer } {
|
|
78
78
|
const next = historyIndex + direction
|
|
79
79
|
if (next < 0) {
|