centaurus-cli 2.9.8 → 2.9.9
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/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +11 -2
- package/dist/cli-adapter.js.map +1 -1
- package/dist/services/clipboard-service.d.ts +9 -14
- package/dist/services/clipboard-service.d.ts.map +1 -1
- package/dist/services/clipboard-service.js +83 -83
- package/dist/services/clipboard-service.js.map +1 -1
- package/dist/tools/read-binary-file.js +6 -6
- package/dist/tools/read-binary-file.js.map +1 -1
- package/dist/ui/components/App.d.ts.map +1 -1
- package/dist/ui/components/App.js +51 -48
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/ClipboardFileAutocomplete.d.ts +10 -0
- package/dist/ui/components/ClipboardFileAutocomplete.d.ts.map +1 -0
- package/dist/ui/components/{ClipboardImageAutocomplete.js → ClipboardFileAutocomplete.js} +14 -12
- package/dist/ui/components/ClipboardFileAutocomplete.js.map +1 -0
- package/dist/ui/components/DetailedPlanReviewScreen.js +1 -1
- package/dist/ui/components/DetailedPlanReviewScreen.js.map +1 -1
- package/dist/ui/components/InputBox.d.ts +2 -2
- package/dist/ui/components/InputBox.d.ts.map +1 -1
- package/dist/ui/components/InputBox.js +41 -21
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/KeyboardHelp.d.ts.map +1 -1
- package/dist/ui/components/KeyboardHelp.js +2 -0
- package/dist/ui/components/KeyboardHelp.js.map +1 -1
- package/dist/ui/components/MessageDisplay.d.ts +4 -4
- package/dist/ui/components/MessageDisplay.d.ts.map +1 -1
- package/dist/ui/components/MessageDisplay.js +33 -31
- package/dist/ui/components/MessageDisplay.js.map +1 -1
- package/dist/ui/components/PlanAcceptedMessage.js +2 -2
- package/dist/ui/components/PlanAcceptedMessage.js.map +1 -1
- package/dist/ui/components/TaskCompletedMessage.js +2 -2
- package/dist/ui/components/TaskCompletedMessage.js.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.js +6 -6
- package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
- package/dist/ui/components/ToolResult.js +1 -1
- package/dist/ui/components/ToolResult.js.map +1 -1
- package/dist/utils/context-sanitizer.d.ts +50 -0
- package/dist/utils/context-sanitizer.d.ts.map +1 -0
- package/dist/utils/context-sanitizer.js +119 -0
- package/dist/utils/context-sanitizer.js.map +1 -0
- package/package.json +1 -1
- package/dist/ui/components/ClipboardImageAutocomplete.d.ts +0 -14
- package/dist/ui/components/ClipboardImageAutocomplete.d.ts.map +0 -1
- package/dist/ui/components/ClipboardImageAutocomplete.js.map +0 -1
|
@@ -8,7 +8,7 @@ import * as path from 'path';
|
|
|
8
8
|
import { quickLog } from '../../utils/conversation-logger.js';
|
|
9
9
|
import { WelcomeBanner } from './WelcomeBanner.js';
|
|
10
10
|
import { InputBox } from './InputBox.js';
|
|
11
|
-
import { MessageDisplay,
|
|
11
|
+
import { MessageDisplay, countFilesInMessage } from './MessageDisplay.js';
|
|
12
12
|
import { StreamingMessageDisplay } from './StreamingMessageDisplay.js';
|
|
13
13
|
import { LoadingIndicator } from './LoadingIndicator.js';
|
|
14
14
|
import { AgentTimer } from './AgentTimer.js';
|
|
@@ -69,21 +69,21 @@ const MessageList = React.memo(({ history, current, showBanner }) => {
|
|
|
69
69
|
const staticItems = React.useMemo(() => {
|
|
70
70
|
return showBanner ? [BANNER_ITEM, ...history] : history;
|
|
71
71
|
}, [history, showBanner]);
|
|
72
|
-
// Calculate cumulative
|
|
73
|
-
const
|
|
72
|
+
// Calculate cumulative file counts for each message (for global numbering)
|
|
73
|
+
const fileCountsUpTo = React.useMemo(() => {
|
|
74
74
|
const counts = [];
|
|
75
75
|
let cumulative = 0;
|
|
76
76
|
for (const msg of history) {
|
|
77
77
|
counts.push(cumulative);
|
|
78
78
|
if (msg.role === 'user') {
|
|
79
|
-
cumulative +=
|
|
79
|
+
cumulative += countFilesInMessage(msg.content);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
return counts;
|
|
83
83
|
}, [history]);
|
|
84
|
-
// Get
|
|
85
|
-
const
|
|
86
|
-
?
|
|
84
|
+
// Get file count for current message (all files in history)
|
|
85
|
+
const totalHistoryFiles = fileCountsUpTo.length > 0
|
|
86
|
+
? fileCountsUpTo[fileCountsUpTo.length - 1] + (history[history.length - 1]?.role === 'user' ? countFilesInMessage(history[history.length - 1].content) : 0)
|
|
87
87
|
: 0;
|
|
88
88
|
return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
|
|
89
89
|
React.createElement(Static, { items: staticItems }, (item, index) => {
|
|
@@ -94,7 +94,7 @@ const MessageList = React.memo(({ history, current, showBanner }) => {
|
|
|
94
94
|
const msg = item;
|
|
95
95
|
// Calculate history index (account for banner if present)
|
|
96
96
|
const historyIndex = showBanner ? index - 1 : index;
|
|
97
|
-
const
|
|
97
|
+
const fileCountBefore = fileCountsUpTo[historyIndex] || 0;
|
|
98
98
|
if (msg.taskCompletion) {
|
|
99
99
|
return (React.createElement(TaskCompletedMessage, { key: item.id, taskNumber: msg.taskCompletion.taskNumber, totalTasks: msg.taskCompletion.totalTasks, taskDescription: msg.taskCompletion.taskDescription, completionNote: msg.taskCompletion.completionNote }));
|
|
100
100
|
}
|
|
@@ -102,7 +102,7 @@ const MessageList = React.memo(({ history, current, showBanner }) => {
|
|
|
102
102
|
if (msg.planAccepted) {
|
|
103
103
|
return (React.createElement(PlanAcceptedMessage, { key: item.id, planTitle: msg.planAccepted.planTitle, totalTasks: msg.planAccepted.totalTasks, tasks: msg.planAccepted.tasks }));
|
|
104
104
|
}
|
|
105
|
-
return React.createElement(MessageDisplay, { key: item.id, message: item,
|
|
105
|
+
return React.createElement(MessageDisplay, { key: item.id, message: item, fileCountBefore: fileCountBefore });
|
|
106
106
|
}),
|
|
107
107
|
current && !history.some(msg => msg.id === current.id) && (() => {
|
|
108
108
|
// Get terminal dimensions to determine if streaming should be enabled
|
|
@@ -114,7 +114,7 @@ const MessageList = React.memo(({ history, current, showBanner }) => {
|
|
|
114
114
|
return (React.createElement(StreamingMessageDisplay, { key: current.id, message: current, maxLines: dimensions.maxStreamingLines }));
|
|
115
115
|
}
|
|
116
116
|
else {
|
|
117
|
-
return React.createElement(MessageDisplay, { key: current.id, message: current,
|
|
117
|
+
return React.createElement(MessageDisplay, { key: current.id, message: current, fileCountBefore: totalHistoryFiles });
|
|
118
118
|
}
|
|
119
119
|
})()));
|
|
120
120
|
}, (prevProps, nextProps) => {
|
|
@@ -2268,7 +2268,7 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2268
2268
|
return;
|
|
2269
2269
|
}
|
|
2270
2270
|
}, { isActive: !state.isInteractiveEditorMode });
|
|
2271
|
-
const handleSubmit = useCallback(async (value,
|
|
2271
|
+
const handleSubmit = useCallback(async (value, clipboardFiles) => {
|
|
2272
2272
|
// Trim the value to remove any leading/trailing whitespace or newlines
|
|
2273
2273
|
const trimmedValue = value.trim();
|
|
2274
2274
|
if (!trimmedValue)
|
|
@@ -2379,18 +2379,19 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2379
2379
|
};
|
|
2380
2380
|
});
|
|
2381
2381
|
}
|
|
2382
|
-
// Show user message IMMEDIATELY with
|
|
2383
|
-
//
|
|
2384
|
-
const
|
|
2385
|
-
// Build initial content with placeholder
|
|
2382
|
+
// Show user message IMMEDIATELY with file breadcrumbs (uploading state)
|
|
2383
|
+
// Files will be uploaded in background, then AI will be called
|
|
2384
|
+
const hasFilesToUpload = clipboardFiles && clipboardFiles.length > 0;
|
|
2385
|
+
// Build initial content with placeholder file markers for immediate display
|
|
2386
2386
|
let initialDisplayContent = trimmedValue;
|
|
2387
|
-
if (
|
|
2387
|
+
if (hasFilesToUpload) {
|
|
2388
2388
|
// Add placeholder markers that will be shown immediately
|
|
2389
|
-
for (let i = 0; i <
|
|
2390
|
-
const
|
|
2389
|
+
for (let i = 0; i < clipboardFiles.length; i++) {
|
|
2390
|
+
const file = clipboardFiles[i];
|
|
2391
2391
|
// Use a placeholder format that MessageDisplay can render as breadcrumb
|
|
2392
|
-
// Format: [
|
|
2393
|
-
|
|
2392
|
+
// Format: [FILE: filename (uploading...)] - will be updated after upload
|
|
2393
|
+
const markerType = file.mimeType?.startsWith('image/') ? 'IMAGE' : 'FILE';
|
|
2394
|
+
initialDisplayContent = `${initialDisplayContent} [${markerType}: ${file.displayName} (gs://uploading)]`;
|
|
2394
2395
|
}
|
|
2395
2396
|
}
|
|
2396
2397
|
// Generate a stable message ID that we can use to update the message later
|
|
@@ -2410,7 +2411,7 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2410
2411
|
messageHistory: [...newHistory, userMessage],
|
|
2411
2412
|
currentMessage: null,
|
|
2412
2413
|
isLoading: true,
|
|
2413
|
-
loadingMessage:
|
|
2414
|
+
loadingMessage: hasFilesToUpload ? 'Uploading file(s)...' : 'Processing your request...',
|
|
2414
2415
|
isAiWorking: true
|
|
2415
2416
|
};
|
|
2416
2417
|
});
|
|
@@ -2432,12 +2433,12 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2432
2433
|
: [...prev.commandHistory, trimmedValue]
|
|
2433
2434
|
}));
|
|
2434
2435
|
try {
|
|
2435
|
-
// Upload clipboard
|
|
2436
|
-
let
|
|
2437
|
-
// Debug logging for clipboard
|
|
2438
|
-
logDebug(`[CLIPBOARD]
|
|
2436
|
+
// Upload clipboard files if present
|
|
2437
|
+
let messageWithFiles = trimmedValue;
|
|
2438
|
+
// Debug logging for clipboard files
|
|
2439
|
+
logDebug(`[CLIPBOARD] clipboardFiles count: ${clipboardFiles?.length || 0}`);
|
|
2439
2440
|
logDebug(`[CLIPBOARD] currentChatId: ${state.currentChatId || 'null'}`);
|
|
2440
|
-
if (
|
|
2441
|
+
if (clipboardFiles && clipboardFiles.length > 0) {
|
|
2441
2442
|
// Check for existing conversation ID - conversationManager is the single source of truth
|
|
2442
2443
|
// This ensures we use the same conversation that cli-adapter uses
|
|
2443
2444
|
let conversationId = conversationManager.getCurrentConversationId() || state.currentChatId;
|
|
@@ -2446,7 +2447,7 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2446
2447
|
if (!conversationId) {
|
|
2447
2448
|
// No conversation exists anywhere - create one via conversationManager
|
|
2448
2449
|
// This will set the internal state that cli-adapter's ensureConversationStarted checks
|
|
2449
|
-
logDebug(`[CLIPBOARD] No existing conversation - creating new one for
|
|
2450
|
+
logDebug(`[CLIPBOARD] No existing conversation - creating new one for file upload`);
|
|
2450
2451
|
try {
|
|
2451
2452
|
const newConversation = await conversationManager.startNewConversation('New Chat', state.currentModel || 'gemini-2-flash-preview', 'google', undefined // workingDirectory - will use process.cwd()
|
|
2452
2453
|
);
|
|
@@ -2461,38 +2462,40 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2461
2462
|
logDebug(`[CLIPBOARD] Proceeding with upload`);
|
|
2462
2463
|
}
|
|
2463
2464
|
catch (createError) {
|
|
2464
|
-
logError('Failed to create conversation for
|
|
2465
|
+
logError('Failed to create conversation for file upload', createError instanceof Error ? createError : undefined);
|
|
2465
2466
|
// Mark the upload as failed - don't use temp IDs that will fail FK constraint
|
|
2466
|
-
for (let i = 0; i <
|
|
2467
|
-
|
|
2467
|
+
for (let i = 0; i < clipboardFiles.length; i++) {
|
|
2468
|
+
messageWithFiles = messageWithFiles.replace(/#image\b/i, '[FILE UPLOAD FAILED - No conversation]');
|
|
2468
2469
|
}
|
|
2469
2470
|
logDebug(`[CLIPBOARD] Skipping upload due to conversation creation failure`);
|
|
2470
2471
|
}
|
|
2471
2472
|
}
|
|
2472
2473
|
// Only attempt upload if we have a valid conversation ID
|
|
2473
2474
|
if (conversationId && !conversationId.startsWith('temp_')) {
|
|
2474
|
-
logDebug(`[CLIPBOARD] Starting upload for ${
|
|
2475
|
-
// Upload each clipboard
|
|
2476
|
-
const
|
|
2477
|
-
for (const
|
|
2475
|
+
logDebug(`[CLIPBOARD] Starting upload for ${clipboardFiles.length} files with conversationId: ${conversationId}`);
|
|
2476
|
+
// Upload each clipboard file and build the final message content
|
|
2477
|
+
const uploadedFileMarkers = [];
|
|
2478
|
+
for (const file of clipboardFiles) {
|
|
2478
2479
|
try {
|
|
2479
|
-
logDebug(`[CLIPBOARD] Uploading
|
|
2480
|
-
const uploadResult = await apiClient.uploadFile(conversationId,
|
|
2480
|
+
logDebug(`[CLIPBOARD] Uploading file: ${file.displayName}, size: ${file.sizeBytes} bytes`);
|
|
2481
|
+
const uploadResult = await apiClient.uploadFile(conversationId, file.displayName, file.mimeType, file.base64Data);
|
|
2481
2482
|
logDebug(`[CLIPBOARD] Upload success! gcsUri: ${uploadResult.gcsUri || 'none'}`);
|
|
2482
|
-
|
|
2483
|
+
const markerType = file.mimeType?.startsWith('image/') ? 'IMAGE' : 'FILE';
|
|
2484
|
+
// Append file reference info to message (for AI to know a file was attached)
|
|
2483
2485
|
// The backend will use gcsUri for Vertex AI multimodal input
|
|
2484
2486
|
if (uploadResult.gcsUri) {
|
|
2485
|
-
// Build the real
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
logDebug(`[CLIPBOARD] Appended
|
|
2487
|
+
// Build the real file marker with actual GCS URI
|
|
2488
|
+
messageWithFiles = `${messageWithFiles} [${markerType}: ${uploadResult.fileName} (${uploadResult.gcsUri})]`;
|
|
2489
|
+
uploadedFileMarkers.push(`[${markerType}: ${uploadResult.fileName} (${uploadResult.gcsUri})]`);
|
|
2490
|
+
logDebug(`[CLIPBOARD] Appended file marker: [${markerType}: ${uploadResult.fileName} (${uploadResult.gcsUri})]`);
|
|
2489
2491
|
}
|
|
2490
2492
|
}
|
|
2491
2493
|
catch (uploadError) {
|
|
2492
|
-
// Log error but continue -
|
|
2493
|
-
|
|
2494
|
+
// Log error but continue - file upload is optional
|
|
2495
|
+
const markerType = file.mimeType?.startsWith('image/') ? 'IMAGE' : 'FILE';
|
|
2496
|
+
logError('Failed to upload clipboard file', uploadError instanceof Error ? uploadError : undefined);
|
|
2494
2497
|
// Add a failed marker so user knows upload failed
|
|
2495
|
-
|
|
2498
|
+
uploadedFileMarkers.push(`[${markerType}: ${file.displayName} (upload failed)]`);
|
|
2496
2499
|
}
|
|
2497
2500
|
}
|
|
2498
2501
|
// Update the user message in history with actual GCS URIs
|
|
@@ -2501,9 +2504,9 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2501
2504
|
const updatedHistory = prev.messageHistory.map(msg => {
|
|
2502
2505
|
// Find the user message we just added (it has placeholder markers)
|
|
2503
2506
|
if (msg.role === 'user' && msg.content.includes('(gs://uploading)')) {
|
|
2504
|
-
// Build new content: original text + real
|
|
2507
|
+
// Build new content: original text + real file markers
|
|
2505
2508
|
let newContent = trimmedValue;
|
|
2506
|
-
for (const marker of
|
|
2509
|
+
for (const marker of uploadedFileMarkers) {
|
|
2507
2510
|
newContent = `${newContent} ${marker}`;
|
|
2508
2511
|
}
|
|
2509
2512
|
return { ...msg, content: newContent };
|
|
@@ -2527,8 +2530,8 @@ export const App = ({ onMessage, onCancelRequest, initialModel, initialPlanMode,
|
|
|
2527
2530
|
return { ...prev, messageHistory: updatedHistory };
|
|
2528
2531
|
});
|
|
2529
2532
|
} // End: if (conversationId && !startsWith temp_)
|
|
2530
|
-
} // End: if (
|
|
2531
|
-
await onMessage(
|
|
2533
|
+
} // End: if (clipboardFiles && clipboardFiles.length > 0)
|
|
2534
|
+
await onMessage(messageWithFiles);
|
|
2532
2535
|
}
|
|
2533
2536
|
catch (error) {
|
|
2534
2537
|
// Add error message
|